Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test #1

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/App.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.App-header{
background-color: #8ABEB1
background-color: #8ABEB1;
text-align: center;
}
.App-logo{
margin: 5px 0px 5px 10px;
Expand Down
77 changes: 44 additions & 33 deletions src/features/route-map.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
import React, {useState, useEffect} from "react"
import { Port } from '../services/ports-reducer'
import { Loader } from "@googlemaps/js-api-loader"
import React, { useState, useEffect } from "react";
import { Port } from "../services/ports-reducer";
import { Loader } from "@googlemaps/js-api-loader";

declare global {
interface Window {
initMap: () => void;
}
interface Window {
initMap: () => void;
}
}

interface RouteMapParams{
ports: Port[]
interface RouteMapParams {
ports: Port[];
}
function RouteMap({ports}: RouteMapParams){
const [theMap, setTheMap] = useState<google.maps.Map>()
function RouteMap({ ports }: RouteMapParams) {
const [theMap, setTheMap] = useState<google.maps.Map>();

const loader = new Loader({
apiKey: "AIzaSyDckokR5ozbjFB7hC0yip1S5mbPYcgoQv8",
version: "weekly"
})
const loader = new Loader({
apiKey: "AIzaSyDckokR5ozbjFB7hC0yip1S5mbPYcgoQv8",
version: "weekly",
});

useEffect(() => {
loader
.load()
.then((google) => {
const mapOptions = {
center: {
lat: 0,
lng: 0
},
zoom: 3
}
const map = new google.maps.Map(document.getElementById("route-map") as HTMLElement, mapOptions)
setTheMap(map);
})
}, []);
return (
<div id="map-container">
<div id="route-map"></div>
useEffect(() => {
loader.load().then((google) => {
const mapOptions = {
center: {
lat: 0,
lng: 0,
},
zoom: 3,
};
const map = new google.maps.Map(
document.getElementById("route-map") as HTMLElement,
mapOptions
);
const infowindow = new google.maps.InfoWindow();
for (let i = 0; i < ports.length; i++) {
const singlePort = ports[i];
const myLatLng = { lat: singlePort["lat"], lng: singlePort["lng"] };
new google.maps.Marker({
position: myLatLng,
map,
title: singlePort["name"],
});
}
setTheMap(map);
});
}, [ports]);
return (
<div id="map-container">
<div id="route-map"></div>
</div>
)
);
}

export default RouteMap;
64 changes: 32 additions & 32 deletions src/features/voyage-planner.css
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
.voyage-planner-container{
display: flex;
flex-direction: row;
}

.voyage-route-container{
width: 20%;
height: 100vh;
border-left: 2px solid grey;
display: flex;
flex-direction: column;
}
#port-select{
width: 90%;
}
.port-locator{

}

.voyage-listing{

}

.voyage-map-container{
width: 80%;
height: 100vh;
}

#route-map{
.voyage-planner-container {
display: flex;
flex-direction: row;
margin: 10px;
}
.voyage-route-container {
width: 20%;
height: 100vh;
border-left: 2px solid grey;
display: flex;
flex-direction: column;
padding: 20px;
}
#port-select {
width: 90%;
}
.port-locator {
line-height: 2;
}
.voyage-listing {
line-height: 1.5;
list-style: none;
}
.voyage-map-container {
width: 80%;
height: 100vh;
margin-top: 20px;
}
#route-map {
width: 100%;
height: 90%;
}
#map-container{
width: 100%;
height: 100%;
#map-container {
width: 100%;
height: 100%;
}
134 changes: 110 additions & 24 deletions src/features/voyage-planner.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,116 @@
import React from 'react'
import RouteMap from "./route-map"
import "./voyage-planner.css"
import React, { useEffect, useState } from "react";
import RouteMap from "./route-map";
import "./voyage-planner.css";
import { fetchPorts, setPorts, Port } from "../services/ports-reducer";
import { addPorts, VESSEL_SPEED_KNPH } from "../services/voyage-reducer";
import { useAppDispatch, useAppSelector } from "../services/hooks";
import store, { RootState } from "../services/store";

interface VoyagePlannerParams{
interface VoyagePlannerParams {}

}
function VoyagePlanner(params: VoyagePlannerParams) {
const [selectedPort, setSelectedPort] = useState("");
const [rerender, setRerender] = useState(false);
const [distance, setDistance] = useState(0);
const [time, setTime] = useState(0);
const [offset, setOffset] = useState(0);

const dispatch = useAppDispatch();

useEffect(() => {
fetchPorts(offset).then((resp: any) => {
dispatch(setPorts(resp));
setOffset(offset + resp.length);
setSelectedPort(store.getState().ports.ports[0].name);
});
}, [offset]);

useEffect(() => {
if (store.getState().voyage.ports && store.getState().voyage.ports.length) {
const ports: any[] = store.getState().voyage.ports;
let dist: number = 0;
ports.forEach((port: any, index: number) => {
if (index < ports.length - 1) {
dist += getDistance(port, ports[index + 1]);
dist = Math.round((dist + Number.EPSILON) * 100) / 100;
}
});
setDistance(dist);
const time: number =
Math.round((dist / VESSEL_SPEED_KNPH + Number.EPSILON) * 100) / 100;
setTime(time);
}
}, [store.getState().voyage.ports, store.getState().voyage.ports.length]);

function VoyagePlanner(params: VoyagePlannerParams){

return (
<div className="voyage-planner-container">
<div className="voyage-route-container">
<h2>Route Planner</h2>
<div className="port-locator">
<select id="port-select"/> <button>+</button>
</div>
<div className="voyage-listing">
<h2>Voyage</h2>
</div>
</div>
<div className="voyage-map-container">
<h2>Route Map</h2>
<RouteMap ports={[]}/>
</div>
const getDistance = (port1: any, port2: any) => {
const val: number = Math.PI / 180;
let lat1: number = port1.lat * val;
let long1: number = port1.lng * val;
let lat2: number = port2.lat * val;
let long2: number = port2.lng * val;

let dist: number = Math.acos(
Math.sin(lat1) * Math.sin(lat2) +
Math.cos(lat1) * Math.cos(lat2) * Math.cos(long1 - long2)
);
//assuming ellipse is sphere
dist = dist * (180 / Math.PI) * 60;
return dist;
};

const handleChange = (e: any) => {
setSelectedPort(e.target.value);
setRerender(false);
};

const addVoyage = () => {
const portTobeAdded =
store.getState().ports.ports &&
store
.getState()
.ports.ports.filter((item: Port) => item.name === selectedPort);
dispatch(addPorts(portTobeAdded[0]));
setRerender(true);
};
return (
<div className='voyage-planner-container'>
<div className='voyage-route-container'>
<h2>Route Planner</h2>
<div className='port-locator'>
<select id='port-select' value={selectedPort} onChange={handleChange}>
{store.getState().ports.ports &&
store.getState().ports.ports.map((item: Port, key: number) => {
return (
<option id={key.toString()} key={key} value={item.name}>
{item.name}
</option>
);
})}
</select>
<button onClick={addVoyage}>Add</button>
</div>
<div className='voyage-listing'>
<h2>Voyage</h2>
{store.getState().voyage.ports &&
store.getState().voyage.ports.map((item, key) => {
return <li key={key}>{item.name}</li>;
})}
</div>
<div className='distance-time'>
<h4>
Distance: <span>{distance}</span>
</h4>
<h4>
Estimated Time:
<span>{time}</span>
</h4>
</div>
)
</div>
<div className='voyage-map-container'>
<h2>Route Map</h2>
<RouteMap ports={store.getState().voyage.ports} />
</div>
</div>
);
}
export default VoyagePlanner
export default VoyagePlanner;
18 changes: 11 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import store from "../src/services/store";

const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<App />
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);

Expand Down
34 changes: 29 additions & 5 deletions src/services/ports-reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface Port {
uncode: string,
name: string,
lat: number,
lon: number
lng: number
}

interface PortsState {
Expand All @@ -23,6 +23,16 @@ const initialState: PortsState = {
offset: 0,
ports: []
}
export interface SetPortAction {
type: 'SETPORTACTION'
portList: Port[]
}

export const setPorts = (portList: Port[]): SetPortAction => {
return { type: 'SETPORTACTION', portList }
}



/**
* TODO: implement the reducer function to manage the list of ports
Expand All @@ -31,9 +41,13 @@ const initialState: PortsState = {
* @param action
* @returns PortsState
*/
export function portsReducer(state = initialState, action: AnyAction): PortsState{

return state
export function portsReducer(state = initialState, action: AnyAction): PortsState {
switch (action.type) {
case 'SETPORTACTION':
return { ...state, ports: state.ports.concat(action.portList) }
default:
return { ...state }
}
}

/**
Expand All @@ -42,7 +56,17 @@ export function portsReducer(state = initialState, action: AnyAction): PortsStat
* so you may need multiple calls to fetch all the data
* @param offset: where to start pulling from
*/
export function fetchPorts(offset: number){}
export async function fetchPorts(offset: number) {
return new Promise((resolve) => {
fetch(
`https://8u9tblsay0.execute-api.us-east-1.amazonaws.com/default/zn-frontend-challenge-port-service?offset=${offset}`
)
.then((response) => response.json())
.then((json) => {
resolve(json.data);
});
});
}



Loading