├── frontend
├── src
│ ├── vite-env.d.ts
│ ├── main.tsx
│ ├── Pages
│ │ ├── dashboard.css
│ │ ├── Home.tsx
│ │ ├── SimulatorDashboard.tsx
│ │ └── UserDashboard.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── AppBar.tsx
│ │ ├── ShipmentInfo.tsx
│ │ ├── ShipmentRequest.tsx
│ │ ├── DraggableMarker.tsx
│ │ ├── UserInfo.tsx
│ │ ├── DriverDashboard.tsx
│ │ └── ShipmentDashboard.tsx
│ ├── assets
│ │ ├── truck.svg
│ │ ├── icon_delivery_associate.svg
│ │ ├── icon_drop.svg
│ │ ├── icon_pickup.svg
│ │ └── react.svg
│ ├── api.ts
│ ├── types.ts
│ └── constants.ts
├── vite.config.ts
├── tsconfig.node.json
├── .gitignore
├── index.html
├── tsconfig.json
├── package.json
├── public
│ └── vite.svg
└── yarn.lock
├── .vscode
├── extensions.json
├── settings.json
└── launch.json
├── src
├── types
│ ├── IUser.ts
│ ├── AppRequest.ts
│ ├── IUserJWTPayload.ts
│ ├── AppResponse.ts
│ ├── IDeliveryAssociate.ts
│ └── IShipment.ts
├── config.ts
├── controller
│ ├── ping.ts
│ ├── user.ts
│ └── shipment.ts
├── services
│ ├── users
│ │ ├── findByEmail.ts
│ │ ├── findById.ts
│ │ └── createOne.ts
│ ├── deliveryAssociates
│ │ ├── findByEmail.ts
│ │ ├── updateLocation.ts
│ │ └── createOne.ts
│ └── shipments
│ │ ├── createOne.ts
│ │ ├── updateDA.ts
│ │ └── updateStatus.ts
├── routes
│ └── index.ts
├── createIndex.ts
├── constants.ts
├── models
│ ├── User.ts
│ ├── Shipment.ts
│ └── DeliveryAssociate.ts
├── watchers
│ ├── deliveryAssociates.ts
│ └── shipment.ts
├── dbClient.ts
├── seed.ts
├── server.ts
├── app.ts
└── socketHandler.ts
├── .gitignore
├── tsconfig.json
├── package.json
└── Readme.md
/frontend/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["esbenp.prettier-vscode"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/types/IUser.ts:
--------------------------------------------------------------------------------
1 | export default interface IUser {
2 | email: string;
3 | name: string;
4 | password: string;
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "prettier.semi": true,
3 | "prettier.singleQuote": true,
4 | "prettier.tabWidth": 2,
5 | "prettier.trailingComma": "es5"
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/types/AppRequest.ts:
--------------------------------------------------------------------------------
1 | import { Request } from 'express';
2 | import IUserJWTPayload from './IUserJWTPayload';
3 |
4 | interface AppRequest extends Request {
5 | user: IUserJWTPayload;
6 | }
7 | export default AppRequest;
8 |
--------------------------------------------------------------------------------
/src/types/IUserJWTPayload.ts:
--------------------------------------------------------------------------------
1 | import jwt from 'jsonwebtoken';
2 |
3 | interface IUserJWTPayload extends jwt.JwtPayload {
4 | userId: string;
5 | userEmail: string;
6 | organizationId: string;
7 | }
8 | export default IUserJWTPayload;
9 |
--------------------------------------------------------------------------------
/frontend/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App'
4 |
5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
6 |
7 |
8 | ,
9 | )
10 |
--------------------------------------------------------------------------------
/src/types/AppResponse.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is a recommended response structure which should be used in every res.send()
3 | * This will help maintain a uniform response format from API
4 | */
5 | export default interface AppResponse {
6 | data: any;
7 | isError: boolean;
8 | errMsg?: string;
9 | statusCode?: number;
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | results
3 | tmp
4 |
5 | # Coverage reports
6 | coverage
7 |
8 | # API keys and secrets
9 | .env
10 |
11 | # Dependency directory
12 | node_modules
13 |
14 | # OS metadata
15 | .DS_Store
16 | Thumbs.db
17 |
18 | # Ignore built ts files
19 | dist/**/*
20 |
21 | # ignore yarn.lock
22 | package-lock.json
23 |
24 | # build artifacts
25 | dist
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | interface IConfig {
2 | port: string;
3 | mongoConnString: string;
4 | secretKey: string;
5 | corsOptions: any;
6 | }
7 | const config: IConfig = {
8 | port: process.env.PORT || '5050',
9 | mongoConnString: process.env.MONGODB_CONNECTION_STRING || '',
10 | secretKey: process.env.SECRET_KEY,
11 | corsOptions: { origin: '*' },
12 | };
13 |
14 | export default config;
15 |
--------------------------------------------------------------------------------
/frontend/src/Pages/dashboard.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: flex;
3 | }
4 |
5 | .col-1 {
6 | flex: 1;
7 | padding: 15px 20px;
8 | }
9 |
10 | .col-2 {
11 | flex: 1;
12 | border: 2px solid black;
13 | margin: 10px;
14 | }
15 |
16 | .marker {
17 | z-index: 10000 !important;
18 | }
19 |
20 | .flex-center {
21 | display: flex;
22 | justify-content: center;
23 | margin: 20px 0;
24 | }
25 |
--------------------------------------------------------------------------------
/src/controller/ping.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from 'express';
2 | import AppResponse from '../types/AppResponse';
3 |
4 | const pong = async (req: Request, res: Response, next: NextFunction) => {
5 | try {
6 | const response: AppResponse = { data: 'pong', isError: false };
7 | res.send(response);
8 | } catch (error) {
9 | next(error);
10 | }
11 | };
12 |
13 | export { pong };
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "esModuleInterop": true,
5 | "allowSyntheticDefaultImports": true,
6 | "target": "es6",
7 | "noImplicitAny": true,
8 | "moduleResolution": "node",
9 | "sourceMap": true,
10 | "outDir": "dist",
11 | "baseUrl": ".",
12 | "paths": {
13 | "*": ["node_modules/*", "src/types/*"]
14 | }
15 | },
16 | "include": ["src/**/*"]
17 | }
18 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/types/IDeliveryAssociate.ts:
--------------------------------------------------------------------------------
1 | import { Point } from 'geojson';
2 |
3 | export enum DeliveryAssociateStatus {
4 | available = 'available', // ready to accept new shipment
5 | delivering = 'delivering', // transporting goods
6 | off = 'off', // on leave
7 | }
8 |
9 | export default interface IDeliveryAssociate {
10 | email: string;
11 | name: string;
12 | status: DeliveryAssociateStatus;
13 | currentLocation: Point;
14 | }
15 |
--------------------------------------------------------------------------------
/src/services/users/findByEmail.ts:
--------------------------------------------------------------------------------
1 | import UserCollection, {
2 | IUserDocument,
3 | } from '../../models/User';
4 |
5 | const findByEmail = async (
6 | email: string
7 | ): Promise => {
8 | try {
9 | const collection = await UserCollection();
10 | const user = await collection.findOne({ email });
11 | return user;
12 | } catch (error) {
13 | throw error;
14 | }
15 | };
16 | export default findByEmail;
17 |
--------------------------------------------------------------------------------
/src/services/users/findById.ts:
--------------------------------------------------------------------------------
1 | import { ObjectId } from 'mongodb';
2 | import UserCollection, { IUserDocument } from '../../models/User';
3 |
4 | const findById = async (id: string): Promise => {
5 | try {
6 | const collection = await UserCollection();
7 | const user = await collection.findOne({ _id: new ObjectId(id) });
8 | return user;
9 | } catch (error) {
10 | throw error;
11 | }
12 | };
13 | export default findById;
14 |
--------------------------------------------------------------------------------
/src/services/deliveryAssociates/findByEmail.ts:
--------------------------------------------------------------------------------
1 | import DeliveryAssociateCollection, {
2 | IDeliveryAssociateDocument,
3 | } from '../../models/DeliveryAssociate';
4 |
5 | const findByEmail = async (
6 | email: string
7 | ): Promise => {
8 | try {
9 | const collection = await DeliveryAssociateCollection();
10 | const associate = await collection.findOne({ email });
11 | return associate;
12 | } catch (error) {
13 | throw error;
14 | }
15 | };
16 | export default findByEmail;
17 |
--------------------------------------------------------------------------------
/src/services/users/createOne.ts:
--------------------------------------------------------------------------------
1 | import UserCollection, {
2 | IUserDocument,
3 | } from '../../models/User';
4 | import IUser from '../../types/IUser';
5 |
6 | export default async function createOne(
7 | user: IUser
8 | ): Promise {
9 | try {
10 | const collection = await UserCollection();
11 | const newDoc = await collection.insertOne(user);
12 | const result = await collection.findOne({ _id: newDoc.insertedId });
13 | return result;
14 | } catch (error) {
15 | throw error;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/services/deliveryAssociates/updateLocation.ts:
--------------------------------------------------------------------------------
1 | import { Point } from 'geojson';
2 | import DeliveryAssociateCollection from '../../models/DeliveryAssociate';
3 |
4 | const updateLocation = async (
5 | email: string,
6 | location: Point
7 | ): Promise => {
8 | try {
9 | const collection = await DeliveryAssociateCollection();
10 | await collection.findOneAndUpdate(
11 | { email },
12 | { $set: { currentLocation: location } }
13 | );
14 | } catch (error) {
15 | throw error;
16 | }
17 | };
18 | export default updateLocation;
19 |
--------------------------------------------------------------------------------
/src/routes/index.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 | import { pong } from '../controller/ping';
3 | import * as shipment from '../controller/shipment';
4 |
5 | let routes = Router();
6 |
7 | /**
8 | * Health check route.
9 | * Always returns 200 OK
10 | */
11 | routes.get('/ping', pong);
12 |
13 | // Shipment
14 | routes.post('/shipment', shipment.createShipment);
15 | routes.patch(
16 | '/shipment/:id/delivery-associate',
17 | shipment.patchDeliveryAssociate
18 | );
19 | routes.patch('/shipment/:id/status', shipment.patchStatus);
20 | export default routes;
21 |
--------------------------------------------------------------------------------
/src/services/shipments/createOne.ts:
--------------------------------------------------------------------------------
1 | import ShipmentCollection, {
2 | IShipmentDocument,
3 | } from '../../models/Shipment';
4 | import IShipment from '../../types/IShipment';
5 |
6 | export default async function createOne(
7 | shipment: IShipment
8 | ): Promise {
9 | try {
10 | const collection = await ShipmentCollection();
11 | const newDoc = await collection.insertOne(shipment);
12 | const result = await collection.findOne({ _id: newDoc.insertedId });
13 | return result;
14 | } catch (error) {
15 | throw error;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/createIndex.ts:
--------------------------------------------------------------------------------
1 | import UserCollection from './models/User';
2 |
3 | const indexUserCollection = async () => {
4 | try {
5 | const usersCollection = await UserCollection();
6 | // createIndex only creates an index if it doesn't already exist
7 | await usersCollection.createIndex({ email: 1 }, { unique: true });
8 | } catch (error) {
9 | console.log('Error creating index for users collection');
10 | console.error(error);
11 | }
12 | };
13 |
14 | const createAllIndexes = async () => {
15 | await indexUserCollection();
16 | };
17 |
18 | export default createAllIndexes;
19 |
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | const socketEvents = {
2 | // DA means DELIVERY_ASSOCIATE
3 | UPDATE_DA_LOCATION: 'UPDATE_DA_LOCATION',
4 | DA_LOCATION_CHANGED: 'DA_LOCATION_CHANGED',
5 |
6 | SHIPMENT_CREATED: 'SHIPMENT_CREATED',
7 | SHIPMENT_UPDATED: 'SHIPMENT_UPDATED',
8 |
9 | SUBSCRIBE_TO_SHIPMENT: 'SUBSCRIBE_TO_SHIPMENT',
10 | SUBSCRIBE_TO_DA: 'SUBSCRIBE_TO_DA',
11 |
12 | LEAVE_ROOM: 'LEAVE_ROOM',
13 | };
14 |
15 | const dbCollections = {
16 | users: 'users',
17 | shipments: 'shipments',
18 | deliveryAssociates: 'deliveryAssociates',
19 | };
20 |
21 | export { socketEvents, dbCollections };
22 |
--------------------------------------------------------------------------------
/src/models/User.ts:
--------------------------------------------------------------------------------
1 | import { ObjectId, Collection, Document } from 'mongodb';
2 | import IUser from '../types/IUser';
3 | import dbClient from '../dbClient';
4 | import { dbCollections } from '../constants';
5 |
6 | export interface IUserDocument
7 | extends IUser,
8 | Document {
9 | _id?: ObjectId;
10 | }
11 |
12 | const UserCollection = async (): Promise<
13 | Collection
14 | > => {
15 | const mongoClient = await dbClient();
16 | const collection: Collection = mongoClient
17 | .db()
18 | .collection(dbCollections.users);
19 | return collection;
20 | };
21 |
22 | export default UserCollection;
23 |
--------------------------------------------------------------------------------
/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/src/services/deliveryAssociates/createOne.ts:
--------------------------------------------------------------------------------
1 | import DeliveryAssociateCollection, {
2 | IDeliveryAssociateDocument,
3 | } from '../../models/DeliveryAssociate';
4 | import IDeliveryAssociate from '../../types/IDeliveryAssociate';
5 |
6 | export default async function createOne(
7 | deliveryAssociate: IDeliveryAssociate
8 | ): Promise {
9 | try {
10 | const collection = await DeliveryAssociateCollection();
11 | const newDoc = await collection.insertOne(deliveryAssociate);
12 | const result = await collection.findOne({ _id: newDoc.insertedId });
13 | return result;
14 | } catch (error) {
15 | throw error;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/services/shipments/updateDA.ts:
--------------------------------------------------------------------------------
1 | import { ObjectId } from 'mongodb';
2 | import ShipmentCollection, { IShipmentDocument } from '../../models/Shipment';
3 |
4 | export default async function updateDA(
5 | _id: string,
6 | deliveryAssociateId: string
7 | ): Promise {
8 | try {
9 | const collection = await ShipmentCollection();
10 | await collection.findOneAndUpdate(
11 | { _id: new ObjectId(_id) },
12 | { $set: { deliveryAssociateId } }
13 | );
14 | const result = await collection.findOne({ _id: new ObjectId(_id) });
15 | return result;
16 | } catch (error) {
17 | console.error(error);
18 | throw error;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/models/Shipment.ts:
--------------------------------------------------------------------------------
1 | import { ObjectId, Collection, Document } from 'mongodb';
2 | import IShipment from '../types/IShipment';
3 | import dbClient from '../dbClient';
4 | import { dbCollections } from '../constants';
5 |
6 | export interface IShipmentDocument
7 | extends IShipment,
8 | Document {
9 | _id?: ObjectId;
10 | }
11 |
12 | const ShipmentCollection = async (): Promise<
13 | Collection
14 | > => {
15 | const mongoClient = await dbClient();
16 | const collection: Collection = mongoClient
17 | .db()
18 | .collection(dbCollections.shipments);
19 | return collection;
20 | };
21 |
22 | export default ShipmentCollection;
23 |
--------------------------------------------------------------------------------
/src/services/shipments/updateStatus.ts:
--------------------------------------------------------------------------------
1 | import { ObjectId } from 'mongodb';
2 | import ShipmentCollection, { IShipmentDocument } from '../../models/Shipment';
3 | import { ShipmentStatus } from '../../types/IShipment';
4 |
5 | export default async function updateStatus(
6 | _id: string,
7 | status: ShipmentStatus
8 | ): Promise {
9 | try {
10 | const collection = await ShipmentCollection();
11 | await collection.findOneAndUpdate(
12 | { _id: new ObjectId(_id) },
13 | { $set: { status } }
14 | );
15 | const result = await collection.findOne({ _id: new ObjectId(_id) });
16 | return result;
17 | } catch (error) {
18 | throw error;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/types/IShipment.ts:
--------------------------------------------------------------------------------
1 | import { Point } from 'geojson';
2 | import IUser from './IUser';
3 | import IDeliveryAssociate from './IDeliveryAssociate';
4 |
5 | export enum ShipmentStatus {
6 | requested = 'requested',
7 | deliveryAssociateAssigned = 'deliveryAssociateAssigned',
8 | pickupLocationReached = 'pickupLocationReached',
9 | transporting = 'transporting',
10 | dropLocationReached = 'dropLocationReached',
11 | delivered = 'delivered',
12 | cancelled = 'cancelled',
13 | }
14 |
15 | export default interface IShipment {
16 | pickupLocation: Point;
17 | dropLocation: Point;
18 | userId: string | IUser;
19 | deliveryAssociateId?: string | IDeliveryAssociate;
20 | status: ShipmentStatus;
21 | }
22 |
--------------------------------------------------------------------------------
/src/watchers/deliveryAssociates.ts:
--------------------------------------------------------------------------------
1 | import { Server } from 'socket.io';
2 | import { socketEvents } from '../constants';
3 | import DeliveryAssociateCollection from '../models/DeliveryAssociate';
4 |
5 | const watcher = async (io: Server) => {
6 | const collection = await DeliveryAssociateCollection();
7 | const changeStream = collection.watch([], { fullDocument: 'updateLookup' });
8 | changeStream.on('change', (event) => {
9 | if (event.operationType === 'update') {
10 | const fullDocument = event.fullDocument;
11 | io.to(String(fullDocument._id)).emit(
12 | socketEvents.DA_LOCATION_CHANGED,
13 | fullDocument
14 | );
15 | }
16 | });
17 | };
18 |
19 | export default watcher;
20 |
--------------------------------------------------------------------------------
/frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
2 | import AppBar from './components/AppBar';
3 | import UserDashboard from './Pages/UserDashboard';
4 | import SimulatorDashboard from './Pages/SimulatorDashboard';
5 | import Home from './Pages/Home';
6 |
7 | function App() {
8 | return (
9 |
10 |
11 |
12 | } />
13 | } />
14 | } />
15 | 404 Page Not Found} />
16 |
17 |
18 | );
19 | }
20 |
21 | export default App;
22 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "pwa-node",
9 | "request": "launch",
10 | "name": "Launch Program",
11 | "skipFiles": [
12 | "/**"
13 | ],
14 | "program": "${workspaceFolder}/src/server.ts",
15 | "preLaunchTask": "tsc: build - tsconfig.json",
16 | "outFiles": [
17 | "${workspaceFolder}/dist/**/*.js"
18 | ]
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/src/models/DeliveryAssociate.ts:
--------------------------------------------------------------------------------
1 | import { ObjectId, Collection, Document } from 'mongodb';
2 | import IDeliveryAssociate from '../types/IDeliveryAssociate';
3 | import dbClient from '../dbClient';
4 | import { dbCollections } from '../constants';
5 |
6 | export interface IDeliveryAssociateDocument
7 | extends IDeliveryAssociate,
8 | Document {
9 | _id?: ObjectId;
10 | }
11 |
12 | const DeliveryAssociateCollection = async (): Promise<
13 | Collection
14 | > => {
15 | const mongoClient = await dbClient();
16 | const collection: Collection = mongoClient
17 | .db()
18 | .collection(dbCollections.deliveryAssociates);
19 | return collection;
20 | };
21 |
22 | export default DeliveryAssociateCollection;
23 |
--------------------------------------------------------------------------------
/frontend/src/components/AppBar.tsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from 'react-router-dom';
2 | import AppBar from '@mui/material/AppBar';
3 | import Toolbar from '@mui/material/Toolbar';
4 | import Typography from '@mui/material/Typography';
5 |
6 | export default function MainAppBar() {
7 | const navigate = useNavigate();
8 | return (
9 | <>
10 |
11 |
12 | {
17 | navigate('/');
18 | }}
19 | style={{
20 | cursor: 'pointer',
21 | maxWidth: 'fit-content',
22 | }}
23 | >
24 | Delivery Service
25 |
26 |
27 |
28 |
29 | >
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/src/watchers/shipment.ts:
--------------------------------------------------------------------------------
1 | import { Server } from 'socket.io';
2 | import { socketEvents } from '../constants';
3 | import ShipmentCollection from '../models/Shipment';
4 |
5 | const watcher = async (io: Server) => {
6 | const collection = await ShipmentCollection();
7 | const changeStream = collection.watch([], { fullDocument: 'updateLookup' });
8 | changeStream.on('change', (event) => {
9 | // @ts-ignore
10 | const fullDocument = event.fullDocument;
11 | if (event.operationType === 'insert') {
12 | // Broadcast Shipment Available Msg to Delivery Associates
13 | io.emit(socketEvents.SHIPMENT_CREATED, fullDocument);
14 | }
15 | if (event.operationType === 'update') {
16 | io.to(String(fullDocument._id)).emit(
17 | socketEvents.SHIPMENT_UPDATED,
18 | fullDocument
19 | );
20 | }
21 | });
22 | };
23 |
24 | export default watcher;
25 |
--------------------------------------------------------------------------------
/frontend/src/components/ShipmentInfo.tsx:
--------------------------------------------------------------------------------
1 | import Alert from '@mui/material/Alert';
2 | import AlertTitle from '@mui/material/AlertTitle';
3 | import Stack from '@mui/material/Stack';
4 | import { DashboardStatus } from '../types';
5 | import { infoMsgs } from '../constants';
6 |
7 | type PropType = {
8 | dashboardStatus: DashboardStatus;
9 | };
10 | export default function ShipmentInfo(props: PropType) {
11 | const { title, msg } = infoMsgs[props.dashboardStatus];
12 | const severity =
13 | props.dashboardStatus === DashboardStatus.DELIVERED ? 'success' : 'info';
14 | return (
15 | <>
16 | {title && msg ? (
17 |
18 |
19 |
20 | {title}
21 |
22 | {msg}
23 |
24 |
25 | ) : null}
26 | >
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/src/dbClient.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file exports an instance of the MongoDB client
3 | * The client exported is a singleton, so it will only be created once and reused for all subsequent calls
4 | * */
5 | import { MongoClient } from 'mongodb';
6 | import config from './config';
7 |
8 | const uri = config.mongoConnString;
9 | let db: MongoClient;
10 |
11 | /**
12 | * Singleton function to connect to the database and return the client.
13 | * If a client is already connected, it will return the existing client
14 | * */
15 | const dbConnect = async (): Promise => {
16 | try {
17 | if (db) {
18 | return db;
19 | }
20 | console.log('Connecting to MongoDB...');
21 | const client = new MongoClient(uri);
22 | await client.connect();
23 | console.log('Connected to db');
24 | db = client;
25 | return db;
26 | } catch (error) {
27 | console.error('Error connecting to MongoDB', error);
28 | throw error;
29 | }
30 | };
31 |
32 | export default dbConnect;
33 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@emotion/react": "^11.10.0",
13 | "@emotion/styled": "^11.10.0",
14 | "@fontsource/roboto": "^4.5.8",
15 | "@mui/icons-material": "^5.8.4",
16 | "@mui/material": "^5.10.1",
17 | "axios": "^0.27.2",
18 | "leaflet": "^1.9.3",
19 | "lodash": "^4.17.21",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "react-leaflet": "^4.1.0",
23 | "react-router-dom": "^6.3.0",
24 | "socket.io-client": "^4.5.1"
25 | },
26 | "devDependencies": {
27 | "@types/leaflet": "^1.9.0",
28 | "@types/lodash": "^4.14.191",
29 | "@types/react": "^18.0.27",
30 | "@types/react-dom": "^18.0.10",
31 | "@types/react-router": "^5.1.20",
32 | "@types/react-router-dom": "^5.3.3",
33 | "@vitejs/plugin-react": "^3.0.1",
34 | "typescript": "^4.9.3",
35 | "vite": "^4.0.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "location-tracker",
3 | "version": "1.0.0",
4 | "description": "location tracker application using MongoDB Change Stream",
5 | "main": "index.js",
6 | "author": "ashiqsultan",
7 | "license": "MIT",
8 | "scripts": {
9 | "test": "echo \"Error: no test specified\" && exit 1",
10 | "dev": "ts-node-dev --respawn src/server.ts",
11 | "build": "tsc",
12 | "start": "node dist/server.js",
13 | "watch-ts": "tsc -w"
14 | },
15 | "devDependencies": {
16 | "@types/bcryptjs": "^2.4.2",
17 | "@types/cors": "^2.8.12",
18 | "@types/express": "^4.17.13",
19 | "@types/jsonwebtoken": "^8.5.8",
20 | "@types/morgan": "^1.9.3",
21 | "@types/node": "^18.11.9",
22 | "ts-node-dev": "^2.0.0",
23 | "typescript": "^4.8.3"
24 | },
25 | "dependencies": {
26 | "@types/geojson": "^7946.0.10",
27 | "bcryptjs": "^2.4.3",
28 | "cors": "^2.8.5",
29 | "dotenv": "^16.0.1",
30 | "express": "^4.18.1",
31 | "jsonwebtoken": "^8.5.1",
32 | "mongodb": "4.13",
33 | "morgan": "^1.10.0",
34 | "socket.io": "^4.5.4"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/controller/user.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from 'express';
2 | import AppResponse from '../types/AppResponse';
3 | import createOne from '../services/users/createOne';
4 |
5 | // Express route handler for creating new user
6 | // The json body should contain IUser interface
7 | export const createUser = async (
8 | req: Request,
9 | res: Response,
10 | next: NextFunction
11 | ) => {
12 | try {
13 | // validate the request body based on IUser interface
14 | const email = req.body.email;
15 | const password = req.body.password;
16 | const name = req.body.name;
17 | if (!email || !password || !name) {
18 | const status = 400;
19 | const message =
20 | 'Invalid request body. Email, password and name are required.';
21 | const errorResponse: AppResponse = {
22 | data: [],
23 | isError: true,
24 | errMsg: message,
25 | };
26 | res.status(status).send(errorResponse);
27 | return;
28 | }
29 | const result = await createOne(req.body);
30 | res.status(201).json(result);
31 | } catch (error) {
32 | next(error);
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/frontend/src/assets/truck.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
--------------------------------------------------------------------------------
/frontend/src/assets/icon_delivery_associate.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
43 |
--------------------------------------------------------------------------------
/src/seed.ts:
--------------------------------------------------------------------------------
1 | import dbClient from './dbClient';
2 | import createAllIndexes from './createIndex';
3 | import IUser from 'IUser';
4 | import IDeliveryAssociate, {
5 | DeliveryAssociateStatus,
6 | } from './types/IDeliveryAssociate';
7 | import createOneUser from './services/users/createOne';
8 | import createOneDA from './services/deliveryAssociates/createOne';
9 | import findUserByEmail from './services/users/findByEmail';
10 | import findDAByEmail from './services/deliveryAssociates/findByEmail';
11 |
12 | const daJohn: IDeliveryAssociate = {
13 | name: 'John',
14 | email: 'john@example.com',
15 | status: DeliveryAssociateStatus.available,
16 | currentLocation: { coordinates: [0, 0], type: 'Point' },
17 | };
18 |
19 | const userAdam: IUser = {
20 | name: 'Adam',
21 | email: 'adam@example.com',
22 | password: 'password123',
23 | };
24 |
25 | const main = async () => {
26 | await dbClient();
27 | await createAllIndexes(); // Checking indexes on seeders might be required if seeders are executed separately.
28 | const adam = await findUserByEmail(userAdam.email);
29 | if (!adam) {
30 | await createOneUser(userAdam);
31 | }
32 | const john = await findDAByEmail(daJohn.email);
33 | if (!john) {
34 | await createOneDA(daJohn);
35 | }
36 | };
37 |
38 | export default main;
39 |
--------------------------------------------------------------------------------
/frontend/src/components/ShipmentRequest.tsx:
--------------------------------------------------------------------------------
1 | import Button from '@mui/material/Button';
2 | import Typography from '@mui/material/Typography';
3 | import Card from '@mui/material/Card';
4 | import CardContent from '@mui/material/CardContent';
5 |
6 | const style = {
7 | display: 'flex',
8 | justifyContent: 'space-evenly',
9 | };
10 |
11 | type Props = {
12 | onAccept: any;
13 | onReject: any;
14 | };
15 | const ShipmentRequest = (props: Props) => {
16 | return (
17 |
18 |
19 |
20 |
21 | New Shipment Available
22 |
23 |
24 |
32 |
40 |
41 |
42 |
43 |
44 | );
45 | };
46 | export default ShipmentRequest;
47 |
--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | import app from './app';
3 | import http from 'http';
4 | import { Server } from 'socket.io';
5 | import socketHandler from './socketHandler';
6 | import dbClient from './dbClient';
7 | import createAllIndexes from './createIndex';
8 | import seed from './seed';
9 | import deliveryAssociateWatchers from './watchers/deliveryAssociates';
10 | import shipmentWatchers from './watchers/shipment';
11 |
12 | const server = http.createServer(app);
13 | const io = new Server(server, {
14 | cors: {
15 | origin: '*',
16 | methods: ['GET', 'POST'],
17 | // allowedHeaders: ['my-custom-header'],
18 | // credentials: true
19 | },
20 | });
21 |
22 | /**
23 | * Start Express server.
24 | */
25 | server.listen(app.get('port'), async () => {
26 | try {
27 | await dbClient();
28 | await createAllIndexes();
29 | await seed();
30 |
31 | socketHandler(io);
32 |
33 | // Initialize MongoDB ChangeStream watchers
34 | await deliveryAssociateWatchers(io);
35 | await shipmentWatchers(io);
36 |
37 | // Server start logs
38 | console.log('node version', process.version);
39 | const GREEN_LINE = '\x1b[32m%s\x1b[0m';
40 | console.log(GREEN_LINE, 'Server started');
41 | console.log(`Port: ${app.get('port')}`);
42 | console.log(`Environment: ${app.get('env')}`);
43 | } catch (error) {
44 | console.error(error);
45 | }
46 | });
47 |
48 | export default server;
49 |
--------------------------------------------------------------------------------
/frontend/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/api.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { Point } from 'geojson';
3 | import { API_URL, USER_EMAIL_DEFAULT } from './constants';
4 | import { ShipmentStatus, ShipmentRes } from './types';
5 |
6 | export const createShipment = async (
7 | pickupLocation: Point,
8 | dropLocation: Point
9 | ): Promise => {
10 | const email = sessionStorage.getItem('userEmail') || USER_EMAIL_DEFAULT;
11 | const data = { pickupLocation, dropLocation, email };
12 | const config = {
13 | method: 'post',
14 | url: `${API_URL}/shipment`,
15 | data: data,
16 | };
17 | const response = await axios(config);
18 | return response.data as any;
19 | };
20 |
21 | export const updateShipmentStatus = async (
22 | id: string,
23 | status: ShipmentStatus
24 | ): Promise => {
25 | const requestBody = { status };
26 | try {
27 | const response = await axios.patch(
28 | `${API_URL}/shipment/${id}/status`,
29 | requestBody
30 | );
31 | return response.data as ShipmentRes;
32 | } catch (error) {
33 | throw error;
34 | }
35 | };
36 |
37 | export const updateShipmentDeliveryAssociate = async (
38 | id: string,
39 | email: string
40 | ): Promise => {
41 | const requestBody = { email };
42 | try {
43 | const response = await axios.patch(
44 | `${API_URL}/shipment/${id}/delivery-associate`,
45 | requestBody
46 | );
47 | return response.data as ShipmentRes;
48 | } catch (error) {
49 | throw error;
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/frontend/src/components/DraggableMarker.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useMemo, useEffect } from 'react';
2 | import { Marker, Popup } from 'react-leaflet';
3 | import { LatLng } from 'leaflet';
4 | import isEqual from 'lodash/isEqual';
5 |
6 | type PropTypes = {
7 | markerIcon: L.Icon;
8 | isDraggable: boolean;
9 | position: LatLng;
10 | setPosition?: Function;
11 | markerName: string;
12 | popupMsg?: string;
13 | };
14 | function DraggableMarker(props: PropTypes) {
15 | const markerRef = useRef(null);
16 |
17 | useEffect(() => {
18 | const marker = markerRef.current;
19 | // @ts-ignore
20 | if (marker != null) marker.openPopup();
21 | }, []);
22 |
23 | const eventHandlers = useMemo(
24 | () => ({
25 | dragend() {
26 | const marker = markerRef.current;
27 | if (marker != null) {
28 | // @ts-ignore
29 | props.setPosition(marker.getLatLng());
30 | }
31 | },
32 | }),
33 | []
34 | );
35 | return (
36 |
43 | {props.popupMsg && {props.popupMsg}}
44 |
45 | );
46 | }
47 | export default React.memo(DraggableMarker, (prev, next) => {
48 | if (
49 | prev.markerName === 'Driver-marker' ||
50 | next.markerName === 'Driver-marker'
51 | ) {
52 | return false;
53 | }
54 | return (
55 | isEqual(prev.position, next.position) ||
56 | prev.isDraggable === next.isDraggable
57 | );
58 | });
59 |
--------------------------------------------------------------------------------
/frontend/src/components/UserInfo.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import Card from '@mui/material/Card';
3 | import Box from '@mui/material/Box';
4 | import Avatar from '@mui/material/Avatar';
5 | import Typography from '@mui/material/Typography';
6 | import Stack from '@mui/material/Stack';
7 | import Divider from '@mui/material/Divider';
8 |
9 | const initialUserData = {
10 | email: '',
11 | name: '',
12 | };
13 |
14 | const UserInfo = () => {
15 | const [userData, setUserData] = useState(initialUserData);
16 |
17 | useEffect(() => {
18 | const email = sessionStorage.getItem('userEmail') || '';
19 | const name = email.substring(0, email.indexOf('@'));
20 | const nameCaseCorrected = name.charAt(0).toUpperCase() + name.slice(1);
21 | setUserData({ email, name: nameCaseCorrected });
22 | }, []);
23 |
24 | return (
25 |
26 |
27 |
32 |
33 | {userData.name}
34 | {userData.email}
35 |
36 |
37 |
38 |
39 |
45 |
46 | );
47 | };
48 | export default UserInfo;
49 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import morgan from 'morgan';
3 | import config from './config';
4 | import AppResponse from './types/AppResponse';
5 | import routes from './routes';
6 | import cors from 'cors';
7 |
8 | // Create Express server
9 | const app = express();
10 |
11 | // Set PORT
12 | app.set('port', config.port);
13 |
14 | // parse json request body
15 | app.use(express.json());
16 | // parse urlencoded request body
17 | app.use(express.urlencoded({ extended: true }));
18 |
19 | // Logger
20 | app.use(morgan('dev'));
21 |
22 | // Enable CORS
23 | app.use(cors(config.corsOptions));
24 |
25 | // Route Handlers
26 | app.use('/', routes);
27 |
28 | // 404 Handler
29 | app.use(function (req, res, next) {
30 | const status = 404;
31 | const message = 'Resource not found';
32 | const errorResponse: AppResponse = {
33 | data: [],
34 | isError: true,
35 | errMsg: message,
36 | };
37 | res.status(status).send(errorResponse);
38 | });
39 |
40 | // Server Error 500 Handler
41 | // Calling next(error) in any of the routes will call this function
42 | app.use(
43 | (
44 | error: Error,
45 | req: express.Request,
46 | res: express.Response,
47 | next: express.NextFunction
48 | ) => {
49 | // Incase of 500 Server Error
50 | // The Error is only logged in server and not sent in response to restrict error details being known in the frontend
51 | console.error(error);
52 | const status = 500;
53 | const message =
54 | process.env.NODE_ENV === 'development'
55 | ? error.message
56 | : 'API Server Error';
57 | const errorResponse: AppResponse = {
58 | data: [],
59 | isError: true,
60 | errMsg: message,
61 | };
62 | res.status(status).send(errorResponse);
63 | }
64 | );
65 |
66 | export default app;
67 |
--------------------------------------------------------------------------------
/src/socketHandler.ts:
--------------------------------------------------------------------------------
1 | import { Server } from 'socket.io';
2 | import { Point } from 'geojson';
3 | import { socketEvents } from './constants';
4 | import updateLocation from './services/deliveryAssociates/updateLocation';
5 |
6 | interface IUpdateDALocation {
7 | email: string;
8 | location: Point;
9 | }
10 | interface ISubscribeToShipment {
11 | shipmentId: string;
12 | }
13 | interface ISubscribeToDA {
14 | deliveryAssociateId: string;
15 | }
16 | interface ILeaveRoom {
17 | roomId: string;
18 | }
19 |
20 | const socketHandler = (io: Server) => {
21 | io.on('connection', (socket: any) => {
22 | console.log('A user connected');
23 | socket.on('disconnect', () => {
24 | console.log('A user disconnected');
25 | });
26 |
27 | // UPDATE_DA_LOCATION : Sent by delivery associates when driving
28 | socket.on(
29 | socketEvents.UPDATE_DA_LOCATION,
30 | async (data: IUpdateDALocation) => {
31 | const { email, location } = data;
32 | await updateLocation(email, location);
33 | }
34 | );
35 |
36 | /**
37 | * Socket rooms are based on shipmentIds or deliveryAssociateIds
38 | * To listen to change streams user needs to subscribe to a shipmentId or deliveryAssociateId
39 | */
40 | // SUBSCRIBE_TO_SHIPMENT
41 | socket.on(
42 | socketEvents.SUBSCRIBE_TO_SHIPMENT,
43 | (data: ISubscribeToShipment) => {
44 | socket.join(data.shipmentId);
45 | }
46 | );
47 | // SUBSCRIBE_TO_DA
48 | socket.on(socketEvents.SUBSCRIBE_TO_DA, (data: ISubscribeToDA) => {
49 | socket.join(data.deliveryAssociateId);
50 | });
51 |
52 | // LEAVE_ROOM
53 | socket.on(socketEvents.LEAVE_ROOM, (data: ILeaveRoom) => {
54 | socket.leave(data.roomId);
55 | });
56 | });
57 | };
58 | export default socketHandler;
59 |
--------------------------------------------------------------------------------
/frontend/src/assets/icon_drop.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/Pages/Home.tsx:
--------------------------------------------------------------------------------
1 | import { useNavigate } from 'react-router-dom';
2 | import Card from '@mui/material/Card';
3 | import CardContent from '@mui/material/CardContent';
4 | import Typography from '@mui/material/Typography';
5 | import { CardActionArea } from '@mui/material';
6 | import Stack from '@mui/material/Stack';
7 | import PersonIcon from '@mui/icons-material/Person';
8 | import LocalShippingIcon from '@mui/icons-material/LocalShipping';
9 |
10 | export default function Home() {
11 | const navigate = useNavigate();
12 |
13 | function CardButton({
14 | pageName,
15 | title,
16 | Icon,
17 | }: {
18 | pageName: string;
19 | title: string;
20 | Icon: any;
21 | }) {
22 | return (
23 |
24 | navigate(`/${pageName}`)}>
25 |
26 |
27 |
28 |
29 | {title}
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
38 | return (
39 | <>
40 |
49 |
50 |
51 | Welcome to Delivery Service
52 |
53 |
54 |
55 |
60 |
61 |
62 |
67 |
68 |
69 | >
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Real-time Delivery Service Using MongoDB Change Streams
2 | The project demonstrates the use of MongoDB Change Streams in a real-time location tracking application. The application is a local package delivery service similar to Uber. Check out the entire article on the [MongoDB Developer Center](https://www.mongodb.com/developer/languages/javascript/real-time-tracking-change-streams-socketio/)
3 |
4 | ## Project Description
5 | The Real-time Delivery Service application allows customers to track their package deliveries in real-time. The application utilizes MongoDB Change Streams to listen for document updates like location and shipment status and uses Socket.io to broadcast them to the connected clients.
6 |
7 | ## How to Run the Application
8 | - Clone the repository to your local machine.
9 | - Create a .env file in the root of the project.
10 | - Add the MONGODB_CONNECTION_STRING
11 | - You can easily generate the connection from MongoDB Atlas UI (incase of local instance check this [link](https://www.mongodb.com/docs/drivers/node/current/fundamentals/connection/connect/))
12 | - Run `npm install` in the root folder to install the required dependencies for the server.
13 | - Run `npm run dev` in the root folder to start the server.
14 | - Open a new terminal and navigate to the `/frontend` folder.
15 | - Run `npm install` to install the required dependencies for the frontend.
16 | - Run `npm run dev` to start the React application.
17 | - The server will be running on http://localhost:5050 and the frontend will be running on http://localhost:5173/
18 |
19 | ## How to Test the Application
20 | - Make sure you have the application running on your local machine as described in the "How to Run the Application" section above.
21 | - For testing purposes, a user and a driver account have been seeded in the database.
22 | - Open the frontend of the application in one browser window and choose to login as a user.
23 | - In another separate browser window, open the frontend and choose to login as a driver.
24 | - In the user window, create a new shipment and choose the pickup and delivery locations.
25 | - The driver should receive a notification of the new shipment and be able to accept it.
26 | - You can then track the shipment in real-time using the location updates provided by MongoDB Change Streams and broadcasted via Socket.io.
27 |
--------------------------------------------------------------------------------
/frontend/src/types.ts:
--------------------------------------------------------------------------------
1 | import { Point } from 'geojson';
2 | import { LatLng } from 'leaflet';
3 |
4 | export interface AppRes {
5 | data: any;
6 | isError: boolean;
7 | errMsg?: string;
8 | }
9 |
10 | export enum ShipmentStatus {
11 | requested = 'requested',
12 | deliveryAssociateAssigned = 'deliveryAssociateAssigned',
13 | pickupLocationReached = 'pickupLocationReached',
14 | transporting = 'transporting',
15 | dropLocationReached = 'dropLocationReached',
16 | delivered = 'delivered',
17 | cancelled = 'cancelled',
18 | }
19 | export interface IShipment {
20 | _id: string;
21 | pickupLocation: Point;
22 | dropLocation: Point;
23 | userId: string;
24 | status: ShipmentStatus;
25 | deliveryAssociateId?: string;
26 | }
27 | export interface ShipmentRes extends AppRes {
28 | data: IShipment;
29 | }
30 |
31 | export interface IUser {
32 | _id: string;
33 | email: string;
34 | name: string;
35 | organization: string;
36 | roles: Array;
37 | }
38 |
39 | export interface UserRes extends AppRes {
40 | data: IUser;
41 | }
42 |
43 | export interface TokenRes extends AppRes {
44 | data: { token: string };
45 | }
46 |
47 | export enum DashboardStatus {
48 | NO_SHIPMENT = 'NO_SHIPMENT',
49 | SHIPMENT_INITIATED = 'SHIPMENT_INITIATED',
50 | PICKUP_SELECTED = 'PICKUP_SELECTED',
51 | DROP_SELECTED = 'DROP_SELECTED',
52 | SEARCHING_ASSOCIATES = 'SEARCHING_ASSOCIATES',
53 | ASSOCIATE_ASSIGNED = 'ASSOCIATE_ASSIGNED',
54 | PICKUP_LOCATION_REACHED = 'PICKUP_LOCATION_REACHED',
55 | TRANSPORTING = 'TRANSPORTING',
56 | DROP_LOCATION_REACHED = 'DROP_LOCATION_REACHED',
57 | DELIVERED = 'DELIVERED',
58 | CANCELLED = 'CANCELLED',
59 | }
60 | export interface IInfo {
61 | title: string;
62 | msg: string;
63 | }
64 | export interface State {
65 | pickupLocation: LatLng;
66 | isPickupDraggable: boolean;
67 | isShowPickupMarker: boolean;
68 | dropLocation: LatLng;
69 | isDropDraggable: boolean;
70 | isShowDropMarker: boolean;
71 | driverLocation: LatLng | null;
72 | dashboardStatus: DashboardStatus;
73 | }
74 | export interface IAction {
75 | type: string;
76 | payload?: any;
77 | }
78 |
79 | export enum DeliveryAssociateStatus {
80 | available = 'available',
81 | delivering = 'delivering',
82 | off = 'off',
83 | }
84 | export interface IDeliveryAssociate {
85 | _id: string;
86 | email: string;
87 | name: string;
88 | status: DeliveryAssociateStatus;
89 | currentLocation: Point;
90 | }
91 | export interface IUpdateDALocation {
92 | email: string;
93 | location: Point;
94 | }
95 |
--------------------------------------------------------------------------------
/src/controller/shipment.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from 'express';
2 | import AppRequest from 'AppRequest';
3 | import IShipment, { ShipmentStatus } from '../types/IShipment';
4 | import AppResponse from '../types/AppResponse';
5 | import createOne from '../services/shipments/createOne';
6 | import updateDA from '../services/shipments/updateDA';
7 | import updateStatus from '../services/shipments/updateStatus';
8 | import findUserByEmail from '../services/users/findByEmail';
9 | import findDAByEmail from '../services/deliveryAssociates/findByEmail';
10 |
11 | export const createShipment = async (
12 | req: AppRequest,
13 | res: Response,
14 | next: NextFunction
15 | ) => {
16 | try {
17 | const userEmail = req.body.email;
18 | const userData = await findUserByEmail(userEmail);
19 | const userId = userData._id.toString();
20 | const newShipment: IShipment = {
21 | pickupLocation: req.body.pickupLocation,
22 | dropLocation: req.body.dropLocation,
23 | status: ShipmentStatus.requested,
24 | userId,
25 | };
26 | const createdShipment = await createOne(newShipment);
27 | console.log('createdShipment');
28 | console.log(createdShipment);
29 | const response: AppResponse = {
30 | data: createdShipment,
31 | isError: false,
32 | };
33 | res.send(response);
34 | } catch (error) {
35 | next(error);
36 | }
37 | };
38 |
39 | export const patchDeliveryAssociate = async (
40 | req: AppRequest,
41 | res: Response,
42 | next: NextFunction
43 | ) => {
44 | try {
45 | const shipmentId = req.params.id;
46 | const daEmail = req.body.email;
47 | const daData = await findDAByEmail(daEmail);
48 | const deliveryAssociateId = daData._id.toString();
49 | const shipmentWithDeliveryAssociate = await updateDA(
50 | shipmentId,
51 | deliveryAssociateId
52 | );
53 | console.log('shipmentWithDeliveryAssociate');
54 | console.log(shipmentWithDeliveryAssociate);
55 | const response: AppResponse = {
56 | data: shipmentWithDeliveryAssociate,
57 | isError: false,
58 | };
59 | res.send(response);
60 | } catch (error) {
61 | next(error);
62 | }
63 | };
64 |
65 | export const patchStatus = async (
66 | req: AppRequest,
67 | res: Response,
68 | next: NextFunction
69 | ) => {
70 | try {
71 | const shipmentId = req.params.id;
72 | const status = req.body.status;
73 | const shipment = await updateStatus(shipmentId, status);
74 | const response: AppResponse = {
75 | data: shipment,
76 | isError: false,
77 | };
78 | res.send(response);
79 | } catch (error) {
80 | next(error);
81 | }
82 | };
83 |
--------------------------------------------------------------------------------
/frontend/src/components/DriverDashboard.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import Typography from '@mui/material/Typography';
3 | import Card from '@mui/material/Card';
4 | import CardContent from '@mui/material/CardContent';
5 | import Box from '@mui/material/Box';
6 | import Stack from '@mui/material/Stack';
7 |
8 | import ShipmentRequest from './ShipmentRequest';
9 | import { socketEvents } from '../constants';
10 | import { IDeliveryAssociate, IShipment, ShipmentStatus } from '../types';
11 | import { updateShipmentStatus, updateShipmentDeliveryAssociate } from '../api';
12 |
13 | type Props = {
14 | socket: any;
15 | setShipmentData: any;
16 | };
17 |
18 | const DriverDashboard = (props: Props) => {
19 | // @ts-ignore
20 | const [newShipmentRequest, setNewShipmentRequest] = useState({});
21 |
22 | const email = sessionStorage.getItem('driverEmail') || '';
23 | const name = email.substring(0, email.indexOf('@'));
24 | const nameCaseCorrected = name.charAt(0).toUpperCase() + name.slice(1);
25 |
26 | useEffect(() => {
27 | props.socket.on(socketEvents.SHIPMENT_CREATED, (data: any) => {
28 | setNewShipmentRequest(data);
29 | });
30 | }, []);
31 |
32 | const onAccept = async () => {
33 | await updateShipmentStatus(
34 | newShipmentRequest?._id,
35 | ShipmentStatus.deliveryAssociateAssigned
36 | );
37 | const email = sessionStorage.getItem('driverEmail') || '';
38 | const shipmentData = await updateShipmentDeliveryAssociate(
39 | newShipmentRequest?._id,
40 | email
41 | );
42 | props.setShipmentData(shipmentData.data);
43 | // @ts-ignore
44 | setNewShipmentRequest({});
45 | };
46 | const onReject = () => {
47 | // @ts-ignore
48 | setNewShipmentRequest({});
49 | };
50 | return (
51 |
56 |
57 |
58 |
59 | Associate Details
60 |
61 |
66 |
67 |
68 | Email:
69 | {email}
70 |
71 |
72 | Name:
73 | {nameCaseCorrected}
74 |
75 |
76 |
77 |
78 |
79 |
80 | {/* New Shipment Notification */}
81 | {newShipmentRequest._id ? (
82 |
83 | ) : null}
84 |
85 |
86 | );
87 | };
88 |
89 | export default DriverDashboard;
90 |
--------------------------------------------------------------------------------
/frontend/src/assets/icon_pickup.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/frontend/src/constants.ts:
--------------------------------------------------------------------------------
1 | import L from 'leaflet';
2 | import iconPickup from './assets/icon_pickup.svg';
3 | import iconDrop from './assets/icon_drop.svg';
4 | import iconDriver from './assets/truck.svg';
5 | import { IInfo, DashboardStatus } from './types';
6 |
7 | export const DRIVER_EMAIL_DEFAULT = 'john@example.com';
8 | export const USER_EMAIL_DEFAULT = 'adam@example.com';
9 |
10 | export const API_URL = 'http://localhost:5050';
11 |
12 | export const pickupMarkerIcon = L.icon({
13 | iconUrl: iconPickup,
14 | iconSize: [50, 50], // size of the icon
15 | popupAnchor: [-3, -20], // point from which the popup should open relative to the iconAnchor
16 | className: 'marker',
17 | });
18 |
19 | export const dropMarkerIcon = L.icon({
20 | iconUrl: iconDrop,
21 | iconSize: [50, 50], // size of the icon
22 | popupAnchor: [-3, -20], // point from which the popup should open relative to the iconAnchor
23 | className: 'marker',
24 | });
25 |
26 | export const driverMarkerIcon = L.icon({
27 | iconUrl: iconDriver,
28 | iconSize: [35, 35], // size of the icon
29 | popupAnchor: [-3, -20], // point from which the popup should open relative to the iconAnchor
30 | className: 'marker',
31 | });
32 |
33 | export const socketEvents = {
34 | SUBSCRIBE_TO_SHIPMENT: 'SUBSCRIBE_TO_SHIPMENT',
35 | SUBSCRIBE_TO_DA: 'SUBSCRIBE_TO_DA',
36 | DA_LOCATION_CHANGED: 'DA_LOCATION_CHANGED',
37 | LEAVE_ROOM: 'LEAVE_ROOM',
38 | SHIPMENT_UPDATED: 'SHIPMENT_UPDATED',
39 | SHIPMENT_CREATED: 'SHIPMENT_CREATED',
40 | UPDATE_DA_LOCATION: 'UPDATE_DA_LOCATION',
41 | };
42 |
43 | export const infoMsgs: Record = {
44 | SHIPMENT_INITIATED: {
45 | title: 'Select Pickup Location',
46 | msg: 'Move the Pickup marker on the map to choose your pickup location. Click confirm to continue.',
47 | },
48 | PICKUP_SELECTED: {
49 | title: 'Select Delivery Location',
50 | msg: 'Move the Flag marker on the map to choose your Delivery location. Click confirm to continue.',
51 | },
52 | DROP_SELECTED: {
53 | title: 'Searching for delivery Associates',
54 | msg: 'Please wait, we are looking for associates to handle your delivery',
55 | },
56 | NO_SHIPMENT: { title: '', msg: '' },
57 | SEARCHING_ASSOCIATES: { title: '', msg: '' },
58 | ASSOCIATE_ASSIGNED: {
59 | title: 'Delivery Associate Assigned',
60 | msg: 'An Associate has been assigned to handle your delivery and will soon reach your pickup location',
61 | },
62 | PICKUP_LOCATION_REACHED: {
63 | title: 'Associate reached Pickup location',
64 | msg: 'Our Associate has arrived at the pickup location',
65 | },
66 | TRANSPORTING: {
67 | title: 'Transporting',
68 | msg: 'Your package is getting delivered',
69 | },
70 | DROP_LOCATION_REACHED: {
71 | title: 'Reached Delivery location',
72 | msg: 'Associate reached delivery location',
73 | },
74 | DELIVERED: {
75 | title: 'Delivered',
76 | msg: 'The package has been delivered successfully',
77 | },
78 | CANCELLED: { title: '', msg: '' },
79 | };
80 |
81 | export const ACTIONS = {
82 | FIRST_LOAD: 'FIRST_LOAD',
83 | NEW_DELIVERY_CLICKED: 'NEW_DELIVERY_CLICKED',
84 | SET_DRIVER_LOCATION: 'SET_DRIVER_LOCATION',
85 | SET_PICKUP_LOCATION: 'SET_PICKUP_LOCATION',
86 | SET_DROP_LOCATION: 'SET_DROP_LOCATION',
87 | ...DashboardStatus,
88 | };
89 |
90 | export const mapInitialViewProps: {
91 | zoom: number;
92 | center: [number, number];
93 | scrollWheelZoom: boolean;
94 | } = {
95 | zoom: 15,
96 | center: [13.092123232608643, 80.28222309087568],
97 | scrollWheelZoom: true,
98 | };
99 |
--------------------------------------------------------------------------------
/frontend/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/ShipmentDashboard.tsx:
--------------------------------------------------------------------------------
1 | import Button from '@mui/material/Button';
2 | import Typography from '@mui/material/Typography';
3 | import Card from '@mui/material/Card';
4 | import CardContent from '@mui/material/CardContent';
5 | import Box from '@mui/material/Box';
6 | import Stack from '@mui/material/Stack';
7 | import Divider from '@mui/material/Divider';
8 | import { IShipment, ShipmentStatus } from '../types';
9 | import { updateShipmentStatus } from '../api';
10 |
11 | type Props = {
12 | shipmentData: IShipment;
13 | setShipmentData: any;
14 | };
15 | const statusDisplayName: Record = {
16 | [ShipmentStatus.deliveryAssociateAssigned]: 'Delivery Associate Assigned',
17 | [ShipmentStatus.pickupLocationReached]: 'Reached Pick up location',
18 | [ShipmentStatus.dropLocationReached]: 'Reached Drop location',
19 | [ShipmentStatus.transporting]: 'Transporting',
20 | [ShipmentStatus.delivered]: 'Delivered',
21 | [ShipmentStatus.requested]: 'Requested',
22 | [ShipmentStatus.cancelled]: 'Cancelled',
23 | };
24 |
25 | type UpdateAction = {
26 | actionName: string;
27 | statusToUpdate: ShipmentStatus;
28 | };
29 |
30 | const ShipmentDashboard = (props: Props) => {
31 | const { shipmentData, setShipmentData } = props;
32 | // Function to determine the next status action based on current status
33 | const updateAction = (): UpdateAction => {
34 | const currentStatus: ShipmentStatus = shipmentData.status;
35 | const pickupLocationReached = {
36 | actionName: statusDisplayName[ShipmentStatus.pickupLocationReached],
37 | statusToUpdate: ShipmentStatus.pickupLocationReached,
38 | };
39 | const transporting = {
40 | actionName: statusDisplayName[ShipmentStatus.transporting],
41 | statusToUpdate: ShipmentStatus.transporting,
42 | };
43 | const dropLocationReached = {
44 | actionName: statusDisplayName[ShipmentStatus.dropLocationReached],
45 | statusToUpdate: ShipmentStatus.dropLocationReached,
46 | };
47 | const delivered = {
48 | actionName: statusDisplayName[ShipmentStatus.delivered],
49 | statusToUpdate: ShipmentStatus.delivered,
50 | };
51 | let returnObj: UpdateAction;
52 | switch (currentStatus) {
53 | case ShipmentStatus.deliveryAssociateAssigned:
54 | returnObj = pickupLocationReached;
55 | break;
56 | case ShipmentStatus.pickupLocationReached:
57 | returnObj = transporting;
58 | break;
59 | case ShipmentStatus.transporting:
60 | returnObj = dropLocationReached;
61 | break;
62 | case ShipmentStatus.dropLocationReached:
63 | returnObj = delivered;
64 | break;
65 | default:
66 | returnObj = delivered;
67 | break;
68 | }
69 | return returnObj;
70 | };
71 |
72 | const onShipmentStatusUpdate = async (statusToUpdate: ShipmentStatus) => {
73 | const updatedShipmentData = await updateShipmentStatus(
74 | shipmentData._id,
75 | statusToUpdate
76 | );
77 | setShipmentData(updatedShipmentData.data);
78 | };
79 | return (
80 | <>
81 | {shipmentData._id ? (
82 |
83 |
88 |
89 |
90 |
91 | Shipment details
92 |
93 |
98 |
99 |
100 | Id: {shipmentData?._id}
101 |
102 |
103 |
104 | Status: {statusDisplayName[shipmentData.status]}
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
120 |
129 |
130 |
131 | ) : null}
132 | >
133 | );
134 | };
135 | export default ShipmentDashboard;
136 |
--------------------------------------------------------------------------------
/frontend/src/Pages/SimulatorDashboard.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useRef, useMemo } from 'react';
2 | import io from 'socket.io-client';
3 | import { useParams } from 'react-router-dom';
4 | import L, { LatLng } from 'leaflet';
5 | import 'leaflet/dist/leaflet.css';
6 | import throttle from 'lodash/throttle';
7 | import {
8 | MapContainer,
9 | TileLayer,
10 | Marker,
11 | Popup,
12 | useMapEvents,
13 | } from 'react-leaflet';
14 |
15 | import { DRIVER_EMAIL_DEFAULT, API_URL, socketEvents } from '../constants';
16 |
17 | import {
18 | IShipment,
19 | ShipmentStatus,
20 | IDeliveryAssociate,
21 | IUpdateDALocation,
22 | } from '../types';
23 |
24 | import iconDeliveryAssociate from '../assets/icon_delivery_associate.svg';
25 | import iconPickup from '../assets/icon_pickup.svg';
26 | import iconDrop from '../assets/icon_drop.svg';
27 |
28 | import DriverDashboard from '../components/DriverDashboard';
29 | import ShipmentDashboard from '../components/ShipmentDashboard';
30 |
31 | import './dashboard.css';
32 |
33 | const initialValues: {
34 | zoom: number;
35 | center: [number, number];
36 | scrollWheelZoom: boolean;
37 | } = {
38 | zoom: 15,
39 | center: [13.092123232608643, 80.28222309087568],
40 | scrollWheelZoom: true,
41 | };
42 | const mapContainerStyle = {
43 | width: '100%',
44 | height: '99vh',
45 | };
46 | const THROTTLE_DELAY = 50;
47 | const socket = io(API_URL);
48 |
49 | const SimulatorDashboard = () => {
50 | const [deliveryAssociate, setDeliveryAssociate] =
51 | // @ts-ignore
52 | useState({});
53 | // @ts-ignore
54 | const [shipmentData, setShipmentData] = useState({});
55 | const [isConnected, setIsConnected] = useState(socket.connected);
56 | const [draggable, setDraggable] = useState(true);
57 | const [position, setPosition] = useState(initialValues.center);
58 |
59 | useEffect(() => {
60 | // Setting the seeded driver email to identify a logged in driver
61 | // Modify this part as per your needs like setting an auth token
62 | sessionStorage.setItem('driverEmail', DRIVER_EMAIL_DEFAULT);
63 |
64 | // Establish Socket
65 | socket.on('connect', () => {
66 | setIsConnected(true);
67 | });
68 |
69 | socket.on('disconnect', () => {
70 | setIsConnected(false);
71 | });
72 |
73 | // on Unmount
74 | return () => {
75 | socket.off('connect');
76 | socket.off('disconnect');
77 | socket.off('pong');
78 | };
79 | }, []);
80 |
81 | const gpsUpdate = (position: any) => {
82 | if (position instanceof LatLng) {
83 | const email = sessionStorage.getItem('driverEmail') || '';
84 | const data: IUpdateDALocation = {
85 | email: email,
86 | location: { type: 'Point', coordinates: [position.lng, position.lat] },
87 | };
88 | socket.emit(socketEvents.UPDATE_DA_LOCATION, data);
89 | }
90 | };
91 |
92 | // position side effects
93 | useEffect(() => {
94 | gpsUpdate(position);
95 | }, [position]);
96 |
97 | const throttledPositionUpdate = throttle(function (position) {
98 | console.log('throttled position', position);
99 | gpsUpdate(position);
100 | }, THROTTLE_DELAY);
101 |
102 | function DraggableMarker() {
103 | const markerIcon = L.icon({
104 | iconUrl: iconDeliveryAssociate,
105 | iconSize: [35, 35], // size of the icon
106 | popupAnchor: [-3, -20], // point from which the popup should open relative to the iconAnchor
107 | className: 'marker',
108 | });
109 | const markerRef = useRef(null);
110 | const eventHandlers = useMemo(
111 | () => ({
112 | dragend() {
113 | const marker = markerRef.current;
114 | if (marker != null) {
115 | // @ts-ignore
116 | setPosition(marker.getLatLng());
117 | }
118 | },
119 | drag() {
120 | const marker = markerRef.current;
121 | if (marker != null) {
122 | // @ts-ignore
123 | throttledPositionUpdate(marker.getLatLng());
124 | }
125 | },
126 | }),
127 | []
128 | );
129 |
130 | return (
131 |
138 | );
139 | }
140 | const PickUpMarker = () => {
141 | const markerIcon = L.icon({
142 | iconUrl: iconPickup,
143 | iconSize: [70, 50], // size of the icon
144 | popupAnchor: [-3, -20], // point from which the popup should open relative to the iconAnchor
145 | });
146 | try {
147 | const shipment = shipmentData as IShipment;
148 | const coordinates = shipment?.pickupLocation?.coordinates;
149 | return Array.isArray(coordinates) ? (
150 |
151 | Pickup location
152 |
153 | ) : null;
154 | } catch (error) {
155 | console.error(error);
156 | return null;
157 | }
158 | };
159 | const DropLocationMarker = () => {
160 | const markerIcon = L.icon({
161 | iconUrl: iconDrop,
162 | iconSize: [50, 40], // size of the icon
163 | popupAnchor: [-3, -20], // point from which the popup should open relative to the iconAnchor
164 | });
165 | try {
166 | const shipment = shipmentData as IShipment;
167 | const coordinates = shipment?.dropLocation?.coordinates;
168 | return Array.isArray(coordinates) ? (
169 |
170 | Delivery location
171 |
172 | ) : null;
173 | } catch (error) {
174 | console.error(error);
175 | return null;
176 | }
177 | };
178 |
179 | return (
180 |
181 |
182 |
183 |
184 | {shipmentData._id ? (
185 |
189 | ) : null}
190 |
191 |
192 |
193 |
194 |
195 |
199 |
200 | {shipmentData._id &&
201 | shipmentData.status !== ShipmentStatus.delivered ? (
202 |
203 | ) : null}
204 | {shipmentData._id &&
205 | shipmentData.status !== ShipmentStatus.delivered ? (
206 |
207 | ) : null}
208 |
209 |
210 |
211 |
212 | );
213 | };
214 | export default SimulatorDashboard;
215 |
--------------------------------------------------------------------------------
/frontend/src/Pages/UserDashboard.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useReducer } from 'react';
2 | import io from 'socket.io-client';
3 | import { LatLng } from 'leaflet';
4 | import { Point } from 'geojson';
5 | import { MapContainer, TileLayer } from 'react-leaflet';
6 | import Button from '@mui/material/Button';
7 | import 'leaflet/dist/leaflet.css';
8 |
9 | import UserInfo from '../components/UserInfo';
10 | import DraggableMarker from '../components/DraggableMarker';
11 | import ShipmentInfo from '../components/ShipmentInfo';
12 | import {
13 | DashboardStatus,
14 | State,
15 | IAction,
16 | IShipment,
17 | ShipmentStatus,
18 | } from '../types';
19 | import {
20 | USER_EMAIL_DEFAULT,
21 | pickupMarkerIcon,
22 | dropMarkerIcon,
23 | driverMarkerIcon,
24 | API_URL,
25 | socketEvents,
26 | mapInitialViewProps,
27 | ACTIONS,
28 | } from '../constants';
29 | import { createShipment } from '../api';
30 | import './dashboard.css';
31 |
32 | const socket = io(API_URL);
33 |
34 | const initialState: State = {
35 | pickupLocation: new LatLng(13.102971824499635, 80.27971744537354),
36 | isPickupDraggable: false,
37 | isShowPickupMarker: false,
38 | dropLocation: new LatLng(13.092123232608643, 80.28222309087568),
39 | isDropDraggable: false,
40 | isShowDropMarker: false,
41 | driverLocation: null,
42 | dashboardStatus: DashboardStatus.NO_SHIPMENT,
43 | };
44 |
45 | function reducer(state: State, action: IAction): State {
46 | switch (action.type) {
47 | case ACTIONS.NEW_DELIVERY_CLICKED:
48 | return {
49 | ...state,
50 | isPickupDraggable: true,
51 | isShowPickupMarker: true,
52 | dashboardStatus: DashboardStatus.SHIPMENT_INITIATED,
53 | };
54 | case ACTIONS.SET_DRIVER_LOCATION:
55 | return {
56 | ...state,
57 | driverLocation: action.payload.position, // position should be Leaflet LatLng
58 | };
59 | case ACTIONS.SET_PICKUP_LOCATION:
60 | return {
61 | ...state,
62 | pickupLocation: action.payload.position, // position should be Leaflet LatLng
63 | };
64 | case ACTIONS.SET_DROP_LOCATION:
65 | return {
66 | ...state,
67 | dropLocation: action.payload.position, // position should be Leaflet LatLng
68 | };
69 | case ACTIONS.PICKUP_SELECTED:
70 | return {
71 | ...state,
72 | isPickupDraggable: false,
73 | isDropDraggable: true,
74 | isShowDropMarker: true,
75 | dashboardStatus: DashboardStatus.PICKUP_SELECTED,
76 | };
77 | case ACTIONS.DROP_SELECTED:
78 | return {
79 | ...state,
80 | isDropDraggable: false,
81 | dashboardStatus: DashboardStatus.DROP_SELECTED,
82 | };
83 | case ACTIONS.ASSOCIATE_ASSIGNED:
84 | return {
85 | ...state,
86 | dashboardStatus: DashboardStatus.ASSOCIATE_ASSIGNED,
87 | };
88 | case ACTIONS.PICKUP_LOCATION_REACHED:
89 | return {
90 | ...state,
91 | dashboardStatus: DashboardStatus.PICKUP_LOCATION_REACHED,
92 | };
93 | case ACTIONS.TRANSPORTING:
94 | return {
95 | ...state,
96 | dashboardStatus: DashboardStatus.TRANSPORTING,
97 | };
98 | case ACTIONS.DROP_LOCATION_REACHED:
99 | return {
100 | ...state,
101 | dashboardStatus: DashboardStatus.DROP_LOCATION_REACHED,
102 | };
103 | case ACTIONS.DELIVERED:
104 | return {
105 | ...state,
106 | dashboardStatus: DashboardStatus.DELIVERED,
107 | };
108 | default:
109 | console.log('default action');
110 | return state;
111 | }
112 | }
113 |
114 | const shipmentStatusActionMapper: Record = {
115 | requested: { type: 'Default' },
116 | deliveryAssociateAssigned: { type: ACTIONS.ASSOCIATE_ASSIGNED },
117 | pickupLocationReached: { type: ACTIONS.PICKUP_LOCATION_REACHED },
118 | transporting: { type: ACTIONS.TRANSPORTING },
119 | dropLocationReached: { type: ACTIONS.DROP_LOCATION_REACHED },
120 | delivered: { type: ACTIONS.DELIVERED },
121 | cancelled: { type: ACTIONS.CANCELLED },
122 | };
123 |
124 | const UserDashboard = () => {
125 | const [isConnected, setIsConnected] = useState(socket.connected);
126 | const [state, dispatch] = useReducer(reducer, initialState);
127 |
128 | useEffect(() => {
129 | // Setting seeded user's email as logged in user
130 | // You can change this to update logged in user email or id
131 | sessionStorage.setItem('userEmail', USER_EMAIL_DEFAULT);
132 |
133 | // Establish Socket
134 | socket.on('connect', () => {
135 | setIsConnected(true);
136 | });
137 |
138 | socket.on('disconnect', () => {
139 | setIsConnected(false);
140 | });
141 | socket.on(socketEvents.DA_LOCATION_CHANGED, (data) => {
142 | try {
143 | const coorArr = data?.currentLocation?.coordinates;
144 | const isNumberType = (value: any) => typeof value === 'number';
145 | if (
146 | Array.isArray(coorArr) &&
147 | coorArr.length === 2 &&
148 | isNumberType(coorArr[0]) &&
149 | isNumberType(coorArr[1])
150 | ) {
151 | const lat = coorArr[1];
152 | const lng = coorArr[0];
153 | const newLocation = new LatLng(lat, lng);
154 | const action = {
155 | type: ACTIONS.SET_DRIVER_LOCATION,
156 | payload: { position: newLocation },
157 | };
158 | dispatch(action);
159 | }
160 | } catch (error) {
161 | console.error(error);
162 | }
163 | });
164 |
165 | // Listens to Shipment updates once subscribed
166 | socket.on(socketEvents.SHIPMENT_UPDATED, (data: IShipment) => {
167 | try {
168 | console.log({ data });
169 | // Subscribe to delivery associate
170 | if (data.deliveryAssociateId) {
171 | socket.emit(socketEvents.SUBSCRIBE_TO_DA, {
172 | deliveryAssociateId: data.deliveryAssociateId,
173 | });
174 | }
175 | // Dispatch Action on Shipment status change
176 | if (data.status) {
177 | dispatch(shipmentStatusActionMapper[data.status]);
178 | }
179 | } catch (error) {
180 | console.error(error);
181 | }
182 | });
183 |
184 | // on Unmount
185 | return () => {
186 | socket.off('connect');
187 | socket.off('disconnect');
188 | socket.off('pong');
189 | };
190 | }, []);
191 |
192 | const setPickupLocation = (position: LatLng) => {
193 | const action = {
194 | type: ACTIONS.SET_PICKUP_LOCATION,
195 | payload: { position },
196 | };
197 | dispatch(action);
198 | };
199 | const setDropLocation = (position: LatLng) => {
200 | const action = {
201 | type: ACTIONS.SET_DROP_LOCATION,
202 | payload: { position },
203 | };
204 | dispatch(action);
205 | };
206 | const onNewDeliveryClick = () => {
207 | const action = {
208 | type: ACTIONS.NEW_DELIVERY_CLICKED,
209 | payload: {},
210 | };
211 | dispatch(action);
212 | };
213 | const onPickupSelected = () => {
214 | const action = { type: ACTIONS.PICKUP_SELECTED, payload: {} };
215 | dispatch(action);
216 | };
217 | const onDropSelected = async () => {
218 | try {
219 | const action = { type: ACTIONS.DROP_SELECTED, payload: {} };
220 | await dispatch(action);
221 | const pickupPoint: Point = {
222 | type: 'Point',
223 | coordinates: [state.pickupLocation.lng, state.pickupLocation.lat],
224 | };
225 | const dropPoint: Point = {
226 | type: 'Point',
227 | coordinates: [state.dropLocation.lng, state.dropLocation.lat],
228 | };
229 | // Call API to Create new Shipment
230 | const createShipmentOp = await createShipment(pickupPoint, dropPoint);
231 | const shipment = createShipmentOp.data;
232 | // Subscribe to MongoDB Change Stream via Socket io for the created Shipment
233 | socket.emit(socketEvents.SUBSCRIBE_TO_SHIPMENT, {
234 | shipmentId: shipment._id,
235 | });
236 | } catch (error) {
237 | console.error(error);
238 | }
239 | };
240 |
241 | const ButtonNewDelivery = () => {
242 | return (
243 |
251 | );
252 | };
253 | const ButtonConfirmPickUp = () => {
254 | return (
255 |
263 | );
264 | };
265 | const ButtonConfirmDrop = () => {
266 | return (
267 |
275 | );
276 | };
277 |
278 | return (
279 |
280 |
281 |
282 | {/* Shipment info */}
283 |
284 |
285 |
286 | {/* Action button */}
287 |
288 | {state.dashboardStatus === DashboardStatus.NO_SHIPMENT && (
289 |
290 | )}
291 | {state.dashboardStatus === DashboardStatus.SHIPMENT_INITIATED && (
292 |
293 | )}
294 | {state.dashboardStatus === DashboardStatus.PICKUP_SELECTED && (
295 |
296 | )}
297 |
298 |
299 |
300 |
304 |
308 | {/* Pickup Marker */}
309 | {state.isShowPickupMarker ? (
310 |
319 | ) : null}
320 | {/* Drop Location Marker */}
321 | {state.isShowDropMarker ? (
322 |
331 | ) : null}
332 | {/* Driver Location Marker */}
333 | {state.driverLocation && (
334 |
341 | )}
342 |
343 |
344 |
345 | );
346 | };
347 | export default UserDashboard;
348 |
--------------------------------------------------------------------------------
/frontend/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@ampproject/remapping@^2.1.0":
6 | version "2.2.0"
7 | resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
8 | integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
9 | dependencies:
10 | "@jridgewell/gen-mapping" "^0.1.0"
11 | "@jridgewell/trace-mapping" "^0.3.9"
12 |
13 | "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6":
14 | version "7.18.6"
15 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
16 | integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
17 | dependencies:
18 | "@babel/highlight" "^7.18.6"
19 |
20 | "@babel/compat-data@^7.20.5":
21 | version "7.20.10"
22 | resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec"
23 | integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
24 |
25 | "@babel/core@^7.20.7":
26 | version "7.20.12"
27 | resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d"
28 | integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==
29 | dependencies:
30 | "@ampproject/remapping" "^2.1.0"
31 | "@babel/code-frame" "^7.18.6"
32 | "@babel/generator" "^7.20.7"
33 | "@babel/helper-compilation-targets" "^7.20.7"
34 | "@babel/helper-module-transforms" "^7.20.11"
35 | "@babel/helpers" "^7.20.7"
36 | "@babel/parser" "^7.20.7"
37 | "@babel/template" "^7.20.7"
38 | "@babel/traverse" "^7.20.12"
39 | "@babel/types" "^7.20.7"
40 | convert-source-map "^1.7.0"
41 | debug "^4.1.0"
42 | gensync "^1.0.0-beta.2"
43 | json5 "^2.2.2"
44 | semver "^6.3.0"
45 |
46 | "@babel/generator@^7.20.7":
47 | version "7.20.7"
48 | resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a"
49 | integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==
50 | dependencies:
51 | "@babel/types" "^7.20.7"
52 | "@jridgewell/gen-mapping" "^0.3.2"
53 | jsesc "^2.5.1"
54 |
55 | "@babel/helper-compilation-targets@^7.20.7":
56 | version "7.20.7"
57 | resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb"
58 | integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==
59 | dependencies:
60 | "@babel/compat-data" "^7.20.5"
61 | "@babel/helper-validator-option" "^7.18.6"
62 | browserslist "^4.21.3"
63 | lru-cache "^5.1.1"
64 | semver "^6.3.0"
65 |
66 | "@babel/helper-environment-visitor@^7.18.9":
67 | version "7.18.9"
68 | resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
69 | integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
70 |
71 | "@babel/helper-function-name@^7.19.0":
72 | version "7.19.0"
73 | resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
74 | integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==
75 | dependencies:
76 | "@babel/template" "^7.18.10"
77 | "@babel/types" "^7.19.0"
78 |
79 | "@babel/helper-hoist-variables@^7.18.6":
80 | version "7.18.6"
81 | resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
82 | integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
83 | dependencies:
84 | "@babel/types" "^7.18.6"
85 |
86 | "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6":
87 | version "7.18.6"
88 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
89 | integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
90 | dependencies:
91 | "@babel/types" "^7.18.6"
92 |
93 | "@babel/helper-module-transforms@^7.20.11":
94 | version "7.20.11"
95 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0"
96 | integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==
97 | dependencies:
98 | "@babel/helper-environment-visitor" "^7.18.9"
99 | "@babel/helper-module-imports" "^7.18.6"
100 | "@babel/helper-simple-access" "^7.20.2"
101 | "@babel/helper-split-export-declaration" "^7.18.6"
102 | "@babel/helper-validator-identifier" "^7.19.1"
103 | "@babel/template" "^7.20.7"
104 | "@babel/traverse" "^7.20.10"
105 | "@babel/types" "^7.20.7"
106 |
107 | "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.19.0":
108 | version "7.20.2"
109 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629"
110 | integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==
111 |
112 | "@babel/helper-simple-access@^7.20.2":
113 | version "7.20.2"
114 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9"
115 | integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==
116 | dependencies:
117 | "@babel/types" "^7.20.2"
118 |
119 | "@babel/helper-split-export-declaration@^7.18.6":
120 | version "7.18.6"
121 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
122 | integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
123 | dependencies:
124 | "@babel/types" "^7.18.6"
125 |
126 | "@babel/helper-string-parser@^7.19.4":
127 | version "7.19.4"
128 | resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
129 | integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
130 |
131 | "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
132 | version "7.19.1"
133 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
134 | integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
135 |
136 | "@babel/helper-validator-option@^7.18.6":
137 | version "7.18.6"
138 | resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
139 | integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
140 |
141 | "@babel/helpers@^7.20.7":
142 | version "7.20.7"
143 | resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce"
144 | integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==
145 | dependencies:
146 | "@babel/template" "^7.20.7"
147 | "@babel/traverse" "^7.20.7"
148 | "@babel/types" "^7.20.7"
149 |
150 | "@babel/highlight@^7.18.6":
151 | version "7.18.6"
152 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
153 | integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
154 | dependencies:
155 | "@babel/helper-validator-identifier" "^7.18.6"
156 | chalk "^2.0.0"
157 | js-tokens "^4.0.0"
158 |
159 | "@babel/parser@^7.20.7":
160 | version "7.20.7"
161 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b"
162 | integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
163 |
164 | "@babel/plugin-syntax-jsx@^7.17.12":
165 | version "7.18.6"
166 | resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
167 | integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==
168 | dependencies:
169 | "@babel/helper-plugin-utils" "^7.18.6"
170 |
171 | "@babel/plugin-transform-react-jsx-self@^7.18.6":
172 | version "7.18.6"
173 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7"
174 | integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==
175 | dependencies:
176 | "@babel/helper-plugin-utils" "^7.18.6"
177 |
178 | "@babel/plugin-transform-react-jsx-source@^7.19.6":
179 | version "7.19.6"
180 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.19.6.tgz#88578ae8331e5887e8ce28e4c9dc83fb29da0b86"
181 | integrity sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==
182 | dependencies:
183 | "@babel/helper-plugin-utils" "^7.19.0"
184 |
185 | "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.6", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
186 | version "7.20.7"
187 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd"
188 | integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==
189 | dependencies:
190 | regenerator-runtime "^0.13.11"
191 |
192 | "@babel/template@^7.18.10", "@babel/template@^7.20.7":
193 | version "7.20.7"
194 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
195 | integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==
196 | dependencies:
197 | "@babel/code-frame" "^7.18.6"
198 | "@babel/parser" "^7.20.7"
199 | "@babel/types" "^7.20.7"
200 |
201 | "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7":
202 | version "7.20.12"
203 | resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5"
204 | integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==
205 | dependencies:
206 | "@babel/code-frame" "^7.18.6"
207 | "@babel/generator" "^7.20.7"
208 | "@babel/helper-environment-visitor" "^7.18.9"
209 | "@babel/helper-function-name" "^7.19.0"
210 | "@babel/helper-hoist-variables" "^7.18.6"
211 | "@babel/helper-split-export-declaration" "^7.18.6"
212 | "@babel/parser" "^7.20.7"
213 | "@babel/types" "^7.20.7"
214 | debug "^4.1.0"
215 | globals "^11.1.0"
216 |
217 | "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7":
218 | version "7.20.7"
219 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
220 | integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==
221 | dependencies:
222 | "@babel/helper-string-parser" "^7.19.4"
223 | "@babel/helper-validator-identifier" "^7.19.1"
224 | to-fast-properties "^2.0.0"
225 |
226 | "@emotion/babel-plugin@^11.10.5":
227 | version "11.10.5"
228 | resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz#65fa6e1790ddc9e23cc22658a4c5dea423c55c3c"
229 | integrity sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==
230 | dependencies:
231 | "@babel/helper-module-imports" "^7.16.7"
232 | "@babel/plugin-syntax-jsx" "^7.17.12"
233 | "@babel/runtime" "^7.18.3"
234 | "@emotion/hash" "^0.9.0"
235 | "@emotion/memoize" "^0.8.0"
236 | "@emotion/serialize" "^1.1.1"
237 | babel-plugin-macros "^3.1.0"
238 | convert-source-map "^1.5.0"
239 | escape-string-regexp "^4.0.0"
240 | find-root "^1.1.0"
241 | source-map "^0.5.7"
242 | stylis "4.1.3"
243 |
244 | "@emotion/cache@^11.10.5":
245 | version "11.10.5"
246 | resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.10.5.tgz#c142da9351f94e47527ed458f7bbbbe40bb13c12"
247 | integrity sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==
248 | dependencies:
249 | "@emotion/memoize" "^0.8.0"
250 | "@emotion/sheet" "^1.2.1"
251 | "@emotion/utils" "^1.2.0"
252 | "@emotion/weak-memoize" "^0.3.0"
253 | stylis "4.1.3"
254 |
255 | "@emotion/hash@^0.9.0":
256 | version "0.9.0"
257 | resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.0.tgz#c5153d50401ee3c027a57a177bc269b16d889cb7"
258 | integrity sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==
259 |
260 | "@emotion/is-prop-valid@^1.2.0":
261 | version "1.2.0"
262 | resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz#7f2d35c97891669f7e276eb71c83376a5dc44c83"
263 | integrity sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==
264 | dependencies:
265 | "@emotion/memoize" "^0.8.0"
266 |
267 | "@emotion/memoize@^0.8.0":
268 | version "0.8.0"
269 | resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
270 | integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
271 |
272 | "@emotion/react@^11.10.0":
273 | version "11.10.5"
274 | resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.5.tgz#95fff612a5de1efa9c0d535384d3cfa115fe175d"
275 | integrity sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==
276 | dependencies:
277 | "@babel/runtime" "^7.18.3"
278 | "@emotion/babel-plugin" "^11.10.5"
279 | "@emotion/cache" "^11.10.5"
280 | "@emotion/serialize" "^1.1.1"
281 | "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
282 | "@emotion/utils" "^1.2.0"
283 | "@emotion/weak-memoize" "^0.3.0"
284 | hoist-non-react-statics "^3.3.1"
285 |
286 | "@emotion/serialize@^1.1.1":
287 | version "1.1.1"
288 | resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0"
289 | integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==
290 | dependencies:
291 | "@emotion/hash" "^0.9.0"
292 | "@emotion/memoize" "^0.8.0"
293 | "@emotion/unitless" "^0.8.0"
294 | "@emotion/utils" "^1.2.0"
295 | csstype "^3.0.2"
296 |
297 | "@emotion/sheet@^1.2.1":
298 | version "1.2.1"
299 | resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
300 | integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
301 |
302 | "@emotion/styled@^11.10.0":
303 | version "11.10.5"
304 | resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.5.tgz#1fe7bf941b0909802cb826457e362444e7e96a79"
305 | integrity sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==
306 | dependencies:
307 | "@babel/runtime" "^7.18.3"
308 | "@emotion/babel-plugin" "^11.10.5"
309 | "@emotion/is-prop-valid" "^1.2.0"
310 | "@emotion/serialize" "^1.1.1"
311 | "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0"
312 | "@emotion/utils" "^1.2.0"
313 |
314 | "@emotion/unitless@^0.8.0":
315 | version "0.8.0"
316 | resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.0.tgz#a4a36e9cbdc6903737cd20d38033241e1b8833db"
317 | integrity sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==
318 |
319 | "@emotion/use-insertion-effect-with-fallbacks@^1.0.0":
320 | version "1.0.0"
321 | resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz#ffadaec35dbb7885bd54de3fa267ab2f860294df"
322 | integrity sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==
323 |
324 | "@emotion/utils@^1.2.0":
325 | version "1.2.0"
326 | resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
327 | integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
328 |
329 | "@emotion/weak-memoize@^0.3.0":
330 | version "0.3.0"
331 | resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
332 | integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
333 |
334 | "@esbuild/android-arm64@0.16.17":
335 | version "0.16.17"
336 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23"
337 | integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==
338 |
339 | "@esbuild/android-arm@0.16.17":
340 | version "0.16.17"
341 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2"
342 | integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==
343 |
344 | "@esbuild/android-x64@0.16.17":
345 | version "0.16.17"
346 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e"
347 | integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==
348 |
349 | "@esbuild/darwin-arm64@0.16.17":
350 | version "0.16.17"
351 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220"
352 | integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==
353 |
354 | "@esbuild/darwin-x64@0.16.17":
355 | version "0.16.17"
356 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4"
357 | integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==
358 |
359 | "@esbuild/freebsd-arm64@0.16.17":
360 | version "0.16.17"
361 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27"
362 | integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==
363 |
364 | "@esbuild/freebsd-x64@0.16.17":
365 | version "0.16.17"
366 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72"
367 | integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==
368 |
369 | "@esbuild/linux-arm64@0.16.17":
370 | version "0.16.17"
371 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca"
372 | integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==
373 |
374 | "@esbuild/linux-arm@0.16.17":
375 | version "0.16.17"
376 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196"
377 | integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==
378 |
379 | "@esbuild/linux-ia32@0.16.17":
380 | version "0.16.17"
381 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54"
382 | integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==
383 |
384 | "@esbuild/linux-loong64@0.16.17":
385 | version "0.16.17"
386 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8"
387 | integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==
388 |
389 | "@esbuild/linux-mips64el@0.16.17":
390 | version "0.16.17"
391 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726"
392 | integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==
393 |
394 | "@esbuild/linux-ppc64@0.16.17":
395 | version "0.16.17"
396 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8"
397 | integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==
398 |
399 | "@esbuild/linux-riscv64@0.16.17":
400 | version "0.16.17"
401 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9"
402 | integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==
403 |
404 | "@esbuild/linux-s390x@0.16.17":
405 | version "0.16.17"
406 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87"
407 | integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==
408 |
409 | "@esbuild/linux-x64@0.16.17":
410 | version "0.16.17"
411 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f"
412 | integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==
413 |
414 | "@esbuild/netbsd-x64@0.16.17":
415 | version "0.16.17"
416 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775"
417 | integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==
418 |
419 | "@esbuild/openbsd-x64@0.16.17":
420 | version "0.16.17"
421 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35"
422 | integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==
423 |
424 | "@esbuild/sunos-x64@0.16.17":
425 | version "0.16.17"
426 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c"
427 | integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==
428 |
429 | "@esbuild/win32-arm64@0.16.17":
430 | version "0.16.17"
431 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a"
432 | integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==
433 |
434 | "@esbuild/win32-ia32@0.16.17":
435 | version "0.16.17"
436 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09"
437 | integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==
438 |
439 | "@esbuild/win32-x64@0.16.17":
440 | version "0.16.17"
441 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091"
442 | integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==
443 |
444 | "@fontsource/roboto@^4.5.8":
445 | version "4.5.8"
446 | resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-4.5.8.tgz#56347764786079838faf43f0eeda22dd7328437f"
447 | integrity sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==
448 |
449 | "@jridgewell/gen-mapping@^0.1.0":
450 | version "0.1.1"
451 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
452 | integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
453 | dependencies:
454 | "@jridgewell/set-array" "^1.0.0"
455 | "@jridgewell/sourcemap-codec" "^1.4.10"
456 |
457 | "@jridgewell/gen-mapping@^0.3.2":
458 | version "0.3.2"
459 | resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
460 | integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
461 | dependencies:
462 | "@jridgewell/set-array" "^1.0.1"
463 | "@jridgewell/sourcemap-codec" "^1.4.10"
464 | "@jridgewell/trace-mapping" "^0.3.9"
465 |
466 | "@jridgewell/resolve-uri@3.1.0":
467 | version "3.1.0"
468 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
469 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
470 |
471 | "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
472 | version "1.1.2"
473 | resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
474 | integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
475 |
476 | "@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
477 | version "1.4.14"
478 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
479 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
480 |
481 | "@jridgewell/trace-mapping@^0.3.9":
482 | version "0.3.17"
483 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
484 | integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
485 | dependencies:
486 | "@jridgewell/resolve-uri" "3.1.0"
487 | "@jridgewell/sourcemap-codec" "1.4.14"
488 |
489 | "@mui/base@5.0.0-alpha.114":
490 | version "5.0.0-alpha.114"
491 | resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.114.tgz#19125f28b7d09d1cc60550872440ecba699d8374"
492 | integrity sha512-ZpsG2I+zTOAnVTj3Un7TxD2zKRA2OhEPGMcWs/9ylPlS6VuGQSXowPooZiqarjT7TZ0+1bOe8titk/t8dLFiGw==
493 | dependencies:
494 | "@babel/runtime" "^7.20.7"
495 | "@emotion/is-prop-valid" "^1.2.0"
496 | "@mui/types" "^7.2.3"
497 | "@mui/utils" "^5.11.2"
498 | "@popperjs/core" "^2.11.6"
499 | clsx "^1.2.1"
500 | prop-types "^15.8.1"
501 | react-is "^18.2.0"
502 |
503 | "@mui/core-downloads-tracker@^5.11.5":
504 | version "5.11.5"
505 | resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.5.tgz#473c9b918d974f03acc07d29ce467bb91eba13c6"
506 | integrity sha512-MIuWGjitOsugpRhp64CQY3ZEVMIu9M/L9ioql6QLSkz73+bGIlC9FEhfi670/GZ8pQIIGmtiGGwofYzlwEWjig==
507 |
508 | "@mui/icons-material@^5.8.4":
509 | version "5.11.0"
510 | resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.11.0.tgz#9ea6949278b2266d2683866069cd43009eaf6464"
511 | integrity sha512-I2LaOKqO8a0xcLGtIozC9xoXjZAto5G5gh0FYUMAlbsIHNHIjn4Xrw9rvjY20vZonyiGrZNMAlAXYkY6JvhF6A==
512 | dependencies:
513 | "@babel/runtime" "^7.20.6"
514 |
515 | "@mui/material@^5.10.1":
516 | version "5.11.5"
517 | resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.11.5.tgz#b4867b4a6f3289e41f70b4393c075a799be8d24b"
518 | integrity sha512-5fzjBbRYaB5MoEpvA32oalAWltOZ3/kSyuovuVmPc6UF6AG42lTtbdMLpdCygurFSGUMZYTg4Cjij52fKlDDgg==
519 | dependencies:
520 | "@babel/runtime" "^7.20.7"
521 | "@mui/base" "5.0.0-alpha.114"
522 | "@mui/core-downloads-tracker" "^5.11.5"
523 | "@mui/system" "^5.11.5"
524 | "@mui/types" "^7.2.3"
525 | "@mui/utils" "^5.11.2"
526 | "@types/react-transition-group" "^4.4.5"
527 | clsx "^1.2.1"
528 | csstype "^3.1.1"
529 | prop-types "^15.8.1"
530 | react-is "^18.2.0"
531 | react-transition-group "^4.4.5"
532 |
533 | "@mui/private-theming@^5.11.2":
534 | version "5.11.2"
535 | resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.11.2.tgz#93eafb317070888a988efa8d6a9ec1f69183a606"
536 | integrity sha512-qZwMaqRFPwlYmqwVKblKBGKtIjJRAj3nsvX93pOmatsXyorW7N/0IPE/swPgz1VwChXhHO75DwBEx8tB+aRMNg==
537 | dependencies:
538 | "@babel/runtime" "^7.20.7"
539 | "@mui/utils" "^5.11.2"
540 | prop-types "^15.8.1"
541 |
542 | "@mui/styled-engine@^5.11.0":
543 | version "5.11.0"
544 | resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.11.0.tgz#79afb30c612c7807c4b77602cf258526d3997c7b"
545 | integrity sha512-AF06K60Zc58qf0f7X+Y/QjaHaZq16znliLnGc9iVrV/+s8Ln/FCoeNuFvhlCbZZQ5WQcJvcy59zp0nXrklGGPQ==
546 | dependencies:
547 | "@babel/runtime" "^7.20.6"
548 | "@emotion/cache" "^11.10.5"
549 | csstype "^3.1.1"
550 | prop-types "^15.8.1"
551 |
552 | "@mui/system@^5.11.5":
553 | version "5.11.5"
554 | resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.11.5.tgz#c880199634708c866063396f88d3fdd4c1dfcb48"
555 | integrity sha512-KNVsJ0sgRRp2XBqhh4wPS5aacteqjwxgiYTVwVnll2fgkgunZKo3DsDiGMrFlCg25ZHA3Ax58txWGE9w58zp0w==
556 | dependencies:
557 | "@babel/runtime" "^7.20.7"
558 | "@mui/private-theming" "^5.11.2"
559 | "@mui/styled-engine" "^5.11.0"
560 | "@mui/types" "^7.2.3"
561 | "@mui/utils" "^5.11.2"
562 | clsx "^1.2.1"
563 | csstype "^3.1.1"
564 | prop-types "^15.8.1"
565 |
566 | "@mui/types@^7.2.3":
567 | version "7.2.3"
568 | resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.3.tgz#06faae1c0e2f3a31c86af6f28b3a4a42143670b9"
569 | integrity sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==
570 |
571 | "@mui/utils@^5.11.2":
572 | version "5.11.2"
573 | resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.11.2.tgz#29764311acb99425159b159b1cb382153ad9be1f"
574 | integrity sha512-AyizuHHlGdAtH5hOOXBW3kriuIwUIKUIgg0P7LzMvzf6jPhoQbENYqY6zJqfoZ7fAWMNNYT8mgN5EftNGzwE2w==
575 | dependencies:
576 | "@babel/runtime" "^7.20.7"
577 | "@types/prop-types" "^15.7.5"
578 | "@types/react-is" "^16.7.1 || ^17.0.0"
579 | prop-types "^15.8.1"
580 | react-is "^18.2.0"
581 |
582 | "@popperjs/core@^2.11.6":
583 | version "2.11.6"
584 | resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
585 | integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
586 |
587 | "@react-leaflet/core@^2.1.0":
588 | version "2.1.0"
589 | resolved "https://registry.yarnpkg.com/@react-leaflet/core/-/core-2.1.0.tgz#383acd31259d7c9ae8fb1b02d5e18fe613c2a13d"
590 | integrity sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==
591 |
592 | "@remix-run/router@1.3.0":
593 | version "1.3.0"
594 | resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.3.0.tgz#b6ee542c7f087b73b3d8215b9bf799f648be71cb"
595 | integrity sha512-nwQoYb3m4DDpHTeOwpJEuDt8lWVcujhYYSFGLluC+9es2PyLjm+jjq3IeRBQbwBtPLJE/lkuHuGHr8uQLgmJRA==
596 |
597 | "@socket.io/component-emitter@~3.1.0":
598 | version "3.1.0"
599 | resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
600 | integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
601 |
602 | "@types/geojson@*":
603 | version "7946.0.10"
604 | resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249"
605 | integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==
606 |
607 | "@types/history@^4.7.11":
608 | version "4.7.11"
609 | resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
610 | integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
611 |
612 | "@types/leaflet@^1.9.0":
613 | version "1.9.0"
614 | resolved "https://registry.yarnpkg.com/@types/leaflet/-/leaflet-1.9.0.tgz#8caf452255e16cb15e0eabcb0d2a26793da0a6a2"
615 | integrity sha512-7LeOSj7EloC5UcyOMo+1kc3S1UT3MjJxwqsMT1d2PTyvQz53w0Y0oSSk9nwZnOZubCmBvpSNGceucxiq+ZPEUw==
616 | dependencies:
617 | "@types/geojson" "*"
618 |
619 | "@types/lodash@^4.14.191":
620 | version "4.14.191"
621 | resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa"
622 | integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==
623 |
624 | "@types/parse-json@^4.0.0":
625 | version "4.0.0"
626 | resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
627 | integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
628 |
629 | "@types/prop-types@*", "@types/prop-types@^15.7.5":
630 | version "15.7.5"
631 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
632 | integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
633 |
634 | "@types/react-dom@^18.0.10":
635 | version "18.0.10"
636 | resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352"
637 | integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==
638 | dependencies:
639 | "@types/react" "*"
640 |
641 | "@types/react-is@^16.7.1 || ^17.0.0":
642 | version "17.0.3"
643 | resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
644 | integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
645 | dependencies:
646 | "@types/react" "*"
647 |
648 | "@types/react-router-dom@^5.3.3":
649 | version "5.3.3"
650 | resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
651 | integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
652 | dependencies:
653 | "@types/history" "^4.7.11"
654 | "@types/react" "*"
655 | "@types/react-router" "*"
656 |
657 | "@types/react-router@*", "@types/react-router@^5.1.20":
658 | version "5.1.20"
659 | resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c"
660 | integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==
661 | dependencies:
662 | "@types/history" "^4.7.11"
663 | "@types/react" "*"
664 |
665 | "@types/react-transition-group@^4.4.5":
666 | version "4.4.5"
667 | resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416"
668 | integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==
669 | dependencies:
670 | "@types/react" "*"
671 |
672 | "@types/react@*", "@types/react@^18.0.27":
673 | version "18.0.27"
674 | resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.27.tgz#d9425abe187a00f8a5ec182b010d4fd9da703b71"
675 | integrity sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA==
676 | dependencies:
677 | "@types/prop-types" "*"
678 | "@types/scheduler" "*"
679 | csstype "^3.0.2"
680 |
681 | "@types/scheduler@*":
682 | version "0.16.2"
683 | resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
684 | integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
685 |
686 | "@vitejs/plugin-react@^3.0.1":
687 | version "3.0.1"
688 | resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-3.0.1.tgz#ad21fb81377970dd4021a31cd95a03eb6f5c4c48"
689 | integrity sha512-mx+QvYwIbbpOIJw+hypjnW1lAbKDHtWK5ibkF/V1/oMBu8HU/chb+SnqJDAsLq1+7rGqjktCEomMTM5KShzUKQ==
690 | dependencies:
691 | "@babel/core" "^7.20.7"
692 | "@babel/plugin-transform-react-jsx-self" "^7.18.6"
693 | "@babel/plugin-transform-react-jsx-source" "^7.19.6"
694 | magic-string "^0.27.0"
695 | react-refresh "^0.14.0"
696 |
697 | ansi-styles@^3.2.1:
698 | version "3.2.1"
699 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
700 | integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
701 | dependencies:
702 | color-convert "^1.9.0"
703 |
704 | asynckit@^0.4.0:
705 | version "0.4.0"
706 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
707 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
708 |
709 | axios@^0.27.2:
710 | version "0.27.2"
711 | resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
712 | integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
713 | dependencies:
714 | follow-redirects "^1.14.9"
715 | form-data "^4.0.0"
716 |
717 | babel-plugin-macros@^3.1.0:
718 | version "3.1.0"
719 | resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
720 | integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
721 | dependencies:
722 | "@babel/runtime" "^7.12.5"
723 | cosmiconfig "^7.0.0"
724 | resolve "^1.19.0"
725 |
726 | browserslist@^4.21.3:
727 | version "4.21.4"
728 | resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
729 | integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
730 | dependencies:
731 | caniuse-lite "^1.0.30001400"
732 | electron-to-chromium "^1.4.251"
733 | node-releases "^2.0.6"
734 | update-browserslist-db "^1.0.9"
735 |
736 | callsites@^3.0.0:
737 | version "3.1.0"
738 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
739 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
740 |
741 | caniuse-lite@^1.0.30001400:
742 | version "1.0.30001446"
743 | resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz#6d4ba828ab19f49f9bcd14a8430d30feebf1e0c5"
744 | integrity sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==
745 |
746 | chalk@^2.0.0:
747 | version "2.4.2"
748 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
749 | integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
750 | dependencies:
751 | ansi-styles "^3.2.1"
752 | escape-string-regexp "^1.0.5"
753 | supports-color "^5.3.0"
754 |
755 | clsx@^1.2.1:
756 | version "1.2.1"
757 | resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
758 | integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
759 |
760 | color-convert@^1.9.0:
761 | version "1.9.3"
762 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
763 | integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
764 | dependencies:
765 | color-name "1.1.3"
766 |
767 | color-name@1.1.3:
768 | version "1.1.3"
769 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
770 | integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
771 |
772 | combined-stream@^1.0.8:
773 | version "1.0.8"
774 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
775 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
776 | dependencies:
777 | delayed-stream "~1.0.0"
778 |
779 | convert-source-map@^1.5.0, convert-source-map@^1.7.0:
780 | version "1.9.0"
781 | resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
782 | integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
783 |
784 | cosmiconfig@^7.0.0:
785 | version "7.1.0"
786 | resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
787 | integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
788 | dependencies:
789 | "@types/parse-json" "^4.0.0"
790 | import-fresh "^3.2.1"
791 | parse-json "^5.0.0"
792 | path-type "^4.0.0"
793 | yaml "^1.10.0"
794 |
795 | csstype@^3.0.2, csstype@^3.1.1:
796 | version "3.1.1"
797 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
798 | integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
799 |
800 | debug@^4.1.0, debug@~4.3.1, debug@~4.3.2:
801 | version "4.3.4"
802 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
803 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
804 | dependencies:
805 | ms "2.1.2"
806 |
807 | delayed-stream@~1.0.0:
808 | version "1.0.0"
809 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
810 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
811 |
812 | dom-helpers@^5.0.1:
813 | version "5.2.1"
814 | resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
815 | integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
816 | dependencies:
817 | "@babel/runtime" "^7.8.7"
818 | csstype "^3.0.2"
819 |
820 | electron-to-chromium@^1.4.251:
821 | version "1.4.284"
822 | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
823 | integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
824 |
825 | engine.io-client@~6.2.3:
826 | version "6.2.3"
827 | resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458"
828 | integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==
829 | dependencies:
830 | "@socket.io/component-emitter" "~3.1.0"
831 | debug "~4.3.1"
832 | engine.io-parser "~5.0.3"
833 | ws "~8.2.3"
834 | xmlhttprequest-ssl "~2.0.0"
835 |
836 | engine.io-parser@~5.0.3:
837 | version "5.0.6"
838 | resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45"
839 | integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==
840 |
841 | error-ex@^1.3.1:
842 | version "1.3.2"
843 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
844 | integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
845 | dependencies:
846 | is-arrayish "^0.2.1"
847 |
848 | esbuild@^0.16.3:
849 | version "0.16.17"
850 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259"
851 | integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==
852 | optionalDependencies:
853 | "@esbuild/android-arm" "0.16.17"
854 | "@esbuild/android-arm64" "0.16.17"
855 | "@esbuild/android-x64" "0.16.17"
856 | "@esbuild/darwin-arm64" "0.16.17"
857 | "@esbuild/darwin-x64" "0.16.17"
858 | "@esbuild/freebsd-arm64" "0.16.17"
859 | "@esbuild/freebsd-x64" "0.16.17"
860 | "@esbuild/linux-arm" "0.16.17"
861 | "@esbuild/linux-arm64" "0.16.17"
862 | "@esbuild/linux-ia32" "0.16.17"
863 | "@esbuild/linux-loong64" "0.16.17"
864 | "@esbuild/linux-mips64el" "0.16.17"
865 | "@esbuild/linux-ppc64" "0.16.17"
866 | "@esbuild/linux-riscv64" "0.16.17"
867 | "@esbuild/linux-s390x" "0.16.17"
868 | "@esbuild/linux-x64" "0.16.17"
869 | "@esbuild/netbsd-x64" "0.16.17"
870 | "@esbuild/openbsd-x64" "0.16.17"
871 | "@esbuild/sunos-x64" "0.16.17"
872 | "@esbuild/win32-arm64" "0.16.17"
873 | "@esbuild/win32-ia32" "0.16.17"
874 | "@esbuild/win32-x64" "0.16.17"
875 |
876 | escalade@^3.1.1:
877 | version "3.1.1"
878 | resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
879 | integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
880 |
881 | escape-string-regexp@^1.0.5:
882 | version "1.0.5"
883 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
884 | integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
885 |
886 | escape-string-regexp@^4.0.0:
887 | version "4.0.0"
888 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
889 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
890 |
891 | find-root@^1.1.0:
892 | version "1.1.0"
893 | resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
894 | integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
895 |
896 | follow-redirects@^1.14.9:
897 | version "1.15.2"
898 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
899 | integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
900 |
901 | form-data@^4.0.0:
902 | version "4.0.0"
903 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
904 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
905 | dependencies:
906 | asynckit "^0.4.0"
907 | combined-stream "^1.0.8"
908 | mime-types "^2.1.12"
909 |
910 | fsevents@~2.3.2:
911 | version "2.3.2"
912 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
913 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
914 |
915 | function-bind@^1.1.1:
916 | version "1.1.1"
917 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
918 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
919 |
920 | gensync@^1.0.0-beta.2:
921 | version "1.0.0-beta.2"
922 | resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
923 | integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
924 |
925 | globals@^11.1.0:
926 | version "11.12.0"
927 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
928 | integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
929 |
930 | has-flag@^3.0.0:
931 | version "3.0.0"
932 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
933 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
934 |
935 | has@^1.0.3:
936 | version "1.0.3"
937 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
938 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
939 | dependencies:
940 | function-bind "^1.1.1"
941 |
942 | hoist-non-react-statics@^3.3.1:
943 | version "3.3.2"
944 | resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
945 | integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
946 | dependencies:
947 | react-is "^16.7.0"
948 |
949 | import-fresh@^3.2.1:
950 | version "3.3.0"
951 | resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
952 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
953 | dependencies:
954 | parent-module "^1.0.0"
955 | resolve-from "^4.0.0"
956 |
957 | is-arrayish@^0.2.1:
958 | version "0.2.1"
959 | resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
960 | integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
961 |
962 | is-core-module@^2.9.0:
963 | version "2.11.0"
964 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
965 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==
966 | dependencies:
967 | has "^1.0.3"
968 |
969 | "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
970 | version "4.0.0"
971 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
972 | integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
973 |
974 | jsesc@^2.5.1:
975 | version "2.5.2"
976 | resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
977 | integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
978 |
979 | json-parse-even-better-errors@^2.3.0:
980 | version "2.3.1"
981 | resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
982 | integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
983 |
984 | json5@^2.2.2:
985 | version "2.2.3"
986 | resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
987 | integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
988 |
989 | leaflet@^1.9.3:
990 | version "1.9.3"
991 | resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.3.tgz#52ec436954964e2d3d39e0d433da4b2500d74414"
992 | integrity sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==
993 |
994 | lines-and-columns@^1.1.6:
995 | version "1.2.4"
996 | resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
997 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
998 |
999 | lodash@^4.17.21:
1000 | version "4.17.21"
1001 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
1002 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
1003 |
1004 | loose-envify@^1.1.0, loose-envify@^1.4.0:
1005 | version "1.4.0"
1006 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
1007 | integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
1008 | dependencies:
1009 | js-tokens "^3.0.0 || ^4.0.0"
1010 |
1011 | lru-cache@^5.1.1:
1012 | version "5.1.1"
1013 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
1014 | integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
1015 | dependencies:
1016 | yallist "^3.0.2"
1017 |
1018 | magic-string@^0.27.0:
1019 | version "0.27.0"
1020 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3"
1021 | integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==
1022 | dependencies:
1023 | "@jridgewell/sourcemap-codec" "^1.4.13"
1024 |
1025 | mime-db@1.52.0:
1026 | version "1.52.0"
1027 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
1028 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
1029 |
1030 | mime-types@^2.1.12:
1031 | version "2.1.35"
1032 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
1033 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
1034 | dependencies:
1035 | mime-db "1.52.0"
1036 |
1037 | ms@2.1.2:
1038 | version "2.1.2"
1039 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
1040 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
1041 |
1042 | nanoid@^3.3.4:
1043 | version "3.3.4"
1044 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
1045 | integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
1046 |
1047 | node-releases@^2.0.6:
1048 | version "2.0.8"
1049 | resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
1050 | integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==
1051 |
1052 | object-assign@^4.1.1:
1053 | version "4.1.1"
1054 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
1055 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
1056 |
1057 | parent-module@^1.0.0:
1058 | version "1.0.1"
1059 | resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
1060 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
1061 | dependencies:
1062 | callsites "^3.0.0"
1063 |
1064 | parse-json@^5.0.0:
1065 | version "5.2.0"
1066 | resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
1067 | integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
1068 | dependencies:
1069 | "@babel/code-frame" "^7.0.0"
1070 | error-ex "^1.3.1"
1071 | json-parse-even-better-errors "^2.3.0"
1072 | lines-and-columns "^1.1.6"
1073 |
1074 | path-parse@^1.0.7:
1075 | version "1.0.7"
1076 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
1077 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
1078 |
1079 | path-type@^4.0.0:
1080 | version "4.0.0"
1081 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
1082 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
1083 |
1084 | picocolors@^1.0.0:
1085 | version "1.0.0"
1086 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
1087 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
1088 |
1089 | postcss@^8.4.20:
1090 | version "8.4.21"
1091 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
1092 | integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
1093 | dependencies:
1094 | nanoid "^3.3.4"
1095 | picocolors "^1.0.0"
1096 | source-map-js "^1.0.2"
1097 |
1098 | prop-types@^15.6.2, prop-types@^15.8.1:
1099 | version "15.8.1"
1100 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
1101 | integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
1102 | dependencies:
1103 | loose-envify "^1.4.0"
1104 | object-assign "^4.1.1"
1105 | react-is "^16.13.1"
1106 |
1107 | react-dom@^18.2.0:
1108 | version "18.2.0"
1109 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
1110 | integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
1111 | dependencies:
1112 | loose-envify "^1.1.0"
1113 | scheduler "^0.23.0"
1114 |
1115 | react-is@^16.13.1, react-is@^16.7.0:
1116 | version "16.13.1"
1117 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
1118 | integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
1119 |
1120 | react-is@^18.2.0:
1121 | version "18.2.0"
1122 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
1123 | integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
1124 |
1125 | react-leaflet@^4.1.0:
1126 | version "4.2.0"
1127 | resolved "https://registry.yarnpkg.com/react-leaflet/-/react-leaflet-4.2.0.tgz#6c2ee4b576209d9fe40e78006cfa37196c216eaa"
1128 | integrity sha512-9d8T7hzYrQA5GLe3vn0qtRLJzQKgjr080NKa45yArGwuSl1nH/6aK9gp7DeYdktpdO1vKGSUTGW5AsUS064X0A==
1129 | dependencies:
1130 | "@react-leaflet/core" "^2.1.0"
1131 |
1132 | react-refresh@^0.14.0:
1133 | version "0.14.0"
1134 | resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
1135 | integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
1136 |
1137 | react-router-dom@^6.3.0:
1138 | version "6.7.0"
1139 | resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.7.0.tgz#0249f4ca4eb704562b8b0ff29caeb928c3a6ed38"
1140 | integrity sha512-jQtXUJyhso3kFw430+0SPCbmCmY1/kJv8iRffGHwHy3CkoomGxeYzMkmeSPYo6Egzh3FKJZRAL22yg5p2tXtfg==
1141 | dependencies:
1142 | "@remix-run/router" "1.3.0"
1143 | react-router "6.7.0"
1144 |
1145 | react-router@6.7.0:
1146 | version "6.7.0"
1147 | resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.7.0.tgz#db262684c13b5c2970694084ae9e8531718a0681"
1148 | integrity sha512-KNWlG622ddq29MAM159uUsNMdbX8USruoKnwMMQcs/QWZgFUayICSn2oB7reHce1zPj6CG18kfkZIunSSRyGHg==
1149 | dependencies:
1150 | "@remix-run/router" "1.3.0"
1151 |
1152 | react-transition-group@^4.4.5:
1153 | version "4.4.5"
1154 | resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
1155 | integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
1156 | dependencies:
1157 | "@babel/runtime" "^7.5.5"
1158 | dom-helpers "^5.0.1"
1159 | loose-envify "^1.4.0"
1160 | prop-types "^15.6.2"
1161 |
1162 | react@^18.2.0:
1163 | version "18.2.0"
1164 | resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
1165 | integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
1166 | dependencies:
1167 | loose-envify "^1.1.0"
1168 |
1169 | regenerator-runtime@^0.13.11:
1170 | version "0.13.11"
1171 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
1172 | integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
1173 |
1174 | resolve-from@^4.0.0:
1175 | version "4.0.0"
1176 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
1177 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
1178 |
1179 | resolve@^1.19.0, resolve@^1.22.1:
1180 | version "1.22.1"
1181 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
1182 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
1183 | dependencies:
1184 | is-core-module "^2.9.0"
1185 | path-parse "^1.0.7"
1186 | supports-preserve-symlinks-flag "^1.0.0"
1187 |
1188 | rollup@^3.7.0:
1189 | version "3.10.1"
1190 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.10.1.tgz#56278901ed11fc2898421e8e3e2c8155bc7b40b4"
1191 | integrity sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw==
1192 | optionalDependencies:
1193 | fsevents "~2.3.2"
1194 |
1195 | scheduler@^0.23.0:
1196 | version "0.23.0"
1197 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
1198 | integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
1199 | dependencies:
1200 | loose-envify "^1.1.0"
1201 |
1202 | semver@^6.3.0:
1203 | version "6.3.0"
1204 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
1205 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
1206 |
1207 | socket.io-client@^4.5.1:
1208 | version "4.5.4"
1209 | resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9"
1210 | integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==
1211 | dependencies:
1212 | "@socket.io/component-emitter" "~3.1.0"
1213 | debug "~4.3.2"
1214 | engine.io-client "~6.2.3"
1215 | socket.io-parser "~4.2.1"
1216 |
1217 | socket.io-parser@~4.2.1:
1218 | version "4.2.2"
1219 | resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206"
1220 | integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
1221 | dependencies:
1222 | "@socket.io/component-emitter" "~3.1.0"
1223 | debug "~4.3.1"
1224 |
1225 | source-map-js@^1.0.2:
1226 | version "1.0.2"
1227 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
1228 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
1229 |
1230 | source-map@^0.5.7:
1231 | version "0.5.7"
1232 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
1233 | integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
1234 |
1235 | stylis@4.1.3:
1236 | version "4.1.3"
1237 | resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7"
1238 | integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==
1239 |
1240 | supports-color@^5.3.0:
1241 | version "5.5.0"
1242 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
1243 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
1244 | dependencies:
1245 | has-flag "^3.0.0"
1246 |
1247 | supports-preserve-symlinks-flag@^1.0.0:
1248 | version "1.0.0"
1249 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
1250 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
1251 |
1252 | to-fast-properties@^2.0.0:
1253 | version "2.0.0"
1254 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
1255 | integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
1256 |
1257 | typescript@^4.9.3:
1258 | version "4.9.4"
1259 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
1260 | integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
1261 |
1262 | update-browserslist-db@^1.0.9:
1263 | version "1.0.10"
1264 | resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
1265 | integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
1266 | dependencies:
1267 | escalade "^3.1.1"
1268 | picocolors "^1.0.0"
1269 |
1270 | vite@^4.0.0:
1271 | version "4.0.4"
1272 | resolved "https://registry.yarnpkg.com/vite/-/vite-4.0.4.tgz#4612ce0b47bbb233a887a54a4ae0c6e240a0da31"
1273 | integrity sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==
1274 | dependencies:
1275 | esbuild "^0.16.3"
1276 | postcss "^8.4.20"
1277 | resolve "^1.22.1"
1278 | rollup "^3.7.0"
1279 | optionalDependencies:
1280 | fsevents "~2.3.2"
1281 |
1282 | ws@~8.2.3:
1283 | version "8.2.3"
1284 | resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
1285 | integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
1286 |
1287 | xmlhttprequest-ssl@~2.0.0:
1288 | version "2.0.0"
1289 | resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
1290 | integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
1291 |
1292 | yallist@^3.0.2:
1293 | version "3.1.1"
1294 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
1295 | integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
1296 |
1297 | yaml@^1.10.0:
1298 | version "1.10.2"
1299 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
1300 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
1301 |
--------------------------------------------------------------------------------