├── models
└── data.json
├── .eslintrc.json
├── jsconfig.json
├── instrumentation.js
├── app
├── country
│ └── [code]
│ │ ├── country.module.css
│ │ └── page.jsx
├── layout.jsx
├── page.jsx
├── nextar-dashboard
│ └── page.jsx
├── globals.css
└── nextar
│ └── page.jsx
├── components
├── page.module.css
├── CountryCard.module.css
├── Navigation.jsx
├── ActivityRow.jsx
├── CountryContainer.jsx
├── CountryCard.jsx
└── LineChart.jsx
├── server
├── controller.js
└── server.js
├── next.config.js
├── .gitignore
├── public
├── vercel.svg
└── next.svg
├── instrumentation.node.js
├── package.json
└── README.md
/models/data.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/instrumentation.js:
--------------------------------------------------------------------------------
1 | export async function register() {
2 | if (process.env.NEXT_RUNTIME === 'nodejs') {
3 | await import('./instrumentation.node.js')
4 | }
5 | }
--------------------------------------------------------------------------------
/app/country/[code]/country.module.css:
--------------------------------------------------------------------------------
1 | .countryContainer {
2 | display: flex;
3 | flex-direction: column;
4 | align-items: center;
5 | }
6 |
7 | .countryDetails {
8 |
9 | }
--------------------------------------------------------------------------------
/components/page.module.css:
--------------------------------------------------------------------------------
1 | .cardContainer {
2 | display: grid;
3 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
4 | margin: 3rem;
5 | padding: 3rem;
6 | gap: 16px;
7 | }
--------------------------------------------------------------------------------
/components/CountryCard.module.css:
--------------------------------------------------------------------------------
1 | .cardContainer {
2 | padding: 20px;
3 | border: 2px solid gray;
4 | border-radius: 10px;
5 | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
6 | background-color: #97cba9;
7 | text-align: center;
8 | }
--------------------------------------------------------------------------------
/server/controller.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const dashboardController = {};
3 |
4 | dashboardController.getTraceData = (req, res, next) => {
5 | try {
6 | res.sendFile(path.join(__dirname, './models/data.json'))
7 |
8 | }
9 | catch(err) {
10 | return next(err)
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/components/Navigation.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from 'next/link';
3 |
4 | const Navigation = () => {
5 | return (
6 |
14 | )
15 | }
16 |
17 | export default Navigation;
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | // next.config.js
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | images: {
6 | domains: ['flagsapi.com'], // Add 'flagsapi.com' to the list of allowed domains
7 | },
8 | env: {
9 | timezoneKey: '02f249b24b414f5388a66cbd0152c747'
10 | },
11 | experimental: {
12 | instrumentationHook: true
13 | }
14 | };
15 |
16 | module.exports = nextConfig;
17 |
--------------------------------------------------------------------------------
/components/ActivityRow.jsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import React from 'react'
3 |
4 | const ActivityRow = (props) => {
5 | const {requestName, origin, httpMethod, httpStatus } = props.result
6 | console.log('this is propsresult', props.result)
7 | return (
8 |
9 | | {requestName} |
10 | {httpMethod} |
11 | {httpStatus} |
12 | {origin} |
13 |
14 | )
15 | }
16 |
17 | export default ActivityRow
--------------------------------------------------------------------------------
/app/layout.jsx:
--------------------------------------------------------------------------------
1 | import './globals.css'
2 | import Navigation from '@components/Navigation.jsx'
3 |
4 | export const metadata = {
5 | title: 'GLOBAL CITIZEN',
6 | description: 'Demo application for Nextar',
7 | }
8 |
9 | const RootLayout = ({ children }) => {
10 | return (
11 |
12 |
13 |
14 |
15 | {children}
16 |
17 |
18 |
19 | )
20 | }
21 |
22 | export default RootLayout;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/instrumentation.node.js:
--------------------------------------------------------------------------------
1 | import { NodeSDK } from '@opentelemetry/sdk-node'
2 | import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
3 | import { Resource } from '@opentelemetry/resources'
4 | import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
5 | import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
6 |
7 | const exporter = new OTLPTraceExporter({
8 | 'url': 'http://localhost:8000/postTraceData'
9 | })
10 |
11 | const sdk = new NodeSDK({
12 | resource: new Resource({
13 | [SemanticResourceAttributes.SERVICE_NAME]: 'next-app',
14 | }),
15 | spanProcessor: new SimpleSpanProcessor(exporter),
16 | })
17 | sdk.start()
--------------------------------------------------------------------------------
/components/CountryContainer.jsx:
--------------------------------------------------------------------------------
1 |
2 | import CountryCard from '@components/CountryCard'
3 | import styles from './page.module.css'
4 |
5 |
6 | const getCountries = async() => {
7 | const response = await fetch('https://restcountries.com/v3.1/all');
8 | const countries = await response.json();
9 | return countries;
10 | }
11 |
12 |
13 | const CountryContainer = async() => {
14 |
15 | const countries = await getCountries();
16 |
17 | return (
18 |
19 | {countries.map((country, index) => (
20 |
21 | ))}
22 |
23 | )
24 | }
25 |
26 | export default CountryContainer
--------------------------------------------------------------------------------
/components/CountryCard.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Image from 'next/image'
3 | import styles from './CountryCard.module.css'
4 | import Link from 'next/link'
5 |
6 | const CountryCard = (props) => {
7 | const { name, capital, region, subregion, languages, latlng, maps, flags, cca2} = props.details
8 | const coordinates = latlng.join(', ')
9 |
10 |
11 | return (
12 |
13 |
14 |
{name.common}
15 |
16 |
17 |
18 | )
19 | }
20 |
21 | export default CountryCard;
--------------------------------------------------------------------------------
/app/page.jsx:
--------------------------------------------------------------------------------
1 |
2 |
3 | import CountryContainer from '@components/CountryContainer'
4 | // import styles from './page.module.css'
5 | import {onCLS, onFID, onLCP} from 'web-vitals';
6 | import {performance, PerformanceObserver} from 'perf_hooks';
7 |
8 |
9 |
10 |
11 | const HomePage = () => {
12 |
13 | console.log('before po')
14 | setTimeout(()=>{
15 |
16 | const po = new PerformanceObserver((entryList) => {
17 | entryList.getEntries()
18 |
19 |
20 | }).observe({ type: "largest-contentful-paint", buffered: false })
21 | console.log(po)
22 |
23 | // po.observe({ type: "largest-contentful-paint", buffered: true });
24 |
25 | },5000)
26 |
27 | return (
28 |
29 |
30 |
31 | )
32 | }
33 |
34 | export default HomePage
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextar_demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "nextar": "nodemon --require ./server/server.js & next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@opentelemetry/exporter-trace-otlp-http": "^0.41.2",
13 | "@opentelemetry/resources": "^1.15.2",
14 | "@opentelemetry/sdk-node": "^0.41.2",
15 | "@opentelemetry/sdk-trace-node": "^1.15.2",
16 | "@opentelemetry/semantic-conventions": "^1.15.2",
17 | "cors": "^2.8.5",
18 | "chart.js": "^4.4.0",
19 | "dotenv": "^16.3.1",
20 | "eslint": "8.47.0",
21 | "eslint-config-next": "13.4.19",
22 | "express": "^4.18.2",
23 | "next": "13.4.19",
24 | "nodemon": "^3.0.1",
25 | "react": "18.2.0",
26 | "react-dom": "18.2.0",
27 | "web-vitals": "^3.4.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | ```
14 |
15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16 |
17 | You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
18 |
19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const fs = require('fs');
4 | const path = require('path');
5 | const cors = require('cors');
6 | const PORT = 8000;
7 | // const dashboardController = require('./controller')
8 |
9 | app.use(cors());
10 | app.use(express.json());
11 |
12 | // post request from tracing
13 | app.post('/postTraceData', (req, res, next) => {
14 | console.log('POSTING!', req.body)
15 |
16 | fs.readFile(path.join(__dirname, '../models/data.json'), (err, data) => {
17 | const json = JSON.parse(data)
18 | json.push(req.body)
19 | fs.writeFile(path.join(__dirname, '../models/data.json'), JSON.stringify(json), (err) => {
20 | if(err) {
21 | return next(err)
22 | } else res.status(200).send('Successful!')
23 | })
24 | })
25 | // fs.appendFile(path.join(__dirname,'../models/data.json'),JSON.stringify(req.body), (err) => {
26 | // if (err) {
27 | // return next(err)
28 | // } else res.status(200).send('Successful')
29 | // })
30 | })
31 |
32 | // get request from dashboard
33 | app.get('/traceData', (req, res, next) => {
34 | console.log('GETTING')
35 | res.sendFile(path.join(__dirname, '../models/data.json'), (err) => {
36 | if(err) {
37 | console.log(err)
38 | return next(err)
39 | }
40 | })
41 | })
42 |
43 | // global error handler
44 | app.use((err, req, res, next) => {
45 | const defaultErr = {
46 | log: 'Express error handler caught unknown middleware error',
47 | status: 400,
48 | message: { err: 'An error occurred' },
49 | };
50 | const errorObj = Object.assign({}, defaultErr, err);
51 | console.log(errorObj.log);
52 | return res.status(errorObj.status).json(errorObj.message);
53 | });
54 |
55 |
56 | app.listen(PORT, function (err) {
57 | if (err) console.log(err);
58 | console.log("Server listening on PORT", PORT);
59 | });
--------------------------------------------------------------------------------
/app/nextar-dashboard/page.jsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import React, { useState, useEffect } from 'react'
3 | import ActivityRow from '@components/ActivityRow.jsx'
4 |
5 |
6 | const Dashboard = () => {
7 | const [data, setData] = useState([]);
8 |
9 | useEffect(() => {
10 | const interval = setInterval(async() => {
11 | try {
12 | console.log('fetching data')
13 | const results = [];
14 | const response = await fetch('http://localhost:8000/traceData');
15 | const traces = await response.json();
16 |
17 | for (let trace of traces) {
18 | const result = {};
19 | result.requestName = trace.resourceSpans[0].scopeSpans[0].spans[0].attributes[3].value.stringValue
20 | result.origin = trace.resourceSpans[0].scopeSpans[0].spans[0].kind
21 | result.startTime = trace.resourceSpans[0].scopeSpans[0].spans[0].startTimeUnixNano
22 | result.endTime = trace.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano
23 | result.httpMethod = trace.resourceSpans[0].scopeSpans[0].spans[0].attributes[2].value.stringValue
24 | result.httpStatus = trace.resourceSpans[0].scopeSpans[0].spans[0].attributes[4].value.intValue
25 | results.push(result)
26 | }
27 | setData(() => [...results])
28 | }
29 | catch(error) {
30 | console.log(error)
31 | }
32 | }, 10000)
33 | console.log('data', data)
34 | return () => clearInterval(interval)
35 | }, [data])
36 |
37 |
38 | return (
39 |
40 |
41 |
42 |
43 | | Resource |
44 | Method |
45 | Status |
46 | Kind |
47 | Duration |
48 |
49 | {data.map((datem, index) => (
50 |
51 | ))}
52 |
53 |
54 |
55 | )
56 | }
57 |
58 | export default Dashboard;
--------------------------------------------------------------------------------
/app/country/[code]/page.jsx:
--------------------------------------------------------------------------------
1 | import styles from './country.module.css'
2 |
3 | const getCountryDetails = async(code) => {
4 | const response = await fetch(`https://restcountries.com/v3.1/alpha/${code}`)
5 | const data = await response.json();
6 | return data;
7 | }
8 |
9 | const getTime = async(coord) => {
10 | const coordinates = coord.join(',')
11 | try {
12 | const response = await fetch(`https://timezone.abstractapi.com/v1/current_time?api_key=02f249b24b414f5388a66cbd0152c747&location=${coordinates}`, { cache: 'no-store'})
13 | const data = await response.json();
14 |
15 | const date = new Date(data.datetime)
16 | const hours = date.getHours();
17 | const mins = date.getMinutes();
18 | const meridiem = hours >= 12 ? 'pm' : 'am';
19 | const hour = hours % 12 || 12;
20 | const time = `${hour}:${mins < 10 ? '0': ''}${mins}${meridiem}`;
21 | return time;
22 | }
23 | catch (error) {
24 | console.log(error)
25 | }
26 | }
27 |
28 | const Country = async ({ params }) => {
29 | const arr = await getCountryDetails(params.code);
30 | const details = arr[0]
31 |
32 | let capitals;
33 | if (Array.isArray(details.capital)) {
34 | capitals = details.capital.join(', ')
35 | } else capitals = details.capital
36 |
37 | let language;
38 | if(details.languages) {
39 | let langArr = Object.values(details.languages)
40 | language = langArr.join(', ')
41 | } else language = 'N/A'
42 |
43 | let latlong;
44 | if(details.capitalInfo.latlng) latlong = details.capitalInfo.latlng;
45 | else latlong = details.latlng
46 |
47 | return (
48 |
49 |
{details.name.common}
50 |
51 |
52 |
Capital
53 |
{capitals}
54 |
55 |
Region
56 |
{details.region}
57 |
Subregion
58 |
{details.subregion}
59 |
60 |
Languages
61 |
{language}
62 |
63 |
64 |
Current Time
65 |
{getTime(latlong)}
66 |
67 |
68 |
69 | )
70 | }
71 |
72 | export default Country
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | /* :root {
2 | --max-width: 1100px;
3 | --border-radius: 12px;
4 | --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
5 | 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
6 | 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
7 |
8 | --foreground-rgb: 0, 0, 0;
9 | --background-start-rgb: 214, 219, 220;
10 | --background-end-rgb: 255, 255, 255;
11 |
12 | --primary-glow: conic-gradient(
13 | from 180deg at 50% 50%,
14 | #16abff33 0deg,
15 | #0885ff33 55deg,
16 | #54d6ff33 120deg,
17 | #0071ff33 160deg,
18 | transparent 360deg
19 | );
20 | --secondary-glow: radial-gradient(
21 | rgba(255, 255, 255, 1),
22 | rgba(255, 255, 255, 0)
23 | );
24 |
25 | --tile-start-rgb: 239, 245, 249;
26 | --tile-end-rgb: 228, 232, 233;
27 | --tile-border: conic-gradient(
28 | #00000080,
29 | #00000040,
30 | #00000030,
31 | #00000020,
32 | #00000010,
33 | #00000010,
34 | #00000080
35 | );
36 |
37 | --callout-rgb: 238, 240, 241;
38 | --callout-border-rgb: 172, 175, 176;
39 | --card-rgb: 180, 185, 188;
40 | --card-border-rgb: 131, 134, 135;
41 | }
42 |
43 | @media (prefers-color-scheme: dark) {
44 | :root {
45 | --foreground-rgb: 255, 255, 255;
46 | --background-start-rgb: 0, 0, 0;
47 | --background-end-rgb: 0, 0, 0;
48 |
49 | --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
50 | --secondary-glow: linear-gradient(
51 | to bottom right,
52 | rgba(1, 65, 255, 0),
53 | rgba(1, 65, 255, 0),
54 | rgba(1, 65, 255, 0.3)
55 | );
56 |
57 | --tile-start-rgb: 2, 13, 46;
58 | --tile-end-rgb: 2, 5, 19;
59 | --tile-border: conic-gradient(
60 | #ffffff80,
61 | #ffffff40,
62 | #ffffff30,
63 | #ffffff20,
64 | #ffffff10,
65 | #ffffff10,
66 | #ffffff80
67 | );
68 |
69 | --callout-rgb: 20, 20, 20;
70 | --callout-border-rgb: 108, 108, 108;
71 | --card-rgb: 100, 100, 100;
72 | --card-border-rgb: 200, 200, 200;
73 | }
74 | }
75 |
76 | * {
77 | box-sizing: border-box;
78 | padding: 0;
79 | margin: 0;
80 | }
81 |
82 | html,
83 | body {
84 | max-width: 100vw;
85 | overflow-x: hidden;
86 | }
87 |
88 |
89 |
90 | a {
91 | color: inherit;
92 | text-decoration: none;
93 | }
94 |
95 | @media (prefers-color-scheme: dark) {
96 | html {
97 | color-scheme: dark;
98 | }
99 | } */
--------------------------------------------------------------------------------
/app/nextar/page.jsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import Link from 'next/link'
4 | // import {performance, PerformanceObserver} from 'perf_hooks';
5 | import Pic from '../../public/next.svg'
6 | import Image from 'next/image'
7 | // import { useState, useEffect, useRef } from "react";
8 | import LineChart from '@components/LineChart'
9 |
10 | export default function Nextar() {
11 |
12 | // FCP CREATOR
13 | const fcp = Math.round(JSON.parse(localStorage.getItem('web-vitals-extension-metrics')).fcp.value)
14 | console.log('test',fcp)
15 |
16 | const fcpArray = JSON.parse(localStorage.getItem('FCParray'));
17 |
18 | let newFCPArray;
19 | if(fcpArray){
20 | newFCPArray = [...fcpArray, fcp];
21 | } else {
22 | newFCPArray = [fcp];
23 | }
24 |
25 | localStorage.setItem('FCParray', JSON.stringify(newFCPArray.sort((a,b)=> a-b)));
26 |
27 |
28 | const FCPindex = Math.ceil(fcpArray.length*0.75);
29 | const FCP75 = newFCPArray.sort((a,b)=> a-b)[FCPindex]
30 |
31 | // LCP CREATOR
32 | const lcp = Math.round(JSON.parse(localStorage.getItem('web-vitals-extension-metrics')).lcp.value)
33 | console.log('test',lcp)
34 |
35 | const lcpArray = JSON.parse(localStorage.getItem('LCParray'));
36 |
37 | let newLCPArray;
38 | if(lcpArray){
39 | newLCPArray = [...lcpArray, lcp];
40 | } else {
41 | newLCPArray = [lcp];
42 | }
43 |
44 | localStorage.setItem('LCParray', JSON.stringify(newLCPArray.sort((a,b)=> a-b)));
45 |
46 | const LCPindex = Math.ceil(lcpArray.length*0.75);
47 | const LCP75 = newLCPArray.sort((a,b)=> a-b)[LCPindex]
48 |
49 | // CLS CREATOR
50 | const cls = JSON.parse(localStorage.getItem('web-vitals-extension-metrics')).cls.value
51 | console.log('test',cls)
52 |
53 | const clsArray = JSON.parse(localStorage.getItem('CLSarray'));
54 |
55 | let newCLSArray;
56 | if(clsArray){
57 | newCLSArray = [...clsArray, cls];
58 | } else {
59 | newCLSArray = [cls];
60 | }
61 |
62 | localStorage.setItem('CLSarray', JSON.stringify(newCLSArray.sort((a,b)=> a-b)));
63 |
64 | const CLSindex = Math.ceil(clsArray.length*0.75);
65 | const CLS75 = (newCLSArray.sort((a,b)=> a-b)[CLSindex]).toFixed(3)
66 |
67 | // FID CREATOR
68 | const fid = JSON.parse(localStorage.getItem('web-vitals-extension-metrics')).fid.value
69 | console.log('test',fid)
70 |
71 | const fidArray = JSON.parse(localStorage.getItem('FIDarray'));
72 |
73 | let newFIDArray;
74 | if(fidArray){
75 | newFIDArray = [...fidArray, fid];
76 | } else {
77 | newFIDArray = [fid];
78 | }
79 |
80 | localStorage.setItem('FIDarray', JSON.stringify(newFIDArray.sort((a,b)=> a-b)));
81 |
82 |
83 | const FIDindex = Math.ceil(fidArray.length*0.75);
84 | const FID75 = (newFIDArray.sort((a,b)=> a-b)[FIDindex]).toFixed(2)
85 |
86 | console.log('test in nextar dash');
87 |
88 | // const [fid, setFID] = useState(0);
89 | // const [LCP, setLCP] = useState(0);
90 | // const [lcpArray, setLcpArray] = useState([]);
91 | // const [CLS, setCLS] = useState(0);
92 | // let clsArray = [];
93 |
94 | // useEffect(() => {
95 | // //LCP
96 | // new PerformanceObserver((entryList) => {
97 | // const entry = +(entryList.getEntries()[0].renderTime/1000).toFixed(3);
98 | // setLCP(entry);
99 |
100 | // const myArray = JSON.parse(localStorage.getItem('LCP array'));
101 |
102 | // let newLCPArray;
103 | // if(myArray){
104 | // newLCPArray = [...myArray, entry];
105 | // } else {
106 | // newLCPArray = [entry];
107 | // }
108 |
109 | // setLcpArray(newLCPArray);
110 |
111 | // localStorage.setItem('LCP array', JSON.stringify(newLCPArray));
112 | // }).observe({ type: "largest-contentful-paint", buffered: true });
113 |
114 | // // FID
115 | // new PerformanceObserver((entryList) => {
116 | // setFID(
117 | // entryList.getEntries()[0].processingStart -
118 | // entryList.getEntries()[0].startTime
119 | // );
120 | // localStorage.setItem('fid', Math.round(fid * 10) / 10);
121 | // }).observe({ type: 'first-input', buffered: true });
122 |
123 |
124 | // // CLS
125 | // new PerformanceObserver((entryList) => {
126 | // const entry = +entryList.getEntries()[0].value.toFixed(4);
127 | // setCLS(entry);
128 |
129 | // const clsArray = JSON.parse(localStorage.getItem('CLS array'));
130 |
131 | // let newCLSArray;
132 | // if(clsArray){
133 | // newCLSArray = [...clsArray, entry];
134 | // } else {
135 | // newCLSArray = [entry];
136 | // }
137 |
138 | // localStorage.setItem('CLS array', JSON.stringify(newCLSArray));
139 | // }).observe({ type: 'layout-shift', buffered: true });
140 | // }, []);
141 |
142 | return (
143 |
144 | Nextar Dashboard
145 | WEB VITALS
146 |
147 |
148 |
149 | SERVER NETWORK ACTIVITY
150 |
151 | {/* head */}
152 |
153 |
154 | | Endpoint |
155 | Type of Request |
156 | Response Status |
157 | Response Size |
158 | Start Time |
159 | Duration |
160 | __ |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 | )
174 | }
175 |
176 |
177 | //
178 | //
FCP
179 | //
{FCP75}
180 | //
181 |
182 | //
183 | //
LCP
184 | // {/*
{LCP} s
*/}
185 | //
186 |
187 | //
188 | //
CLS
189 | // {/*
{CLS}
*/}
190 | //
191 |
192 | //
193 | //
FID
194 | // {/*
{fid.toFixed(3)} ms
*/}
195 | //
196 |
197 | //
198 | //
199 | //
200 | //
201 | //
--------------------------------------------------------------------------------
/components/LineChart.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react';
2 | import {
3 | Chart,
4 | ChartConfiguration,
5 | LineController,
6 | LineElement,
7 | PointElement,
8 | LinearScale,
9 | Title,
10 | CategoryScale,
11 | Filler,
12 | Legend,
13 | } from 'chart.js';
14 |
15 | export default function LineChart(props) {
16 | useEffect(() => {
17 | Chart.register(
18 | CategoryScale,
19 | LineController,
20 | LineElement,
21 | PointElement,
22 | LinearScale,
23 | Title,
24 | Filler,
25 | Legend
26 | );
27 | const ctx = document.getElementById('myChart').getContext('2d');
28 | const myChart = new Chart(ctx, {
29 | type: 'line',
30 | data: {
31 | labels: JSON.parse(localStorage.LCParray).map((el, i) => i + 1),
32 | datasets: [
33 | {
34 | data: JSON.parse(localStorage.LCParray),
35 | label: 'Actual',
36 | borderColor: 'rgb(62,149,205)',
37 | backgroundColor: 'rgb(62,149,205,0.1)',
38 | // fill: true,
39 | },
40 | {
41 | data: JSON.parse(localStorage.LCParray).map((el) => 2500),
42 | label: 'Good',
43 | borderColor: 'rgb(60,186,159)',
44 | backgroundColor: 'rgb(60,186,159,0.1)',
45 | fill: true,
46 | },
47 | {
48 | data: JSON.parse(localStorage.LCParray).map((el) => 4000),
49 | label: 'Needs Improvement',
50 | borderColor: 'rgb(255,165,0)',
51 | backgroundColor: 'rgb(255,165,0,0.1)',
52 | fill: true,
53 | },
54 | {
55 | data: JSON.parse(localStorage.LCParray).map((el) =>
56 | Math.max(6000, ...JSON.parse(localStorage.LCParray))
57 | ),
58 | label: 'Poor',
59 | borderColor: 'rgb(196,88,80)',
60 | backgroundColor: 'rgb(196,88,80,0.1)',
61 | fill: true,
62 | },
63 | ],
64 | },
65 | options: {
66 | scales: {
67 | y: {
68 | suggestedMin: 0,
69 | suggestedMax: 400,
70 | },
71 | },
72 | },
73 | });
74 |
75 | const ctx1 = document.getElementById('CLSChart').getContext('2d');
76 | const CLSChart = new Chart(ctx1, {
77 | type: 'line',
78 | data: {
79 | labels: JSON.parse(localStorage.CLSarray).map((el, i) => i + 1),
80 | datasets: [
81 | {
82 | data: JSON.parse(localStorage.CLSarray),
83 | label: 'Actual',
84 | borderColor: 'rgb(62,149,205)',
85 | backgroundColor: 'rgb(62,149,205,0.1)',
86 | // fill: true,
87 | },
88 | {
89 | data: JSON.parse(localStorage.CLSarray).map((el) => 1.2),
90 | label: 'Good',
91 | borderColor: 'rgb(60,186,159)',
92 | backgroundColor: 'rgb(60,186,159,0.1)',
93 | fill: true,
94 | },
95 | {
96 | data: JSON.parse(localStorage.CLSarray).map((el) => 1.666),
97 | label: 'Needs Improvement',
98 | borderColor: 'rgb(255,165,0)',
99 | backgroundColor: 'rgb(255,165,0,0.1)',
100 | fill: true,
101 | },
102 | {
103 | data: JSON.parse(localStorage.CLSarray).map((el) =>
104 | Math.max(3.00, ...JSON.parse(localStorage.CLSarray))
105 | ),
106 | label: 'Poor',
107 | borderColor: 'rgb(196,88,80)',
108 | backgroundColor: 'rgb(196,88,80,0.1)',
109 | fill: true,
110 | },
111 | ],
112 | },
113 | options: {
114 | scales: {
115 | y: {
116 | suggestedMin: 0,
117 | suggestedMax: 0.5,
118 | },
119 | },
120 | },
121 | });
122 |
123 | const ctx2 = document.getElementById('FIDChart').getContext('2d');
124 | const FIDChart = new Chart(ctx2, {
125 | type: 'line',
126 | data: {
127 | labels: JSON.parse(localStorage.FIDarray).map((el, i) => i + 1),
128 | datasets: [
129 | {
130 | data: JSON.parse(localStorage.FIDarray),
131 | label: 'Actual',
132 | borderColor: 'rgb(62,149,205)',
133 | backgroundColor: 'rgb(62,149,205,0.1)',
134 | // fill: true,
135 | },
136 | {
137 | data: JSON.parse(localStorage.FIDarray).map((el) => 100),
138 | label: 'Good',
139 | borderColor: 'rgb(60,186,159)',
140 | backgroundColor: 'rgb(60,186,159,0.1)',
141 | fill: true,
142 | },
143 | {
144 | data: JSON.parse(localStorage.FIDarray).map((el) => 300),
145 | label: 'Needs Improvement',
146 | borderColor: 'rgb(255,165,0)',
147 | backgroundColor: 'rgb(255,165,0,0.1)',
148 | fill: true,
149 | },
150 | {
151 | data: JSON.parse(localStorage.FIDarray).map((el) =>
152 | Math.max(600, ...JSON.parse(localStorage.FIDarray))
153 | ),
154 | label: 'Poor',
155 | borderColor: 'rgb(196,88,80)',
156 | backgroundColor: 'rgb(196,88,80,0.1)',
157 | fill: true,
158 | },
159 | ],
160 | },
161 | options: {
162 | scales: {
163 | y: {
164 | suggestedMin: 0,
165 | suggestedMax: 0.5,
166 | },
167 | },
168 | },
169 | });
170 |
171 |
172 | const ctx3 = document.getElementById('FCPChart').getContext('2d');
173 | const FCPChart = new Chart(ctx3, {
174 | type: 'line',
175 | data: {
176 | labels: JSON.parse(localStorage.FCParray).map((el, i) => i + 1),
177 | datasets: [
178 | {
179 | data: JSON.parse(localStorage.FCParray),
180 | label: 'Actual',
181 | borderColor: 'rgb(62,149,205)',
182 | backgroundColor: 'rgb(62,149,205,0.1)',
183 | // fill: true,
184 | },
185 | {
186 | data: JSON.parse(localStorage.FCParray).map((el) => 1800),
187 | label: 'Good',
188 | borderColor: 'rgb(60,186,159)',
189 | backgroundColor: 'rgb(60,186,159,0.1)',
190 | fill: true,
191 | },
192 | {
193 | data: JSON.parse(localStorage.FCParray).map((el) => 3000),
194 | label: 'Needs Improvement',
195 | borderColor: 'rgb(255,165,0)',
196 | backgroundColor: 'rgb(255,165,0,0.1)',
197 | fill: true,
198 | },
199 | {
200 | data: JSON.parse(localStorage.FCParray).map((el) =>
201 | Math.max(5000, ...JSON.parse(localStorage.FCParray))
202 | ),
203 | label: 'Poor',
204 | borderColor: 'rgb(196,88,80)',
205 | backgroundColor: 'rgb(196,88,80,0.1)',
206 | fill: true,
207 | },
208 | ],
209 | },
210 | options: {
211 | scales: {
212 | y: {
213 | suggestedMin: 0,
214 | suggestedMax: 10,
215 | },
216 | },
217 | },
218 | });
219 | }, []);
220 |
221 | return (
222 | <>
223 | {/* Filled line chart */}
224 |
225 |
226 |
Largest Contentful Paint (LCP): {props.FCP75}ms (75th percentile)
227 |
228 | Cumulative Layout Shift (CLS): {props.CLS75} (75th percentile)
229 |
230 | First Input Delay (FID): {props.FID75}ms (75th percentile)
231 |
232 | First Contentful Paint (FCP): {props.FCP75}ms (75th percentile)
233 |
234 |
235 |
236 | >
237 | );
238 | }
239 |
--------------------------------------------------------------------------------