diff --git a/packages/react-app/src/App.less b/packages/react-app/src/App.less
index eccaf83e..133612ca 100644
--- a/packages/react-app/src/App.less
+++ b/packages/react-app/src/App.less
@@ -257,6 +257,23 @@ select:focus {
}
}
+/* Parcel Info */
+.parcel-info {
+ border-left: 1px solid @gray-4;
+ padding: 1rem;
+}
+
+/* Header */
+.search-and-connect-wrapper {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+
+ @media (max-width: 1024px) {
+ justify-content: initial;
+ }
+}
+
/* BrowsePlots Layout */
.progress-bar-indicator {
margin-left: 3rem;
@@ -291,49 +308,47 @@ select:focus {
height: 100%;
display: grid;
grid-template-columns: 400px 1fr;
- grid-template-rows: 3rem 4rem 1fr;
+ grid-template-rows: 4rem 230px 1fr;
grid-column-gap: 0px;
grid-row-gap: 0px;
@media (max-width: 1024px) {
grid-template-columns: 1fr;
- grid-template-rows: 3rem repeat(2, 4rem) 300px 4rem 5fr;
+ grid-template-rows: 0 repeat(2, 4rem) 300px 4rem 5fr;
}
// Responsive for mobile landscape screens
@media (max-height: 500px) {
- grid-template-rows: 3rem repeat(2, 4rem) 300px 4rem 600px;
+ grid-template-rows: 0 repeat(2, 4rem) 300px 4rem 600px;
}
}
-.progress-bar {
- grid-area: 1 / 1 / 2 / 3;
-}
.logo-link {
- grid-area: 2 / 1 / 3 / 2;
+ grid-area: 1 / 1 / 2 / 2;
}
.connect-wallet-section {
- grid-area: 2 / 2 / 3 / 3;
+ grid-area: 1 / 2 / 2 / 3;
border-color: @gray-4;
- justify-content: flex-end;
+ justify-content: space-between;
+}
+.parcel-info {
+ grid-area: 2 / 2 / 3 / 3;
}
.plot-detail {
- grid-area: 3 / 1 / 4 / 2;
+ grid-area: 2 / 1 / 4 / 2;
}
.plot-tabs {
- grid-area: 3 / 1 / 4 / 2;
+ grid-area: 2 / 1 / 4 / 2;
width: 100%;
}
.plot-map {
grid-area: 3 / 2 / 4 / 3;
+ border-left: 1px solid @gray-4;
}
@media (max-width: 1024px) {
- .progress-bar {
- grid-area: 1 / 1 / 2 / 2;
- }
.logo-link {
- grid-area: 2 / 1 / 3 / 2;
+ grid-area: 1 / 1 / 2 / 2;
}
.connect-wallet-section {
grid-area: 3 / 1 / 4 / 2;
@@ -347,7 +362,13 @@ select:focus {
grid-area: 5 / 1 / 7 / 2;
max-width: 100vw;
}
+ .parcel-info {
+ display: none;
+ }
.plot-detail {
grid-area: 6 / 1 / 7 / 2;
}
+ .sensor-header-info {
+ display: none;
+ }
}
diff --git a/packages/react-app/src/assets/images/icon-chart.png b/packages/react-app/src/assets/images/icon-chart.png
new file mode 100644
index 00000000..2ff98f13
Binary files /dev/null and b/packages/react-app/src/assets/images/icon-chart.png differ
diff --git a/packages/react-app/src/assets/images/icon-globe.svg b/packages/react-app/src/assets/images/icon-globe.svg
new file mode 100644
index 00000000..038b46f9
--- /dev/null
+++ b/packages/react-app/src/assets/images/icon-globe.svg
@@ -0,0 +1,3 @@
+
diff --git a/packages/react-app/src/components/Header.tsx b/packages/react-app/src/components/Header.tsx
index 6d5414b5..fc0bf4f3 100644
--- a/packages/react-app/src/components/Header.tsx
+++ b/packages/react-app/src/components/Header.tsx
@@ -1,14 +1,16 @@
import React from "react";
-import { ConnectWalletButton, SearchPlots } from ".";
-
+import { ConnectWalletButton, SearchPlots, SensorHeaderInfo } from ".";
interface Props {
connectWallet: () => void;
}
-export default function Header({ connectWallet }: Props) {
+export default function Header({ connectWallet }: Props): JSX.Element {
return (
);
}
diff --git a/packages/react-app/src/components/ParcelInfo.tsx b/packages/react-app/src/components/ParcelInfo.tsx
new file mode 100644
index 00000000..8dbcac81
--- /dev/null
+++ b/packages/react-app/src/components/ParcelInfo.tsx
@@ -0,0 +1,75 @@
+import React from "react";
+import { timesAgo, tsToDate } from "../utils";
+import IconChart from "../assets/images/icon-chart.png";
+import IconGlobe from "../assets/images/icon-globe.svg";
+interface SensorData {
+ snr: number;
+ vbat: number;
+ latitude: number;
+ longitude: number;
+ gas_resistance: number;
+ temperature: number;
+ pressure: number;
+ humidity: number;
+ light: number;
+ temperature2: number;
+ gyroscope: [number, number, number];
+ accelerometer: [number, number, number];
+ timestamp: string;
+ random: string;
+}
+interface Props {
+ sensorData: SensorData;
+}
+interface DataBoxProps {
+ value: string;
+ label: string;
+ unit?: string;
+}
+function DataBox({ value, label, unit }: DataBoxProps): JSX.Element {
+ return (
+
+
+

+
+ );
+}
+export default function ParcelInfo({ sensorData }: Props): JSX.Element {
+ const { temperature, pressure, humidity, gas_resistance, timestamp, latitude, longitude } = sensorData;
+ const localisedTime = new Date().toLocaleTimeString("en-US", {
+ timeZone: "America/Denver",
+ hour: "2-digit",
+ minute: "2-digit",
+ });
+ const fetchedTime = tsToDate(timestamp);
+
+ return (
+
+
+

+
+ {latitude}, {longitude}
+
+ BENNET CREEK ROAD, POWELL WYOMING 82435
+
+
+
It’s currently {localisedTime}
[-7 GMT] -------------- Last transmissoin
+ ~{timesAgo(fetchedTime)} ago{" "}
+
+ view on MachineFi
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/packages/react-app/src/components/PlotList.tsx b/packages/react-app/src/components/PlotList.tsx
index 4e04729c..1ebb960c 100644
--- a/packages/react-app/src/components/PlotList.tsx
+++ b/packages/react-app/src/components/PlotList.tsx
@@ -27,7 +27,7 @@ export default function PlotList({ plots, emptyMessage }: Props) {
};
return (
-
+
{displayedPlots.map((plot: Plot, idx: number) => (
{/* modulo delay by 10 because plot buttons are added in batches of 10 */}
diff --git a/packages/react-app/src/components/ProgressBar.tsx b/packages/react-app/src/components/ProgressBar.tsx
deleted file mode 100644
index c6179809..00000000
--- a/packages/react-app/src/components/ProgressBar.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from "react";
-import { useAppSelector } from "../hooks";
-
-export default function ProgressBar() {
- const plots = useAppSelector(state => state.plots.plots);
- const percent_sold = Math.ceil((plots.filter(plot => plot.sold).length / plots.length) * 100);
- return (
-
- {plots.length > 0 && (percent_sold < 100 ? "Parcel 0 sale is live!" : "Parcel 0 sale is sold out!")}
- {plots.length === 0 && "Fetching plots..."}
- {percent_sold < 100 ? (
-
-
- {percent_sold > 0 ? `${percent_sold}% already sold!` : "Be the first owner!"}
-
- ) : null}
-
- );
-}
diff --git a/packages/react-app/src/components/SensorHeaderInfo.tsx b/packages/react-app/src/components/SensorHeaderInfo.tsx
new file mode 100644
index 00000000..d5b6a2ce
--- /dev/null
+++ b/packages/react-app/src/components/SensorHeaderInfo.tsx
@@ -0,0 +1,7 @@
+import * as React from "react";
+
+const SensorHeaderInfo = () => {
+ return Parcel 0
;
+};
+
+export default SensorHeaderInfo;
diff --git a/packages/react-app/src/components/index.ts b/packages/react-app/src/components/index.ts
index 96edb57c..e6432368 100644
--- a/packages/react-app/src/components/index.ts
+++ b/packages/react-app/src/components/index.ts
@@ -4,7 +4,6 @@ export { default as PlotMap } from "./PlotMap";
export { default as PlotButton } from "./PlotButton";
export { default as PlotDetail } from "./PlotDetail";
export { default as PlotList } from "./PlotList";
-export { default as ProgressBar } from "./ProgressBar";
export { default as BuyPlot } from "./BuyPlot";
export { default as ViewPlot } from "./ViewPlot";
export { default as PlotTabs } from "./PlotTabs";
@@ -12,3 +11,5 @@ export { default as LogoDisplay } from "./LogoDisplay";
export { default as Header } from "./Header";
export { default as ConnectWalletButton } from "./ConnectWalletButton";
export { default as SearchPlots } from "./SearchPlots";
+export { default as ParcelInfo } from "./ParcelInfo";
+export { default as SensorHeaderInfo } from "./SensorHeaderInfo";
diff --git a/packages/react-app/src/containers/ParcelInfoContainer.tsx b/packages/react-app/src/containers/ParcelInfoContainer.tsx
new file mode 100644
index 00000000..0facf2ec
--- /dev/null
+++ b/packages/react-app/src/containers/ParcelInfoContainer.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import { gql, useQuery, InMemoryCache, ApolloClient } from "@apollo/client";
+import { ParcelInfo } from "../components";
+
+const PEBBLE_DEVICE_ID = "351358813276802";
+const pebbleUri = "https://pebble.iotex.me/v1/graphql";
+
+const pebbleClient = new ApolloClient({
+ uri: pebbleUri,
+ cache: new InMemoryCache(),
+});
+
+const GET_SENSOR_DATA = gql`
+ query getDevices($deviceId: String!) {
+ pebble_device_record(
+ limit: 1
+ order_by: { timestamp: desc }
+ where: { imei: { _eq: $deviceId }, latitude: { _neq: "200.0000000" } }
+ ) {
+ temperature
+ humidity
+ gas_resistance
+ pressure
+ latitude
+ longitude
+ timestamp
+ }
+ }
+`;
+
+function ParcelInfoContainer(): JSX.Element {
+ const { loading, error, data } = useQuery(GET_SENSOR_DATA, {
+ variables: { deviceId: PEBBLE_DEVICE_ID },
+ client: pebbleClient,
+ });
+ if (loading) return Sensor Data Loading ...
;
+ if (error) console.log(error);
+ if (error) return Sensor Data Load Failed.
;
+ console.log(data?.pebble_device_record[0]);
+ return (
+
+ );
+}
+
+export default ParcelInfoContainer;
diff --git a/packages/react-app/src/containers/index.ts b/packages/react-app/src/containers/index.ts
new file mode 100644
index 00000000..6cd0c774
--- /dev/null
+++ b/packages/react-app/src/containers/index.ts
@@ -0,0 +1 @@
+export { default as ParcelInfoContainer } from "./ParcelInfoContainer"
\ No newline at end of file
diff --git a/packages/react-app/src/utils.ts b/packages/react-app/src/utils.ts
new file mode 100644
index 00000000..27ae6c0c
--- /dev/null
+++ b/packages/react-app/src/utils.ts
@@ -0,0 +1,38 @@
+export function tsToDate(ts: string): Date {
+ return new Date(Number(ts) * 1000);
+}
+
+export function timesAgo(date: Date): string { // source: https://stackoverflow.com/a/55999110/458679
+ const seconds = Math.floor((+new Date() - +date) / 1000); // get the diffrence of date object sent with current date time of the system time
+ let interval = Math.floor(seconds / 31536000); // divide seconds by seconds in avg for a year to get years
+
+ //conditioning based on years derived above
+ if (interval > 1) {
+ return interval + " years";
+ }
+ interval = Math.floor(seconds / 2592000); // months check similar to years
+ if (interval > 1) {
+ return interval + " months";
+ }
+ interval = Math.floor(seconds / 86400); // days check similar to above
+ if (interval > 1) {
+ return interval + " days";
+ }
+ interval = Math.floor(seconds / 3600); // hours check
+ if (interval > 1) {
+ return interval + " hours";
+ }
+ interval = Math.floor(seconds / 60); // minutes check
+ if (interval > 1) {
+ return interval + " min";
+ }
+ return Math.floor(seconds) + " seconds"; // seconds check at the end
+}
+
+// withYears 22 years
+// withMonths 9 months
+// withDays 5 days
+// withPreviousDay 24 hours
+// withHours 2 hours
+// withMinutes 5 min
+
diff --git a/packages/react-app/src/views/BrowsePlots.tsx b/packages/react-app/src/views/BrowsePlots.tsx
index eb0c11f1..e59d9e39 100644
--- a/packages/react-app/src/views/BrowsePlots.tsx
+++ b/packages/react-app/src/views/BrowsePlots.tsx
@@ -4,7 +4,7 @@ import { Link } from "react-router-dom";
import { toast } from "react-toastify";
import { useContractLoader, useAppSelector, useAppDispatch, useUserSigner } from "../hooks";
-import { PlotMap, ProgressBar, PlotDetail, LogoDisplay, Header } from "../components";
+import { PlotMap, PlotDetail, LogoDisplay, Header } from "../components";
import { setPlots } from "../actions";
import { PlotTabs } from "../components";
import { Plot } from "../models/Plot";
@@ -13,6 +13,7 @@ import { fetchedPlots, setCommunalLand, setParcelGeojson } from "../actions/plot
import { fetchMetadata } from "../data";
import updatePlots from "../helpers/UpdatePlots";
import { setWhitelistedAmount } from "../actions/userSlice";
+import { ParcelInfoContainer } from "../containers";
interface Props {
networkProvider: any;
@@ -127,7 +128,6 @@ export default function BrowsePlots({ networkProvider, web3Modal }: Props) {
return (
-
@@ -137,6 +137,7 @@ export default function BrowsePlots({ networkProvider, web3Modal }: Props) {
)}
+
{/* key prop is to cause rerendering whenever it changes */}