Restructure types for retailer-centric flow
This commit is contained in:
parent
2ebb81a981
commit
174a163cd3
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
// }
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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?'
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
48
sampleResponses/How dispatches work.ts
Normal file
48
sampleResponses/How dispatches work.ts
Normal 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;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user