Restructure types for retailer-centric flow

This commit is contained in:
toqvist 2024-04-04 12:45:26 +02:00
parent 2ebb81a981
commit 174a163cd3
9 changed files with 221 additions and 187 deletions

View File

@ -6,12 +6,19 @@ import L, { LatLngBounds } from 'leaflet';
import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/react-query";
import axios from "axios"; import axios from "axios";
//Todo: Move types to own file //Todo: Move types to own file
type User = {
name: string;
id: string;
email: string;
phoneNumber: string;
}
type Product = { type Product = {
id: string; id: string;
productTitle: string; productTitle: string;
weight: number;
img: string;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
}; };
@ -24,6 +31,8 @@ type Product = {
type Maker = { type Maker = {
id: string; id: string;
name: string; name: string;
email: string;
phoneNumber: string;
location: [number, number]; location: [number, number];
products: Product[]; products: Product[];
createdAt: string; createdAt: string;
@ -33,48 +42,55 @@ type Maker = {
type Retailer = { type Retailer = {
id: string; id: string;
name: string; name: string;
email: string;
phoneNumber: string;
location: [number, number]; location: [number, number];
products: Product[]; products: Product[];
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
}; };
//Todo: the courier should not hold the starting and end points const DISPATCH_STATUS = ['requested', 'accepted', 'archived'] as const;
type Courier = { type DispatchStatus = typeof DISPATCH_STATUS[number];
id: string;
name: string;
startingPoint: string;
destination: string;
departureDate: string;
arrivalDate: string;
weightAllowance: number;
createdAt: string;
updatedAt: string;
}
type Dispatch = { type Dispatch = {
id: string; id: string;
dispatchesCode: string; dispatchesCode: string; //Human readable id
products: Product[];
typeOfTransportation: string[];
courier: Courier;
timeSensitive: boolean;
status: string[];
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
maker: Maker;
retailer: Retailer;
products: Product[];
courier: User;
timeSensitive: boolean;
status: DispatchStatus[];
departureDate: string;
arrivalDate: string;
weightAllowance: number;
} }
//Todo: update fetch url endpoints //Todo: update fetch url endpoints
//Todo: Move queryclient and hooks to own file //Todo: Move queryclient and hooks to own file
const ROOT_URL = "http://localhost:3001" //Todo: Move axios stuff
const API_URL = "http://localhost:3001"
const headers = {
"Content-Type": "application/json",
}
const getMakers = async () => { const getMakers = async () => {
const url = `${ROOT_URL}/api/makers` const url = `${API_URL}/api/makers`
console.log("Fetching url:", url) console.log("Fetching url:", url)
const response = await axios.get(url); const response = await axios.get(url);
const makers = response.data.docs; const makers: Maker[] = response.data.docs;
console.log(`Fetch result from ${url}`, makers) console.log(`Fetch result from ${url}`, makers)
return makers; return makers;
@ -89,18 +105,18 @@ const useGetMakers = () => {
} }
const getRetailers = async () => { const getRetailers = async () => {
const url = `${ROOT_URL}/api/retailers` const url = `${API_URL}/api/retailers`
console.log("Fetching url:", url) console.log("Fetching url:", url)
const response = await axios.get(url); const response = await axios.get(url);
const retailers = response.data.docs; const retailers:Retailer[] = response.data.docs;
console.log(`Fetch result from ${url}`, retailers) console.log(`Fetch result from ${url}`, retailers)
return retailers; return retailers;
} }
const useGetRetailers = () => { const useGetRetailers = () => {
return useQuery<Dispatch[]>({ return useQuery<Retailer[]>({
queryFn: () => getRetailers(), queryFn: () => getRetailers(),
queryKey: ['retailers'], queryKey: ['retailers'],
enabled: true enabled: true
@ -108,11 +124,11 @@ const useGetRetailers = () => {
} }
const getDispatches = async () => { const getDispatches = async () => {
const url = `${ROOT_URL}/api/dispatches` const url = `${API_URL}/api/dispatches`
console.log("Fetching url:", url) console.log("Fetching url:", url)
const response = await axios.get(url); const response = await axios.get(url);
const dispatches = response.data.docs; const dispatches:Dispatch[] = response.data.docs;
console.log(`Fetch result from ${url}`, dispatches) console.log(`Fetch result from ${url}`, dispatches)
return dispatches; return dispatches;
@ -188,11 +204,11 @@ export const KiosMap = () => {
</LayerGroup> </LayerGroup>
} }
{/* {dispatches && {dispatches &&
<LayerGroup> <LayerGroup>
{dispatches.map((dispatch: any, index: number) => { {dispatches.map((dispatch: any, index: number) => {
const start = dispatch.startingPoint.location; const start = dispatch.maker.location;
const end = dispatch.endPoint.location; const end = dispatch.retailer.location;
let productsString = ''; let productsString = '';
dispatch.products.forEach((product: any, i: number) => { dispatch.products.forEach((product: any, i: number) => {
@ -200,19 +216,19 @@ export const KiosMap = () => {
}); });
const myDashArray = const myDashArray =
dispatch.status === 'routeRequested' ? '20, 10' : dispatch.status === 'requested' ? '20, 10' :
dispatch.status === 'completed' ? '1, 5' : dispatch.status === 'archived' ? '1, 5' :
'0, 0'; '0, 0';
return ( return (
<div key={index}> <div key={index}>
<Marker position={[start[0], start[1]]} <Marker position={[start[0], start[1]]}
//icon={blackDotIcon} icon={blackDotIcon}
> >
<Popup>{dispatch.startingPoint.name}</Popup> <Popup>{dispatch.startingPoint.name}</Popup>
</Marker> </Marker>
<Marker position={[end[0], end[1]]} <Marker position={[end[0], end[1]]}
//icon={blackDotIcon} icon={blackDotIcon}
> >
<Popup>{dispatch.endPoint.name}</Popup> <Popup>{dispatch.endPoint.name}</Popup>
</Marker> </Marker>
@ -224,7 +240,7 @@ export const KiosMap = () => {
); );
})} })}
</LayerGroup> </LayerGroup>
} */} }
</MapContainer > </MapContainer >
</div> </div>
); );

View File

@ -33,7 +33,8 @@ export interface Post {
} }
export interface User { export interface User {
id: string; id: string;
name?: string; name: string;
phoneNumber: number;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
email: string; email: string;
@ -59,12 +60,6 @@ export interface Media {
} }
export interface Courier { export interface Courier {
id: string; id: string;
name: string;
startingPoint?: string;
destination?: string;
departureDate?: string;
arrivalDate?: string;
weightAllowance?: number;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
@ -72,18 +67,17 @@ export interface Dispatch {
id: string; id: string;
dispatchesCode: string; dispatchesCode: string;
products?: string[] | Product[]; products?: string[] | Product[];
startingPoint?: string | Maker; courier?: string | Courier;
endPoint?: string | Retailer; maker?: string | Maker;
typeOfTransportation?: ('air' | 'car' | 'train' | 'boat')[]; retailer?: string | Retailer;
courier: string | Courier; status?: ('requested' | 'accepted' | 'archived')[];
timeSensitive?: boolean;
status?: ('routeRequested' | 'inTransit' | 'completed')[];
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
export interface Product { export interface Product {
id: string; id: string;
productTitle: string; productTitle: string;
picture?: string | Media;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }
@ -107,8 +101,7 @@ export interface Retailer {
* @maxItems 2 * @maxItems 2
*/ */
location?: [number, number]; location?: [number, number];
products?: string[] | Product[]; requestedProducts?: string[] | Product[];
salesPoint?: string;
updatedAt: string; updatedAt: string;
createdAt: string; createdAt: string;
} }

View File

@ -10,34 +10,9 @@ const Couriers: CollectionConfig = {
}, },
fields: [ fields: [
{ {
name: 'name', name: 'id',
type: 'text', type: 'text',
required: true, required: true,
},
{
name: 'startingPoint',
label: 'Traveling from',
type: 'text', // TODO use geopicker here
},
{
name: 'destination',
label: 'Traveling to',
type: 'text' // TODO user geopicker here
},
{
name: 'departureDate',
label: 'Departure date',
type: 'date'
},
{
name: 'arrivalDate',
label: 'Arrival date',
type: 'date'
},
{
name: 'weightAllowance',
label: 'Weight Allowance (KG)',
type: 'number'
} }
], ],
}; };

View File

@ -10,86 +10,78 @@ const Dispatches: CollectionConfig = {
}, },
fields: [ fields: [
{ {
name: 'dispatchesCode', name: 'dispatchesCode',
type: 'text', type: 'text',
required: true, required: true,
}, },
{ {
name: 'products', name: 'products',
type: 'relationship', type: 'relationship',
relationTo: 'products', relationTo: 'products',
hasMany: true, hasMany: true // required
}, // required,
{ },
name: 'startingPoint', {
type: 'relationship', name: 'courier',
relationTo: 'makers', type: 'relationship',
hasMany: false, relationTo: 'couriers',
}, hasMany: false,
{ required: false
name: 'endPoint', },
type: 'relationship', {
relationTo: 'retailers', name: 'maker',
hasMany: false, type: 'relationship',
}, relationTo: 'makers',
{ hasMany: false,
name: 'typeOfTransportation', required: false
type: 'select', },
hasMany: true, {
options: [ name: 'retailer',
{ type: 'relationship',
label: 'Air', relationTo: 'retailers',
value: 'air' hasMany: false,
}, required: false
{ },
label: 'Car', {
value: 'car' name: 'status',
}, type: 'select',
{ hasMany: true,
label: 'Train', options: [
value: 'train' {
}, label: 'Requested',
{ value: 'requested',
label: 'Boat', },
value: 'boat' {
} label: 'Accepted',
] value: 'accepted',
}, },
{ {
name: 'courier', label: 'Archived',
type: 'relationship', value: 'archived',
relationTo: 'couriers', },
hasMany: false, ],
required: true }
},
{
name: 'timeSensitive',
type: 'checkbox',
},
{
name: 'status', // required
type: 'select', // required
hasMany: true,
// admin: {
// isClearable: true,
// isSortable: true, // use mouse to drag and drop different values, and sort them according to your choice
// },
options: [
{
label: 'Route requested',
value: 'routeRequested',
},
{
label: 'In transit',
value: 'inTransit',
},
{
label: 'Completed',
value: 'completed',
},
],
}
], ],
}; };
export default Dispatches; export default Dispatches;
// type Dispatch = {
// id: string;
// dispatchesCode: string; //Human readable id
// createdAt: string;
// updatedAt: string;
// maker: Maker;
// retailer: Retailer;
// products: Product[];
// courier: User;
// timeSensitive: boolean;
// status: DispatchStatus[];
// departureDate: string;
// arrivalDate: string;
// weightAllowance: number;
// }

View File

@ -11,22 +11,20 @@ const Makers: CollectionConfig = {
}, },
fields: [ fields: [
{ {
name: 'name', // required name: 'name',
type: 'text', // required type: 'text',
required: true, required: true,
}, },
//geoPickerField, is a bit broken right now
{ {
name: 'location', name: 'location',
type: 'point', type: 'point',
label: 'Location', label: 'Location',
}, },
{ {
name: 'products', // required name: 'products',
type: 'relationship', // required type: 'relationship',
relationTo: 'products', // required relationTo: 'products',
hasMany: true, hasMany: true,
// TODO: make the name of the product visible in the dropdown
} }
], ],
}; };

View File

@ -10,10 +10,16 @@ const Products: CollectionConfig = {
}, },
fields: [ fields: [
{ {
name: 'productTitle', // required name: 'productTitle',
type: 'text', // required type: 'text',
required: true, required: true,
}, },
{
name: 'picture',
type: 'relationship',
relationTo: 'media',
hasMany: false
}
], ],
}; };

View File

@ -10,26 +10,21 @@ const Retailers: CollectionConfig = {
}, },
fields: [ fields: [
{ {
name: 'name', // required name: 'name',
type: 'text', // required type: 'text',
required: true, required: true,
}, },
{ {
name: 'location', name: 'location',
type: 'point', type: 'point',
label: 'Location', label: 'Location',
}, },
{ {
name: 'products', // required name: 'requestedProducts',
type: 'relationship', // required type: 'relationship',
relationTo: 'products', // required relationTo: 'products',
hasMany: true, hasMany: true,
}, },
{
name: 'salesPoint',
type: 'text',
label: 'Where do you plan to sell/exhibit products?'
}
], ],
}; };

View File

@ -14,6 +14,17 @@ const Users: CollectionConfig = {
{ {
name: 'name', name: 'name',
type: 'text', type: 'text',
required: true
},
{
name: 'phoneNumber',
type: 'number',
required: true
},
{
name: 'email',
type: 'email',
required: true
} }
], ],
}; };

View File

@ -0,0 +1,48 @@
//Dispatch Represents:
//- a connection between two locations
//- An agreement between a courier, a maker and a retailer to transport goods (Products) from point A (maker) to point B (retailer)
//- A dispatch has 4 statuses: offered (by a courier), requested (by a maker/retailer), accepted and archived
//- An offered dispatch will not yet have a Product, Maker, or Retailer.
//- A requested dispatch will have a Product, Maker and Retailer, but not a courier
//- A dispatch is accepted (moved from offered/requested) once all parties have accepted the conditions
//- A retailer accepts an offered route by responding with products (courier must then also confirm, but does not provide further data)
//- A courier accepts a requested route by responding with a date of arrival (the retailer must then confirm, but does not provide further data)
//- A retailer requests a dispatch by requesting a product they want to stock
//- A maker requests a dispatch by requesting to stock a product with a retailer
const DISPATCH_STATUS = ['requested', 'accepted', 'archived'] as const;
type DispatchStatus = typeof DISPATCH_STATUS[number];
type Dispatch = {
id: string;
dispatchesCode: string; //Human readable id
createdAt: string;
updatedAt: string;
maker: Maker;
retailer: Retailer;
products: Product[];
courier: User;
timeSensitive: boolean;
status: DispatchStatus[];
departureDate: string;
arrivalDate: string;
weightAllowance: number;
}
//Courier is just a person (User) and doesn't need to be its own thing. A user is a courier when they accept a dispatch as a courier
// Delete the couriers collection
type Product = {
id: string;
productTitle: string;
weight: number;
img: string;
createdAt: string;
updatedAt: string;
};