Auth flow functional

This commit is contained in:
2024-04-06 17:28:36 +02:00
parent 98e967f7c0
commit 3b5ac81cc3
16 changed files with 646 additions and 406 deletions

80
astro/src/astroTypes.ts Normal file
View File

@ -0,0 +1,80 @@
export interface User {
name: string;
id: string;
email: string;
phoneNumber: string;
}
export interface Node {
name: string;
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;
}
export interface Product extends Node {
id: string;
name: string;
weight?: number;
picture: Media;
createdAt: string;
updatedAt: string;
};
// export interface Location = {
// latitude: number;
// longitude: number;
// }
export interface Maker extends Node {
email: string;
phoneNumber?: string;
location: [number, number];
stock: Product[];
createdAt: string;
updatedAt: string;
};
export interface Retailer extends Node {
email: string;
phoneNumber?: string;
location: [number, number];
stock: Product[];
createdAt: string;
updatedAt: string;
};
const DISPATCH_STATUS = ['requested', 'accepted', 'archived'] as const;
export type DispatchStatus = typeof DISPATCH_STATUS[number];
export interface 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

@ -3,9 +3,10 @@ import { MapContainer, TileLayer, Marker, CircleMarker, Popup, Polyline, LayerGr
import 'leaflet/dist/leaflet.css';
import L, { LatLngBounds } from 'leaflet';
import Contacts from './Contacts';
import type { User, Node, Retailer, Maker, Product, Dispatch, DispatchStatus } from '../astroTypes';
import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/react-query";
import axios from "axios";
import { useGetMakers, useGetDispatches, useGetRetailers, useGetUser, useGetMyself } from "../utils/hooks"
import { Button, buttonVariants } from './ui/Button';
@ -17,156 +18,8 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
//Todo: Move types to own file
interface User {
name: string;
id: string;
email: string;
phoneNumber: string;
}
interface Node {
name: string;
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: Media;
createdAt: string;
updatedAt: string;
};
// interface Location = {
// latitude: number;
// longitude: number;
// }
interface Maker extends Node {
email: string;
phoneNumber?: string;
location: [number, number];
stock: Product[];
createdAt: string;
updatedAt: string;
};
interface Retailer extends Node {
email: string;
phoneNumber?: string;
location: [number, number];
stock: Product[];
createdAt: string;
updatedAt: string;
};
const DISPATCH_STATUS = ['requested', 'accepted', 'archived'] as const;
type DispatchStatus = typeof DISPATCH_STATUS[number];
interface 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;
}
//Todo: update fetch url endpoints
//Todo: Move queryclient and hooks to own file
//Todo: Move axios stuff
const API_URL = "http://localhost:3001"
const headers = {
"Content-Type": "application/json",
}
const getMakers = async () => {
const url = `${API_URL}/api/makers`
console.log("Fetching url:", url)
const response = await axios.get(url);
const makers: Maker[] = response.data.docs;
console.log(`Fetch result from ${url}`, makers)
return makers;
}
const useGetMakers = () => {
return useQuery<Maker[]>({
queryFn: () => getMakers(),
queryKey: ['makers'],
enabled: true
})
}
const getRetailers = async () => {
const url = `${API_URL}/api/retailers`
console.log("Fetching url:", url)
const response = await axios.get(url);
const retailers: Retailer[] = response.data.docs;
console.log(`Fetch result from ${url}`, retailers)
return retailers;
}
const useGetRetailers = () => {
return useQuery<Retailer[]>({
queryFn: () => getRetailers(),
queryKey: ['retailers'],
enabled: true
})
}
const getDispatches = async () => {
const url = `${API_URL}/api/dispatches`
console.log("Fetching url:", url)
const response = await axios.get(url);
const dispatches: Dispatch[] = response.data.docs;
console.log(`Fetch result from ${url}`, dispatches)
return dispatches;
}
const useGetDispatches = () => {
return useQuery<Dispatch[]>({
queryFn: () => getDispatches(),
queryKey: ['dispatches'],
enabled: true
})
}
import { LoginForm } from './LoginForm';
import { hasAuthCookie } from '@/utils/authUtils';
//Payload longitude and latitude are mislabeled in payload (lol)
const locationSwitcharoo = (location: number[]) => {
@ -206,10 +59,14 @@ interface NodeSelection {
export const KiosMap = () => {
const [authToken, setAuthToken] = useState('')
const { data: makers, isLoading: isLoadingMakers } = useGetMakers();
const { data: retailers, isLoading: isLoadingRetailers } = useGetRetailers();
const { data: dispatches, isLoading: isLoadingDispatches } = useGetDispatches();
const { data: myself, isLoading: isLoadingMyself } = useGetMyself(authToken);
const [selectedNode, setSelectedNode] = useState<NodeSelection>({ id: "", type: "none" })
let selectedMaker: Maker | undefined = undefined;
@ -267,31 +124,64 @@ 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-[998] left-10 top-1/5 mt-24 p-4'>
<div className='flex gap-8 flex-col'>
{selectedMaker !== undefined && (
<div className='flex gap-4 flex-col'>
<Contacts
name={selectedMaker.name}
email={selectedMaker.email}
phoneNumber={selectedMaker.phoneNumber}
role={'maker'}
/>
{(selectedMaker.stock !== undefined && selectedMaker.stock.length > 0) &&
<Dialog>
<DialogTrigger
className={buttonVariants({ variant: "kios" })}
>
See catalogue
</DialogTrigger>
<div className='flex flex-col gap-4'>
<Dialog>
<div className="flex justify-between items-end px-4 gap-4">
<img
width={120}
src="/kios-logo.png"
alt="" />
{(myself && myself.name) &&
<p>Logged in as: {myself.name}</p>
}
{
(!hasAuthCookie() && !authToken) &&
<DialogTrigger
className={`px-14 w-6 ${buttonVariants({ variant: "kios" })}`}
>
Login
</DialogTrigger>
}
</div>
<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>
<DialogContent>
<DialogHeader>
<DialogTitle className="text-4xl text-center">Login</DialogTitle>
<LoginForm setAuthToken={setAuthToken} authToken={authToken}/>
</DialogHeader>
</DialogContent>
</Dialog>
<div className='w-full flex justify-center align-middle'>
<div className=''>
<div>
<img src="/route-guide.png" width={300} className="absolute right-6 top-3/4 z-[998] border border-black" alt="" />
</div>
</div>
{
selectedNode.type !== 'none' && (
<div className='absolute bg-white border-gray-950 border-2 z-[998] left-10 top-1/5 mt-24 p-4'>
<div className='flex gap-8 flex-col'>
{selectedMaker !== undefined && (
<div className='flex gap-4 flex-col'>
<Contacts
name={selectedMaker.name}
email={selectedMaker.email}
phoneNumber={selectedMaker.phoneNumber}
role={'maker'}
/>
{(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>
<ul className='flex flex-col gap-4'>
{selectedMaker.stock.map((product, i) => {
return (
@ -322,175 +212,175 @@ export const KiosMap = () => {
)
})}
</ul>
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
}
</div>
)}
{selectedRetailer !== undefined && (
<>
<Contacts
name={selectedRetailer.name}
email={selectedRetailer.email}
phoneNumber={selectedRetailer.phoneNumber}
role={'retailer'}
/>
</>
)}
{selectedDispatch !== undefined && (
<div className='flex flex-col gap-8'>
<div>
<h2 className='text-xl font-bold underline-offset-2 underline py-2'>
Product{selectedDispatch.products.length > 1 && 's'}
</h2>
{selectedDispatch.products.map((product, i) => {
return (
<div className='flex flex-row items-center gap-4'>
<img
src={product.picture.url}
alt={product.picture.alt}
className='border-2 border-black'
width={60}
/>
<h3 className='font-bold text-xl'>{product.name}</h3>
</div>
)
})}
</DialogHeader>
</DialogContent>
</Dialog>
}
</div>
)}
{selectedRetailer !== undefined && (
<>
<Contacts
name={selectedRetailer.name}
email={selectedRetailer.email}
phoneNumber={selectedRetailer.phoneNumber}
role={'retailer'}
/>
</>
)}
<Contacts
name={selectedDispatch.maker.name}
email={selectedDispatch.maker.email}
phoneNumber={selectedDispatch.maker.phoneNumber}
role={'maker'}
/>
{selectedDispatch !== undefined && (
<div className='flex flex-col gap-8'>
<div>
<h2 className='text-xl font-bold underline-offset-2 underline py-2'>
Product{selectedDispatch.products.length > 1 && 's'}
</h2>
{selectedDispatch.products.map((product, i) => {
return (
<div className='flex flex-row items-center gap-4'>
<img
src={product.picture.url}
alt={product.picture.alt}
className='border-2 border-black'
width={60}
/>
<h3 className='font-bold text-xl'>{product.name}</h3>
</div>
)
})}
</div>
<Contacts
name={selectedDispatch.retailer.name}
email={selectedDispatch.retailer.email}
phoneNumber={selectedDispatch.retailer.phoneNumber}
role={'retailer'}
/>
{selectedDispatch.courier !== undefined ? (
<Contacts
name={selectedDispatch.courier.name}
email={selectedDispatch.courier.email}
phoneNumber={selectedDispatch.courier.phoneNumber}
role={'courier'}
name={selectedDispatch.maker.name}
email={selectedDispatch.maker.email}
phoneNumber={selectedDispatch.maker.phoneNumber}
role={'maker'}
/>
) :
<div>
<h2 className='text-xl font-bold underline-offset-2 underline py-2'>No courier!</h2>
<Button
variant={"kios"}
onClick={() => handleAcceptRoute()}
>
Accept route as courier
</Button>
</div>
}
</div>
)}
<Contacts
name={selectedDispatch.retailer.name}
email={selectedDispatch.retailer.email}
phoneNumber={selectedDispatch.retailer.phoneNumber}
role={'retailer'}
/>
{selectedDispatch.courier !== undefined ? (
<Contacts
name={selectedDispatch.courier.name}
email={selectedDispatch.courier.email}
phoneNumber={selectedDispatch.courier.phoneNumber}
role={'courier'}
/>
) :
<div>
<h2 className='text-xl font-bold underline-offset-2 underline py-2'>No courier!</h2>
<Button
variant={"kios"}
onClick={() => handleAcceptRoute()}
>
Accept route as courier
</Button>
</div>
}
</div>
)}
</div>
</div>
</div>
)
}
<MapContainer
id="map"
center={[-6.1815, 106.8228]}
zoom={3}
maxBounds={bounds} // Restrict panning beyond these bounds
maxBoundsViscosity={0.9} // How strongly to snap the map's bounds to the restricted area
style={{ height: '800px', width: '100%' }}
>
<TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
eventHandlers={{
click: () => handleSelectNode("", "none")
}}
attribution='&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
/>
{(makers && !isLoadingMakers) &&
<LayerGroup>
{makers.map((maker: any, index: number) => (
<Marker
eventHandlers={{
click: () => handleSelectNode(maker.id, "maker")
}}
key={maker.id}
position={[locationSwitcharoo(maker.location)[0], locationSwitcharoo(maker.location)[1]]}
icon={selectedNode.id === maker.id ? selectedDotIcon : blackDotIcon}
>
{/* <Popup>{maker.name}</Popup> */}
</Marker>
))}
</LayerGroup>
)
}
{(retailers && !isLoadingRetailers) &&
<LayerGroup>
{retailers.map((retailer: any, index: number) => (
<Marker
eventHandlers={{
click: () => handleSelectNode(retailer.id, "retailer")
}}
key={retailer.id}
position={[locationSwitcharoo(retailer.location)[0], locationSwitcharoo(retailer.location)[1]]}
icon={selectedNode.id === retailer.id ? selectedDotIcon : blackDotIcon}
>
{/* <Popup>{retailer.name}</Popup> */}
</Marker>
))}
</LayerGroup>
}
<MapContainer
id="map"
center={[-6.1815, 106.8228]}
zoom={3}
maxBounds={bounds} // Restrict panning beyond these bounds
maxBoundsViscosity={0.9} // How strongly to snap the map's bounds to the restricted area
style={{ height: '800px', width: '100%' }}
>
<TileLayer url="https://tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
eventHandlers={{
click: () => handleSelectNode("", "none")
}}
attribution='&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
/>
{(dispatches && !isLoadingDispatches) &&
<LayerGroup>
{dispatches.map((dispatch: any, index: number) => {
{(makers && !isLoadingMakers) &&
<LayerGroup>
{makers.map((maker: any, index: number) => (
<Marker
eventHandlers={{
click: () => handleSelectNode(maker.id, "maker")
}}
key={maker.id}
position={[locationSwitcharoo(maker.location)[0], locationSwitcharoo(maker.location)[1]]}
icon={selectedNode.id === maker.id ? selectedDotIcon : blackDotIcon}
>
{/* <Popup>{maker.name}</Popup> */}
</Marker>
))}
</LayerGroup>
}
if (dispatch.maker && dispatch.retailer) {
{(retailers && !isLoadingRetailers) &&
<LayerGroup>
{retailers.map((retailer: any, index: number) => (
<Marker
eventHandlers={{
click: () => handleSelectNode(retailer.id, "retailer")
}}
key={retailer.id}
position={[locationSwitcharoo(retailer.location)[0], locationSwitcharoo(retailer.location)[1]]}
icon={selectedNode.id === retailer.id ? selectedDotIcon : blackDotIcon}
>
{/* <Popup>{retailer.name}</Popup> */}
</Marker>
))}
</LayerGroup>
}
const start = locationSwitcharoo(dispatch.maker.location);
const end = locationSwitcharoo(dispatch.retailer.location);
{(dispatches && !isLoadingDispatches) &&
<LayerGroup>
{dispatches.map((dispatch: any, index: number) => {
if (dispatch.maker && dispatch.retailer) {
const start = locationSwitcharoo(dispatch.maker.location);
const end = locationSwitcharoo(dispatch.retailer.location);
let productsString = '';
dispatch.products.forEach((product: any, i: number) => {
productsString += product.productTitle + (i + 1 < dispatch.products.length ? ', ' : '');
});
let productsString = '';
dispatch.products.forEach((product: any, i: number) => {
productsString += product.productTitle + (i + 1 < dispatch.products.length ? ', ' : '');
});
//status type should already be inferred when list of dispatches is created, weird that is is required
const status: DispatchStatus = dispatch.status;
//status type should already be inferred when list of dispatches is created, weird that is is required
const status: DispatchStatus = dispatch.status;
const dashArray: string = dashArrays[status]
const dashColor: string = dashColors[status]
const dashOpacity: number = dashOpacities[status]
const dashArray: string = dashArrays[status]
const dashColor: string = dashColors[status]
const dashOpacity: number = dashOpacities[status]
return (
<Polyline
eventHandlers={{
click: () => handleSelectNode(dispatch.id, "dispatch")
}}
key={dispatch.id}
positions={[[start[0], start[1]], [end[0], end[1]]]}
pathOptions={{ color: selectedNode.id === dispatch.id ? dashColorSelected : dashColor }}
opacity={dashOpacity}
dashArray={dashArray} />
);
}
})}
</LayerGroup>
}
</MapContainer >
return (
<Polyline
eventHandlers={{
click: () => handleSelectNode(dispatch.id, "dispatch")
}}
key={dispatch.id}
positions={[[start[0], start[1]], [end[0], end[1]]]}
pathOptions={{ color: selectedNode.id === dispatch.id ? dashColorSelected : dashColor }}
opacity={dashOpacity}
dashArray={dashArray} />
);
}
})}
</LayerGroup>
}
</MapContainer >
</div>
</div>
);
};

View File

@ -0,0 +1,143 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/Button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import axios from "axios";
import { setAuthCookie } from "@/utils/authUtils"
const API_URL = "http://localhost:3001";
const headers = {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": "true"
}
const loginFetch = async (email: string, password: string) => {
try {
const response = await fetch(`${API_URL}/api/users/login`, {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email: email,
password: password
}),
})
} catch (err) {
console.log(err)
}
};
interface LoginFormProps {
setAuthToken: (token: string) => void;
authToken: string;
}
export function LoginForm(props: LoginFormProps) {
const login = async (email: string, password: string) => {
try {
const response = await axios.post(`${API_URL}/api/users/login`, {
email: email,
password: password
}, {
withCredentials: true, // include cookies in the request
headers: headers
});
const data = response.data;
if (response.status !== 200) {
throw Error("Login failed")
}
setAuthCookie(data.token, 30);
props.setAuthToken(data.token)
return
} catch (error) {
window.alert(error)
}
};
const formSchema = z.object({
email: z.string().email({
message: "Email must be valid"
}),
password: z.string().min(1, {
}),
})
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: "",
password: "",
},
})
function onSubmit(values: z.infer<typeof formSchema>) {
login(values.email, values.password)
}
return (
<div className="flex justify-center">
{props.authToken ? <p>Login successful</p>
:
<div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" placeholder="" {...field} />
</FormControl>
<FormDescription>
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button variant={"kios"} type="submit">Login</Button>
</form>
</Form>
</div>
}
</div>
)
}

View File

@ -3,9 +3,9 @@ import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
type ControllerProps,
type FieldPath,
type FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form"

View File

@ -22,8 +22,6 @@ export interface User {
id: string;
name: string;
phoneNumber?: number;
adminOfMakers?: string[] | Maker[];
adminOfRetailers?: string[] | Retailer[];
updatedAt: string;
createdAt: string;
email: string;
@ -35,28 +33,6 @@ 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;
@ -69,20 +45,6 @@ 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;
@ -99,3 +61,41 @@ 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];
admins?: string[] | User[];
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];
admins?: string[] | User[];
stock?: string[] | Product[];
updatedAt: string;
createdAt: string;
}

View File

@ -0,0 +1,12 @@
export const hasAuthCookie = () => {
const matches = document.cookie.match(/^(.*;)?\s*payload-token\s*=\s*[^;]+(.*)?$/) || []
const hasMatch = matches.length > 0
return hasMatch;
}
export const setAuthCookie = (value: string, expirationDays: number) => {
const date = new Date();
date.setTime(date.getTime() + (expirationDays * 24 * 60 * 60 * 1000));
const expires = "expires=" + date.toUTCString();
document.cookie = "payload-token=" + value + ";" + expires + ";path=/";
}

View File

@ -0,0 +1,114 @@
import type { User, Node, Retailer, Maker, Product, Dispatch } from '../astroTypes';
import { useQuery, useMutation, useQueryClient, queryOptions } from "@tanstack/react-query";
import axios from "axios";
import { hasAuthCookie } from './authUtils';
const API_URL = "http://localhost:3001"
const nonAuthHeaders = {
"Content-Type": "application/json",
}
const authHeaders = {
"Content-Type": "application/json",
}
const getMakers = async () => {
const url = `${API_URL}/api/makers`
console.log("Fetching url:", url)
const response = await axios.get(url);
const makers: Maker[] = response.data.docs;
console.log(`Fetch result from ${url}`, makers)
return makers;
}
export const useGetMakers = () => {
return useQuery<Maker[]>({
queryFn: () => getMakers(),
queryKey: ['makers'],
enabled: true
})
}
const getRetailers = async () => {
const url = `${API_URL}/api/retailers`
console.log("Fetching url:", url)
const response = await axios.get(url);
const retailers: Retailer[] = response.data.docs;
console.log(`Fetch result from ${url}`, retailers)
return retailers;
}
export const useGetRetailers = () => {
return useQuery<Retailer[]>({
queryFn: () => getRetailers(),
queryKey: ['retailers'],
enabled: true
})
}
const getUser = async (userId: string) => {
const url = `${API_URL}/api/users/${userId}`
console.log("Fetching url:", url)
const response = await axios.get(url);
const user: User = response.data.docs;
console.log(`Fetch result from ${url}`, user)
return user;
}
export const useGetUser = (userId: string) => {
return useQuery<User>({
queryFn: () => getUser(userId),
queryKey: ['user'],
enabled: true//If login cookie
})
}
const getMyself = async (authToken: string) => {
const url = `${API_URL}/api/users/me`
console.log("Fetching url:", url)
const response = await axios.get(`${API_URL}/api/users/me`, {
withCredentials: true,
headers: authHeaders
});
const user: User = response.data.docs;
console.log(`Fetch result from ${url}`, user)
return user;
}
export const useGetMyself = (authToken: string) => {
return useQuery<User>({
queryFn: () => getMyself(authToken),
queryKey: ['myself'],
enabled: authToken !== ''
})
}
const getDispatches = async () => {
const url = `${API_URL}/api/dispatches`
console.log("Fetching url:", url)
const response = await axios.get(url);
const dispatches: Dispatch[] = response.data.docs;
console.log(`Fetch result from ${url}`, dispatches)
return dispatches;
}
export const useGetDispatches = () => {
return useQuery<Dispatch[]>({
queryFn: () => getDispatches(),
queryKey: ['dispatches'],
enabled: true
})
}