matthew
2 years ago
21 changed files with 471 additions and 73 deletions
@ -0,0 +1,3 @@
|
||||
export interface StaticPageProps<T>{ |
||||
props: T |
||||
} |
@ -0,0 +1,141 @@
|
||||
import { Doughnut, Line } from "react-chartjs-2" |
||||
import { format } from "date-fns" |
||||
import { useQuery } from "@blitzjs/rpc" |
||||
import { useState } from "react" |
||||
import { useTranslation } from "react-i18next" |
||||
import { ChartData } from "chart.js/auto" |
||||
|
||||
import getActualSitesState from "app/stateSites/queries/getActualSitesState" |
||||
import getHistoryOfSitesState from "app/stateSites/queries/getHistoryOfSitesState" |
||||
import { InfluxPeriod } from "services/modules/influxdb/types" |
||||
import { cn } from "app/core/helpers/common" |
||||
|
||||
import s from "./styles.module.css" |
||||
|
||||
interface HistoryOfStateItem { |
||||
value: number |
||||
time: string |
||||
} |
||||
|
||||
interface HistoryOfState { |
||||
all: HistoryOfStateItem[] |
||||
available: HistoryOfStateItem[] |
||||
} |
||||
|
||||
export interface StatePageProps { |
||||
actualState: Awaited<ReturnType<typeof getActualSitesState>> |
||||
historyOfState: HistoryOfState |
||||
} |
||||
|
||||
const availableSitesColor = "#08c" |
||||
const allSitesColor = "hsl(200 15% 81% / 1)" |
||||
|
||||
const getDohnutData = (data: number[]) => ({ |
||||
labels: ["Available", "All"], |
||||
datasets: [ |
||||
{ |
||||
data: data, |
||||
backgroundColor: [availableSitesColor, allSitesColor], |
||||
borderColor: [availableSitesColor, "rgba(0,0,0,0)"], |
||||
borderWidth: 1, |
||||
}, |
||||
], |
||||
}) |
||||
|
||||
const liteOptions = { |
||||
responsive: true, |
||||
plugins: { |
||||
legend: { |
||||
display: false, |
||||
}, |
||||
tooltip: { |
||||
intersect: false, |
||||
}, |
||||
}, |
||||
|
||||
scales: { |
||||
x: { |
||||
display: false, |
||||
}, |
||||
}, |
||||
} |
||||
export const getGraphData = ( |
||||
data: HistoryOfStateItem[], |
||||
label: string, |
||||
color: string |
||||
): ChartData<"line", number[], string> => ({ |
||||
labels: data.map((item) => format(new Date(item.time), "dd.MM.yy hh:mm")), |
||||
datasets: [ |
||||
{ |
||||
label, |
||||
data: data.map((item) => item.value), |
||||
backgroundColor: color, |
||||
borderColor: color, |
||||
tension: 0.3, |
||||
}, |
||||
], |
||||
}) |
||||
|
||||
interface HistoryItemProps { |
||||
data: ChartData<"line", number[], string> |
||||
title: string |
||||
} |
||||
|
||||
const HistoryItem = ({ data, title }: HistoryItemProps) => { |
||||
return ( |
||||
<div className={s.historyStateItem}> |
||||
<div>{title}</div> |
||||
<Line options={liteOptions} data={data} /> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
const State = ({ actualState, historyOfState: historyOfStatePreloaded }: StatePageProps) => { |
||||
const [historyPeriod, setHistoryPeriod] = useState(InfluxPeriod.D) |
||||
const { t } = useTranslation() |
||||
const [historyOfState] = useQuery(getHistoryOfSitesState, historyPeriod, { |
||||
suspense: false, |
||||
keepPreviousData: true, |
||||
initialData: historyOfStatePreloaded, |
||||
}) |
||||
console.log(historyPeriod, historyOfState) |
||||
return ( |
||||
<div className={s.root}> |
||||
<div className={s.title}>{t("state.title")}</div> |
||||
<div className={s.doughnutAvailable}> |
||||
<Doughnut |
||||
data={getDohnutData([actualState.availableDomainsCount, actualState.allDomainsCount])} |
||||
/> |
||||
</div> |
||||
<div className={s.actualStateWrapper}> |
||||
<span className={s.availableCount}>{actualState.availableDomainsCount}</span>{" "} |
||||
{t("state.siteOutOf")} <span className={s.allCount}>{actualState.allDomainsCount}</span> |
||||
<span className={s.areNowAvailable}>{t("state.areNowAvailable")}</span> |
||||
</div> |
||||
{historyOfState && ( |
||||
<div className={s.historyStateWrapper}> |
||||
<div className={s.historyStatePeriodsWrapper}> |
||||
{Object.values(InfluxPeriod).map((i) => ( |
||||
<button |
||||
onClick={() => setHistoryPeriod(i)} |
||||
className={cn(s.historyStatePeriodsItem, { [s.active]: historyPeriod === i })} |
||||
> |
||||
{i} |
||||
</button> |
||||
))} |
||||
</div> |
||||
<HistoryItem |
||||
title="All sites count" |
||||
data={getGraphData(historyOfState.all, "All sites count", availableSitesColor)} |
||||
/> |
||||
<HistoryItem |
||||
title="Available sites count" |
||||
data={getGraphData(historyOfState.available, "Available sites count", allSitesColor)} |
||||
/> |
||||
</div> |
||||
)} |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
export default State |
@ -0,0 +1 @@
|
||||
export { default } from "./State" |
@ -0,0 +1,67 @@
|
||||
.root { |
||||
/* margin-right: var(--logoWrapperWidth); */ |
||||
} |
||||
|
||||
.actualStateWrapper { |
||||
font-size: 20px; |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
.availableCount { |
||||
font-size: 100px; |
||||
margin-right: 10px; |
||||
color: var(--button_primary); |
||||
font-weight: bold; |
||||
} |
||||
.allCount { |
||||
margin: 0px 10px; |
||||
font-size: 30px; |
||||
color: var(--text_secondary); |
||||
font-weight: bold; |
||||
} |
||||
.areNowAvailable { |
||||
} |
||||
.doughnutAvailable { |
||||
width: 200px; |
||||
margin: auto; |
||||
} |
||||
|
||||
.title { |
||||
font-size: 40px; |
||||
text-align: center; |
||||
margin: auto; |
||||
margin-bottom: 20px; |
||||
/* font-weight: 500; */ |
||||
} |
||||
|
||||
.historyStateWrapper { |
||||
display: flex; |
||||
justify-content: space-around; |
||||
flex-wrap: wrap; |
||||
max-width: 1000px; |
||||
margin: auto; |
||||
} |
||||
.historyStateItem { |
||||
width: 400px; |
||||
} |
||||
|
||||
.historyStatePeriodsWrapper { |
||||
width: 100%; |
||||
display: flex; |
||||
justify-content: center; |
||||
margin-bottom: 10px; |
||||
} |
||||
|
||||
.historyStatePeriodsItem { |
||||
border-radius: 5px; |
||||
margin: 5px; |
||||
border: none; |
||||
background: rgba(0, 0, 0, 0.04); |
||||
cursor: pointer; |
||||
} |
||||
|
||||
.historyStatePeriodsItem.active { |
||||
background: var(--button_primary); |
||||
color: white; |
||||
} |
@ -0,0 +1,11 @@
|
||||
import db from "db" |
||||
|
||||
export default async function getActualSitesState() { |
||||
const allDomainsCount = await db.nftDomain.count() |
||||
const availableDomainsCount = await db.nftDomain.count({ where: { available: true } }) |
||||
|
||||
return { |
||||
allDomainsCount, |
||||
availableDomainsCount, |
||||
} |
||||
} |
@ -0,0 +1,6 @@
|
||||
import influxdb from "services/modules/influxdb" |
||||
import { InfluxPeriod } from "services/modules/influxdb/types" |
||||
|
||||
export default async function getActualSitesState(period: InfluxPeriod = InfluxPeriod.W) { |
||||
return await influxdb.getHistoryOfState(period) |
||||
} |
@ -0,0 +1,58 @@
|
||||
import { Suspense } from "react" |
||||
|
||||
import Layout from "app/core/layouts/Layout" |
||||
import getActualSitesState from "app/stateSites/queries/getActualSitesState" |
||||
import getHistoryOfSitesState from "app/stateSites/queries/getHistoryOfSitesState" |
||||
import { BlitzPage } from "@blitzjs/next" |
||||
import State from "app/core/pages/State" |
||||
import { gSP } from "app/blitz-server" |
||||
|
||||
import { ErrorBoundary } from "@blitzjs/next" |
||||
|
||||
import { |
||||
ServerSidePropsContext, |
||||
} from "app/core/contextProviders/serverSidePropsProvider" |
||||
import { StatePageProps } from "app/core/pages/State/State" |
||||
import { StaticPageProps } from "app/core/commonTypes" |
||||
|
||||
function ErrorFallback({ error, resetErrorBoundary }) { |
||||
return ( |
||||
<div role="alert"> |
||||
<p>Something went wrong:</p> |
||||
<pre>{error.message}</pre> |
||||
<button onClick={resetErrorBoundary}>Try again</button> |
||||
</div> |
||||
) |
||||
} |
||||
|
||||
const StatePage: BlitzPage<StatePageProps> = (props) => { |
||||
return ( |
||||
<ErrorBoundary |
||||
FallbackComponent={ErrorFallback} |
||||
onReset={() => { |
||||
// reset the state of your app so the error doesn't happen again
|
||||
}} |
||||
> |
||||
<ServerSidePropsContext.Provider value={props}> |
||||
<Layout title="State of TON Sites" withoutPaddings> |
||||
<Suspense fallback="Loading..."> |
||||
<State {...props} /> |
||||
</Suspense> |
||||
</Layout> |
||||
</ServerSidePropsContext.Provider> |
||||
</ErrorBoundary> |
||||
) |
||||
} |
||||
|
||||
export const getStaticProps = gSP(async ({ params, ctx }): StaticPageProps<StatePageProps> => { |
||||
const actualState = await getActualSitesState(); |
||||
const historyOfState = await getHistoryOfSitesState(); |
||||
return { |
||||
props: { |
||||
actualState, |
||||
historyOfState |
||||
}, |
||||
} |
||||
}) |
||||
|
||||
export default StatePage |
@ -0,0 +1,17 @@
|
||||
import path from "path" |
||||
import dotenv from "dotenv" |
||||
dotenv.config({ path: path.resolve(__dirname, "../.env.local") }) |
||||
|
||||
import { Ctx } from "blitz" |
||||
import db from "db" |
||||
|
||||
|
||||
import { InfluxPeriod } from "./modules/influxdb/types" |
||||
import influxdb from "./modules/influxdb" |
||||
|
||||
export default async function run(period:InfluxPeriod = InfluxPeriod.W) { |
||||
const res = await influxdb.getHistoryOfState(period); |
||||
console.log(res) |
||||
|
||||
} |
||||
run() |
@ -1,46 +1,51 @@
|
||||
import { fluxDuration } from "@influxdata/influxdb-client"; |
||||
import { influxBucket, influxHost, influxPointName } from "./constants"; |
||||
import { InfluxField, InfluxPeriod } from "./types"; |
||||
import { fluxDuration } from "@influxdata/influxdb-client" |
||||
import { influxBucket, influxHost, influxPointName } from "./constants" |
||||
import { InfluxField, InfluxPeriod } from "./types" |
||||
|
||||
export const influxQuery = (field: InfluxField, fetchPeriod: InfluxPeriod) =>{ |
||||
let start; |
||||
let period; |
||||
export const influxQuery = (field: InfluxField, fetchPeriod: InfluxPeriod) => { |
||||
let start |
||||
let period |
||||
|
||||
switch(fetchPeriod){
|
||||
case InfluxPeriod.H: |
||||
start ='-1h' |
||||
period = '6m' |
||||
case InfluxPeriod.D: |
||||
start ='-1d' |
||||
period = '144m' |
||||
case InfluxPeriod.W: |
||||
start ='-1w' |
||||
period = '1008m' |
||||
case InfluxPeriod.M: |
||||
start ='-1mo' |
||||
period = '3d' |
||||
case InfluxPeriod.Y: |
||||
start ='-1y' |
||||
period = '36d' |
||||
case InfluxPeriod.tenminute: |
||||
start ='10m' |
||||
period = '1m' |
||||
} |
||||
switch (fetchPeriod) { |
||||
case InfluxPeriod.H: |
||||
start = "-1h" |
||||
period = "6m" |
||||
break |
||||
case InfluxPeriod.D: |
||||
start = "-1d" |
||||
period = "144m" |
||||
break |
||||
// case InfluxPeriod.W:
|
||||
// start = "-1w"
|
||||
// period = "1008m"
|
||||
// break
|
||||
// case InfluxPeriod.M:
|
||||
// start = "-1mo"
|
||||
// period = "3d"
|
||||
// break
|
||||
// case InfluxPeriod.Y:
|
||||
// start = "-1y"
|
||||
// period = "36d"
|
||||
// break
|
||||
// case InfluxPeriod.tenminute:
|
||||
// start ='-10m'
|
||||
// period = '1m'
|
||||
} |
||||
|
||||
const influxPeriod = fluxDuration(period); |
||||
const influxStart = fluxDuration(start); |
||||
return `from(bucket: "${influxBucket}")
|
||||
const query = `from(bucket: "${influxBucket}")
|
||||
|> range(start: ${start}, stop: now()) |
||||
|> filter(fn: (r) => r["host"] == "${influxHost}") |
||||
|> filter(fn: (r) => r["_field"] == "${field}") |
||||
|> filter(fn: (r) => r["_measurement"] == "${influxPointName}") |
||||
|> aggregateWindow(every: ${period}, fn: mean, createEmpty: false) |
||||
|> yield(name: "mean")` |
||||
}
|
||||
|
||||
export const processInfluxResult = (res:unknown[]) => { |
||||
return res.map(i=>({ |
||||
value: i._value, |
||||
time: i._time |
||||
})) |
||||
} |
||||
return query |
||||
} |
||||
|
||||
export const processInfluxResult = (res: unknown[]) => { |
||||
return res.map((i) => ({ |
||||
value: i._value, |
||||
time: i._time, |
||||
})) |
||||
} |
||||
|
Loading…
Reference in new issue