Refactor: Bring open source to parity with SAAS

This commit is contained in:
Himanshu Dixit 2021-02-21 06:02:06 +05:30 committed by Himanshu DIxit
parent 3795aa059e
commit a1331536ca
34 changed files with 2975 additions and 3008 deletions

18
frontend/.babelrc Normal file
View File

@ -0,0 +1,18 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
"react-hot-loader/babel",
"@babel/plugin-proposal-class-properties"
],
"env": {
"production": {
"presets": [
"minify"
]
}
}
}

View File

@ -1,9 +1,21 @@
{ {
"name": "client", "name": "frontend",
"version": "0.1.0", "version": "1.0.0",
"private": true, "description": "",
"main": "webpack.config.js",
"scripts": {
"dev": "NODE_ENV=development webpack serve",
"start": "node scripts/start.js",
"build": "webpack --config=webpack.config.prod.js"
},
"author": "",
"license": "ISC",
"dependencies": { "dependencies": {
"@auth0/auth0-react": "^1.2.0",
"@babel/core": "7.12.3",
"@material-ui/core": "^4.0.0", "@material-ui/core": "^4.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.4.2",
"@svgr/webpack": "5.4.0",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0", "@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^12.1.10",
@ -18,41 +30,87 @@
"@types/redux": "^3.6.0", "@types/redux": "^3.6.0",
"@types/styled-components": "^5.1.4", "@types/styled-components": "^5.1.4",
"@types/vis": "^4.21.21", "@types/vis": "^4.21.21",
"@typescript-eslint/eslint-plugin": "^4.5.0",
"@typescript-eslint/parser": "^4.5.0",
"antd": "^4.8.0", "antd": "^4.8.0",
"axios": "^0.21.0", "axios": "^0.21.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.0",
"babel-loader": "8.1.0",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-minify": "^0.5.1",
"babel-preset-react-app": "^10.0.0",
"bfj": "^7.0.2",
"camelcase": "^6.1.0",
"case-sensitive-paths-webpack-plugin": "2.3.0",
"chart.js": "^2.9.4", "chart.js": "^2.9.4",
"css-loader": "4.3.0",
"d3": "^6.2.0", "d3": "^6.2.0",
"d3-array": "^2.8.0", "d3-array": "^2.8.0",
"d3-ease": "^2.0.0", "d3-ease": "^2.0.0",
"d3-flame-graph": "^3.1.1", "d3-flame-graph": "^3.1.1",
"d3-tip": "^0.9.1", "d3-tip": "^0.9.1",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
"eslint": "^7.11.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-testing-library": "^3.9.2",
"eslint-webpack-plugin": "^2.1.0",
"file-loader": "6.1.1",
"fs-extra": "^9.0.1",
"html-webpack-plugin": "5.1.0",
"identity-obj-proxy": "3.0.0",
"jest": "26.6.0",
"jest-circus": "26.6.0",
"jest-resolve": "26.6.0",
"jest-watch-typeahead": "0.6.1",
"material-ui-chip-input": "^2.0.0-beta.2", "material-ui-chip-input": "^2.0.0-beta.2",
"mini-css-extract-plugin": "0.11.3",
"optimize-css-assets-webpack-plugin": "5.0.4",
"pnp-webpack-plugin": "1.6.4",
"postcss-flexbugs-fixes": "4.2.1",
"postcss-loader": "3.0.0",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
"postcss-safe-parser": "5.0.2",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "17.0.0", "react": "17.0.0",
"react-app-polyfill": "^2.0.0",
"react-chartjs-2": "^2.11.1", "react-chartjs-2": "^2.11.1",
"react-chips": "^0.8.0", "react-chips": "^0.8.0",
"react-css-theme-switcher": "^0.1.6", "react-css-theme-switcher": "^0.1.6",
"react-dev-utils": "^11.0.0",
"react-dom": "17.0.0", "react-dom": "17.0.0",
"react-graph-vis": "^1.0.5", "react-graph-vis": "^1.0.5",
"react-modal": "^3.12.1",
"react-redux": "^7.2.2", "react-redux": "^7.2.2",
"react-refresh": "^0.8.3",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "4.0.0",
"react-vis": "^1.11.7", "react-vis": "^1.11.7",
"recharts": "^1.8.5", "recharts": "^1.8.5",
"redux": "^4.0.5", "redux": "^4.0.5",
"redux-thunk": "^2.3.0", "redux-thunk": "^2.3.0",
"resolve": "1.18.1",
"resolve-url-loader": "^3.1.2",
"sass-loader": "8.0.2",
"semver": "7.3.2",
"style-loader": "1.3.0",
"styled-components": "^5.2.1", "styled-components": "^5.2.1",
"terser-webpack-plugin": "4.2.3",
"ts-pnp": "1.2.0",
"typescript": "^4.0.5", "typescript": "^4.0.5",
"web-vitals": "^0.2.4" "url-loader": "4.1.1",
}, "web-vitals": "^0.2.4",
"scripts": { "webpack": "^5.23.0",
"start": "react-scripts start", "webpack-dev-server": "^3.11.2",
"build": "react-scripts build", "webpack-manifest-plugin": "2.2.0",
"test": "react-scripts test", "workbox-webpack-plugin": "5.1.4"
"eject": "react-scripts eject",
"storybook": "start-storybook -p 6006 -s public --no-dll",
"build-storybook": "build-storybook -s public --no-dll",
"prettify": "prettier --write ."
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@ -74,9 +132,14 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.12.3", "@babel/core": "^7.12.3",
"@babel/preset-typescript": "^7.12.7", "@babel/plugin-proposal-class-properties": "^7.12.13",
"@babel/plugin-syntax-jsx": "^7.12.13",
"@babel/preset-env": "^7.12.17",
"@babel/preset-react": "^7.12.13",
"@babel/preset-typescript": "^7.12.17",
"autoprefixer": "^9.0.0", "autoprefixer": "^9.0.0",
"babel-plugin-styled-components": "^1.12.0", "babel-plugin-styled-components": "^1.12.0",
"copy-webpack-plugin": "^7.0.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-csso": "^4.0.1", "gulp-csso": "^4.0.1",
"gulp-debug": "^4.0.0", "gulp-debug": "^4.0.0",
@ -86,6 +149,8 @@
"less-plugin-npm-import": "^2.1.0", "less-plugin-npm-import": "^2.1.0",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
"prettier": "2.2.1", "prettier": "2.2.1",
"react-is": "^17.0.1" "react-hot-loader": "^4.13.0",
"react-is": "^17.0.1",
"webpack-cli": "^4.5.0"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,46 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -2,6 +2,8 @@ import { Dispatch } from "redux";
import metricsAPI from "../api/metricsAPI"; import metricsAPI from "../api/metricsAPI";
import { GlobalTime } from "./global"; import { GlobalTime } from "./global";
import { ActionTypes } from "./types"; import { ActionTypes } from "./types";
import {Token} from "../utils/token";
import { toUTCEpoch } from "../utils/timeUtils";
export interface servicesListItem { export interface servicesListItem {
serviceName: string; serviceName: string;
@ -59,8 +61,8 @@ export interface getFilteredTraceMetricsAction {
export const getServicesList = (globalTime: GlobalTime) => { export const getServicesList = (globalTime: GlobalTime) => {
return async (dispatch: Dispatch) => { return async (dispatch: Dispatch) => {
let request_string = let request_string = "services?start=" + globalTime.minTime + "&end=" + globalTime.maxTime;
"services?start=" + globalTime.minTime + "&end=" + globalTime.maxTime;
const response = await metricsAPI.get<servicesListItem[]>(request_string); const response = await metricsAPI.get<servicesListItem[]>(request_string);
dispatch<getServicesListAction>({ dispatch<getServicesListAction>({
@ -123,9 +125,9 @@ export const getFilteredTraceMetrics = (
return async (dispatch: Dispatch) => { return async (dispatch: Dispatch) => {
let request_string = let request_string =
"spans/aggregates?start=" + "spans/aggregates?start=" +
globalTime.minTime + toUTCEpoch( globalTime.minTime) +
"&end=" + "&end=" +
globalTime.maxTime + toUTCEpoch(globalTime.maxTime) +
"&" + "&" +
filter_params; filter_params;
const response = await metricsAPI.get<customMetricsItem[]>(request_string); const response = await metricsAPI.get<customMetricsItem[]>(request_string);

View File

@ -2,6 +2,7 @@ import { ActionTypes } from "./types";
import tracesAPI from "../api/tracesAPI"; import tracesAPI from "../api/tracesAPI";
import { Dispatch } from "redux"; import { Dispatch } from "redux";
import { GlobalTime } from "./global"; import { GlobalTime } from "./global";
import { toUTCEpoch } from "../utils/timeUtils";
// PNOTE // PNOTE
// define trace interface - what it should return // define trace interface - what it should return
@ -121,9 +122,9 @@ export const fetchTraces = (globalTime: GlobalTime, filter_params: string) => {
if (globalTime) { if (globalTime) {
let request_string = let request_string =
"spans?limit=100&lookback=2d&start=" + "spans?limit=100&lookback=2d&start=" +
globalTime.minTime + toUTCEpoch(globalTime.minTime) +
"&end=" + "&end=" +
globalTime.maxTime + toUTCEpoch(globalTime.maxTime) +
"&" + "&" +
filter_params; filter_params;
const response = await tracesAPI.get<traceResponseNew>(request_string); const response = await tracesAPI.get<traceResponseNew>(request_string);

View File

@ -2,6 +2,7 @@ import { Dispatch } from "redux";
import metricsAPI from "../api/metricsAPI"; import metricsAPI from "../api/metricsAPI";
import { ActionTypes } from "./types"; import { ActionTypes } from "./types";
import { GlobalTime } from "./global"; import { GlobalTime } from "./global";
import { toUTCEpoch } from "../utils/timeUtils";
export interface usageDataItem { export interface usageDataItem {
timestamp: number; timestamp: number;
@ -13,14 +14,9 @@ export interface getUsageDataAction {
payload: usageDataItem[]; payload: usageDataItem[];
} }
export const getUsageData = (globalTime: GlobalTime) => { export const getUsageData = (minTime: number, maxTime: number, step: number, service: string) => {
return async (dispatch: Dispatch) => { return async (dispatch: Dispatch) => {
let request_string = let request_string = `usage?start=${toUTCEpoch(minTime)}&end=${toUTCEpoch(maxTime)}&step=${step}&service=${service ? service : ""}`;
"usage?start=" +
globalTime.minTime +
"&end=" +
globalTime.maxTime +
"&step=3600&service=driver";
//Step can only be multiple of 3600 //Step can only be multiple of 3600
const response = await metricsAPI.get<usageDataItem[]>(request_string); const response = await metricsAPI.get<usageDataItem[]>(request_string);

View File

@ -1,7 +1,8 @@
import axios from "axios"; import axios from "axios";
import { ENVIRONMENT } from "../constants/env"; import { ENVIRONMENT } from "../constants/env";
import {Token} from "../utils/token";
// No auth for the API // No auth for the API
export default axios.create({ export default axios.create({
baseURL: `${ENVIRONMENT.baseURL}/api/prom/api/v1`, baseURL: `${ENVIRONMENT.baseURL}/api/prom/api/v1`
}); });

View File

@ -2,5 +2,5 @@ import axios from "axios";
import { ENVIRONMENT } from "../constants/env"; import { ENVIRONMENT } from "../constants/env";
export default axios.create({ export default axios.create({
baseURL: `${ENVIRONMENT.baseURL}/api/v1/`, baseURL: `${ENVIRONMENT.baseURL}/api/v1/`
}); });

View File

@ -1,11 +1,12 @@
import axios from "axios"; import axios from "axios";
import { ENVIRONMENT } from "../constants/env"; import { ENVIRONMENT } from "../constants/env";
import {Token} from "../utils/token";
export default axios.create({ export default axios.create({
// baseURL: 'https://api.telegram.org/bot1518273960:AAHcgVvym9a0Qkl-PKiCI84X1VZaVbkTud0/', // baseURL: 'https://api.telegram.org/bot1518273960:AAHcgVvym9a0Qkl-PKiCI84X1VZaVbkTud0/',
// baseURL: 'http://104.211.113.204:8080/api/v1/', // baseURL: 'http://104.211.113.204:8080/api/v1/',
// baseURL: "/api/v1/", // baseURL: "/api/v1/",
baseURL: `${ENVIRONMENT.baseURL}/api/v1/`, baseURL: `${ENVIRONMENT.baseURL}/api/v1/`
}); });
//https://api.telegram.org/bot1518273960:AAHcgVvym9a0Qkl-PKiCI84X1VZaVbkTud0/sendMessage?chat_id=351813222&text=Hello%20there //https://api.telegram.org/bot1518273960:AAHcgVvym9a0Qkl-PKiCI84X1VZaVbkTud0/sendMessage?chat_id=351813222&text=Hello%20there

View File

@ -1,5 +1,6 @@
import axios from "axios"; import axios from "axios";
import { ENVIRONMENT } from "../constants/env"; import { ENVIRONMENT } from "../constants/env";
import {Token} from "../utils/token";
//import { format } from 'path'; //import { format } from 'path';
export default axios.create({ export default axios.create({
@ -7,5 +8,5 @@ export default axios.create({
// baseURL: process.env.QUERY_SERVICE_URL, // baseURL: process.env.QUERY_SERVICE_URL,
// console.log('in traces API', process.env.QUERY_SERVICE_URL) // console.log('in traces API', process.env.QUERY_SERVICE_URL)
// baseURL: "/api/v1/", // baseURL: "/api/v1/",
baseURL: `${ENVIRONMENT.baseURL}/api/v1/`, baseURL: `${ENVIRONMENT.baseURL}/api/v1/`
}); });

View File

@ -1,6 +1,9 @@
@import "~antd/dist/antd.dark.css"; @import "~antd/dist/antd.dark.css";
@import "~antd/dist/antd.compact.css"; @import "~antd/dist/antd.compact.css";
.ant-space-item{
margin-right: 0 !important;
}
/* #components-layout-demo-side .logo { /* #components-layout-demo-side .logo {
height: 32px; height: 32px;
margin: 16px; margin: 16px;

View File

@ -13,22 +13,24 @@ import {
BarChartOutlined, BarChartOutlined,
DeploymentUnitOutlined, DeploymentUnitOutlined,
AlignLeftOutlined, AlignLeftOutlined,
AppstoreOutlined,
SettingOutlined
} from "@ant-design/icons"; } from "@ant-design/icons";
import DateTimeSelector from "./DateTimeSelector"; import DateTimeSelector from "Src/components/DateTimeSelector";
import ShowBreadcrumbs from "./ShowBreadcrumbs"; import ShowBreadcrumbs from "Src/components/ShowBreadcrumbs";
import styled from "styled-components"; import styled from "styled-components";
const { Content, Footer, Sider } = Layout; const { Content, Footer, Sider } = Layout;
const ServiceMetrics = React.lazy(() => import("./metrics/ServiceMetricsDef")); const ServiceMetrics = React.lazy(() => import("Src/components/metrics/ServiceMetricsDef"));
const ServiceMap = React.lazy(() => import("./servicemap/ServiceMap")); const ServiceMap = React.lazy(() => import("Src/components/servicemap/ServiceMap"));
const TraceDetail = React.lazy(() => import("./traces/TraceDetail")); const TraceDetail = React.lazy(() => import("Src/components/traces/TraceDetail"));
const TraceGraph = React.lazy(() => import("./traces/TraceGraphDef")); const TraceGraph = React.lazy(() => import("Src/components/traces/TraceGraphDef"));
const UsageExplorer = React.lazy(() => import("./usage/UsageExplorerDef")); const UsageExplorer = React.lazy(() => import("Src/components/usage/UsageExplorerDef"));
const ServicesTable = React.lazy(() => import("./metrics/ServicesTableDef")); const ServicesTable = React.lazy(() => import("Src/components/metrics/ServicesTableDef"));
// const Signup = React.lazy(() => import('./Signup')); const Signup = React.lazy(() => import('Src/components/Signup'));
const SettingsPage = React.lazy(() => import("Src/components/settings/settingsPage"));
//PNOTE //PNOTE
//React. lazy currently only supports default exports. If the module you want to import uses named exports, you can create an intermediate module that reexports it as the default. This ensures that tree shaking keeps working and that you don't pull in unused components. //React. lazy currently only supports default exports. If the module you want to import uses named exports, you can create an intermediate module that reexports it as the default. This ensures that tree shaking keeps working and that you don't pull in unused components.
@ -91,7 +93,7 @@ const App = () => {
Metrics Metrics
</NavLink> </NavLink>
</Menu.Item> </Menu.Item>
<Menu.Item key="2" icon={<AlignLeftOutlined />}> <Menu.Item key="2" icon={<AlignLeftOutlined/>}>
<NavLink to="/traces" style={{ fontSize: 12, textDecoration: "none" }}> <NavLink to="/traces" style={{ fontSize: 12, textDecoration: "none" }}>
Traces Traces
</NavLink> </NavLink>
@ -112,16 +114,24 @@ const App = () => {
Usage Explorer Usage Explorer
</NavLink> </NavLink>
</Menu.Item> </Menu.Item>
<Menu.Item key="5" icon={<SettingOutlined />}>
<NavLink
to="/settings"
style={{ fontSize: 12, textDecoration: "none" }}
>
Settings
</NavLink>
</Menu.Item>
</Menu> </Menu>
</Sider> </Sider>
<Layout className="site-layout"> <Layout className="site-layout">
<Content style={{ margin: "0 16px" }}> <Content style={{ margin: "0 16px" }}>
<Row> <Row>
<Col span={20}> <Col span={16}>
<ShowBreadcrumbs /> <ShowBreadcrumbs />
</Col> </Col>
<Col span={4}> <Col span={8}>
<DateTimeSelector /> <DateTimeSelector />
</Col> </Col>
</Row> </Row>
@ -134,10 +144,11 @@ const App = () => {
<Route path="/service-map" component={ServiceMap} /> <Route path="/service-map" component={ServiceMap} />
<Route path="/traces" exact component={TraceDetail} /> <Route path="/traces" exact component={TraceDetail} />
<Route path="/traces/:id" component={TraceGraph} /> <Route path="/traces/:id" component={TraceGraph} />
<Route path="/settings" exact component={SettingsPage} />
<Route path="/usage-explorer" component={UsageExplorer} /> <Route path="/usage-explorer" component={UsageExplorer} />
<Route path="/" component={ServicesTable} /> <Route path="/" component={ServicesTable} />
<Route path="/application" exact component={ServicesTable} /> <Route path="/application" exact component={ServicesTable} />
{/* <Route path="/signup" component={Signup} /> */}
</Switch> </Switch>
</Suspense> </Suspense>
</Content> </Content>

View File

@ -1,31 +1,27 @@
import React, { Suspense, useState } from "react"; import React, { Suspense, useState } from "react";
import { Spin } from "antd"; import { Spin } from "antd";
import { import {
BrowserRouter as Router,
Route, Route,
Switch, Switch,
Redirect, Redirect
} from "react-router-dom"; } from "react-router-dom";
import Signup from "./Signup";
const Signup = React.lazy(() => import("./Signup")); const App = React.lazy(() => import("Src/components/App"));
const App = React.lazy(() => import("./App"));
const AppWrapper = () => { const AppWrapper = () => {
const [isUserAuthenticated, setIsUserAuthenticated] = useState(false);
console.log("other")
return ( return (
<Router basename="/"> <Suspense fallback={<Spin size="large"/>}>
<Suspense fallback={<Spin size="large" />}>
<Switch> <Switch>
<Route path="/signup" exact component={Signup} />
<Route path="/application" exact component={App} /> <Route path="/application" exact component={App} />
<Route path="/application/:servicename" component={App} /> <Route path="/application/:servicename" component={App} />
<Route path="/service-map" component={App} /> <Route path="/service-map" component={App} />
<Route path="/traces" exact component={App} /> <Route path="/traces" exact component={App} />
<Route path="/traces/:id" component={App} /> <Route path="/traces/:id" component={App} />
<Route path="/usage-explorer" component={App} /> <Route path="/usage-explorer" component={App} />
<Route path="/settings" component={App} />
<Route path="/signup" component={Signup} />
<Route <Route
path="/" path="/"
exact exact
@ -39,7 +35,6 @@ const AppWrapper = () => {
/> />
</Switch> </Switch>
</Suspense> </Suspense>
</Router>
); );
}; };

View File

@ -36,6 +36,8 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
const [startTime, setStartTime] = useState<moment.Moment | null>(null); const [startTime, setStartTime] = useState<moment.Moment | null>(null);
const [endTime, setEndTime] = useState<moment.Moment | null>(null); const [endTime, setEndTime] = useState<moment.Moment | null>(null);
const [refreshButtonHidden, setRefreshButtonHidden] = useState(false); const [refreshButtonHidden, setRefreshButtonHidden] = useState(false);
const [refreshText, setRefreshText] = useState("");
const [refreshButtonClick, setRefreshButtoClick] = useState(0);
const [form_dtselector] = Form.useForm(); const [form_dtselector] = Form.useForm();
const location = useLocation(); const location = useLocation();
const updateTimeOnQueryParamChange = ()=>{ const updateTimeOnQueryParamChange = ()=>{
@ -143,25 +145,37 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
}; };
const timeSinceLastRefresh = () => { const timeSinceLastRefresh = () => {
let timeDiffSec = Math.round( const currentTime = moment();
(Date.now() - Math.round(props.globalTime.maxTime / 1000000)) / 1000, const lastRefresh = moment(props.globalTime.maxTime / 1000000)
); const duration = moment.duration(currentTime.diff(lastRefresh));
//How will Refresh button get updated? Needs to be periodically updated via timer. const secondsDiff = Math.floor(duration.asSeconds());
// For now, not returning any text here const minutedDiff = Math.floor(duration.asMinutes());
// if (timeDiffSec < 60) const hoursDiff = Math.floor(duration.asHours())
// return timeDiffSec.toString()+' s';
// else if (timeDiffSec < 3600) if(hoursDiff>0){
// return Math.round(timeDiffSec/60).toString()+' min'; return `Last refresh - ${hoursDiff} hrs ago`
// else }else if(minutedDiff>0){
// return Math.round(timeDiffSec/3600).toString()+' hr'; return `Last refresh - ${minutedDiff} mins ago`
return null; }
return `Last refresh - ${secondsDiff} sec ago`
}; };
const handleRefresh = () => { const handleRefresh = () => {
setRefreshButtoClick(refreshButtonClick+1);
setMetricsTimeInterval(timeInterval); setMetricsTimeInterval(timeInterval);
}; };
useEffect(()=>{
setRefreshText("")
const interval = setInterval(()=>{
setRefreshText(timeSinceLastRefresh())
}, 2000)
return ()=>{
clearInterval(interval)
}
},[props.location,refreshButtonClick])
const options = [ const options = [
{ value: "custom", label: "Custom" }, { value: "custom", label: "Custom" },
{ value: "15min", label: "Last 15 min" }, { value: "15min", label: "Last 15 min" },
@ -178,6 +192,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
const inputLabeLToShow = startTime && endTime? (`${startTime.format("YYYY/MM/DD HH:mm")} - ${endTime.format("YYYY/MM/DD HH:mm")}`):timeInterval const inputLabeLToShow = startTime && endTime? (`${startTime.format("YYYY/MM/DD HH:mm")} - ${endTime.format("YYYY/MM/DD HH:mm")}`):timeInterval
return ( return (
<DateTimeWrapper> <DateTimeWrapper>
<Space style={{float: "right", display:"block"}}>
<Space> <Space>
<Form <Form
form={form_dtselector} form={form_dtselector}
@ -185,20 +200,24 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
initialValues={{ interval: "15min" }} initialValues={{ interval: "15min" }}
style={{ marginTop: 10, marginBottom: 10 }} style={{ marginTop: 10, marginBottom: 10 }}
> >
<FormItem></FormItem>
<Select onSelect={handleOnSelect} value={inputLabeLToShow}> <Select onSelect={handleOnSelect} value={inputLabeLToShow}>
{options.map(({ value, label }) => ( {options.map(({ value, label }) => (
<Option value={value}>{label}</Option> <Option value={value}>{label}</Option>
))} ))}
</Select> </Select>
<FormItem hidden={refreshButtonHidden} name="refresh_button"> <FormItem hidden={refreshButtonHidden} name="refresh_button">
<Button type="primary" onClick={handleRefresh}> <Button type="primary" onClick={handleRefresh}>
Refresh {timeSinceLastRefresh()} Refresh
</Button> </Button>
{/* if refresh time is more than x min, give a message? */}
</FormItem> </FormItem>
</Form> </Form>
</Space>
<Space style={{float: "right", display:"block", marginRight: 20, minHeight: 23, width: 200, textAlign: "right"}}>
{refreshText}
</Space>
<CustomDateTimeModal <CustomDateTimeModal
visible={customDTPickerVisible} visible={customDTPickerVisible}
onCreate={handleCustomDate} onCreate={handleCustomDate}

View File

@ -14,6 +14,7 @@ const breadcrumbNameMap: any = {
"/traces": "Traces", "/traces": "Traces",
"/service-map": "Service Map", "/service-map": "Service Map",
"/usage-explorer": "Usage Explorer", "/usage-explorer": "Usage Explorer",
"/settings": "Settings",
}; };
const ShowBreadcrumbs = withRouter((props) => { const ShowBreadcrumbs = withRouter((props) => {

View File

@ -0,0 +1,9 @@
const Test = () => {
return (
<div>
INSIDE PUBLIC CODE
</div>
)
}
export default Test;

View File

@ -0,0 +1,23 @@
import React, { ReactElement, useState } from "react";
import { Modal, Button } from 'antd';
export const CustomModal = ({title,children, isModalVisible, setIsModalVisible, footer, closable=true}:{
isModalVisible: boolean,
closable?: boolean,
setIsModalVisible: Function,
footer?: any,
title: string,
children: ReactElement
}) => {
return (
<>
<Modal title={title} visible={isModalVisible} footer={footer}
closable={closable}
>
{children}
</Modal>
</>
);
};

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react"; import React, { useEffect, useState } from "react";
import { Tabs, Card, Row, Col } from "antd"; import { Tabs, Card, Row, Col } from "antd";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { useParams, RouteComponentProps } from "react-router-dom"; import { useParams, RouteComponentProps } from "react-router-dom";
@ -55,6 +55,8 @@ const _ServiceMetrics = (props: ServicesMetricsProps) => {
props.history.push(`/traces?${urlParams.toString()}`); props.history.push(`/traces?${urlParams.toString()}`);
}; };
return ( return (
<Tabs defaultActiveKey="1"> <Tabs defaultActiveKey="1">
<TabPane tab="Application Metrics" key="1"> <TabPane tab="Application Metrics" key="1">

View File

@ -1,12 +1,13 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { Spin, Table } from "antd"; import { Button, Space, Spin, Table } from "antd";
import styled from "styled-components"; import styled from "styled-components";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { getServicesList, GlobalTime, servicesListItem } from "../../actions"; import { getServicesList, GlobalTime, servicesListItem } from "../../actions";
import { StoreState } from "../../reducers"; import { StoreState } from "../../reducers";
import { CustomModal } from "../common/Modal";
interface ServicesTableProps { interface ServicesTableProps {
servicesList: servicesListItem[]; servicesList: servicesListItem[];
@ -80,19 +81,37 @@ const columns = [
const _ServicesTable = (props: ServicesTableProps) => { const _ServicesTable = (props: ServicesTableProps) => {
const search = useLocation().search; const search = useLocation().search;
const time_interval = new URLSearchParams(search).get("time"); const time_interval = new URLSearchParams(search).get("time");
const [dataFetched, setDataFetched] = useState(false) const [initialDataFetch, setDataFetched] = useState(false)
useEffect(() => { const [errorObject, setErrorObject] = useState({message: "", isError: false});
/* const isEmptyServiceList = !initialDataFetch && props.servicesList.length === 0;
@Note - Change this from action to thunk const refetchFromBackend = isEmptyServiceList || errorObject.isError;
*/ const [skipOnboarding, setSkipOnboarding ] = useState(localStorage.getItem('skip_onboarding') === "true");
props.getServicesList(props.globalTime).then(()=>{
setDataFetched(true)
}).catch((e:string)=>{
alert(e)
});
}, [props.globalTime]);
if(!dataFetched){ const onContinueClick = ()=>{
localStorage.setItem('skip_onboarding', 'true');
setSkipOnboarding(true)
}
function getApiServiceData() {
props.getServicesList(props.globalTime).then(() => {
setDataFetched(true);
setErrorObject({ message: "", isError: false });
}).catch((e: string) => {
setErrorObject({ message: e, isError: true });
setDataFetched(true);
});
}
useEffect(getApiServiceData, [props.globalTime]);
useEffect(()=>{
if(props.servicesList.length > 1 ){
localStorage.removeItem('skip_onboarding') ;
}
refetchFromBackend && setTimeout(getApiServiceData, 50000)
}, [props.servicesList,errorObject])
if(!initialDataFetch){
return ( return (
<TableLoadingWrapper> <TableLoadingWrapper>
<Spin/> <Spin/>
@ -101,6 +120,34 @@ const _ServicesTable = (props: ServicesTableProps) => {
) )
} }
if(refetchFromBackend && !skipOnboarding){
return (
<CustomModal title={"Setup instrumentation"}
isModalVisible={true}
closable={false}
setIsModalVisible={()=>{}}
footer={[
<Button key="submit" type="primary" onClick={onContinueClick}>
Continue without instrumentation
</Button>,
]}
>
<div>
<iframe width="100%" height="265" src="https://www.youtube.com/embed/Ly34WBQ2640" frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen></iframe>
<div style={{margin: "20px 0"}}>
<Spin/>
</div>
<div>
No instrumentation data.<br/>
Please instrument your application as mentioned <a href={"https://signoz.io/docs/instrumentation/overview"} target={"_blank"}>here</a>
</div>
</div>
</CustomModal>
)
}
return ( return (
<Wrapper> <Wrapper>
<Table <Table
@ -108,6 +155,12 @@ const _ServicesTable = (props: ServicesTableProps) => {
columns={columns} columns={columns}
pagination={false} pagination={false}
/> />
{props.servicesList[0].numCalls === 0 && (
<Space style={{width: '100%', margin: "40px 0", justifyContent: "center"}}>
No applications present. Please add instrumentation (follow this
<a href={"https://signoz.io/docs/instrumentation/overview"} target={"_blank"} style={{marginLeft: 3}}>guide</a>)</Space>
)}
</Wrapper> </Wrapper>
); );
}; };

View File

@ -0,0 +1,72 @@
import React, { useEffect, useState } from "react";
import { Form, Input, Space } from "antd";
import { connect } from "react-redux";
import { Tooltip } from 'antd';
import { InfoCircleOutlined,EyeTwoTone,EyeInvisibleOutlined } from '@ant-design/icons';
import { StoreState } from "../../reducers";
import { useAuthenticationData } from "../../hooks/authentication";
import { TOKEN_DATE } from "../../../../../sass/frontend/src/constants/accessToken";
import { Alert } from 'antd';
interface SettingsPageProps {
}
const layout = {
labelCol: { span: 3 },
wrapperCol: { span: 6 },
};
const SettingsPage = (props: SettingsPageProps) => {
const [form] = Form.useForm();
useEffect(()=>{
form.setFieldsValue({
retention_period: "3 days"
});
})
return (
<React.Fragment>
<Form
{...layout}
name="basic"
initialValues={{ remember: true }}
style={{ marginLeft: 20}}
form={form}
>
<Form.Item
label="Retention Period"
name="retention_period"
rules={[{ required: false }]}
>
<Input style={{ marginLeft: 60}} disabled={true}/>
</Form.Item>
</Form>
<Space style={{ marginLeft: 60, marginTop: 48}}>
<Alert
message="Mail us at plans@signoz.io to change your plan or retention period"
type="info"
/>
</Space>
</React.Fragment>
);
};
const mapStateToProps = (
state: StoreState
): { } => {
return {};
};
export default connect(mapStateToProps, {
})(SettingsPage);

View File

@ -1,10 +1,11 @@
import React, { useEffect } from "react"; import React, { useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { Table } from "antd"; import { Space, Table } from "antd";
import { traceResponseNew, fetchTraces, pushDStree } from "../../actions"; import { traceResponseNew, fetchTraces, pushDStree } from "../../actions";
import { StoreState } from "../../reducers"; import { StoreState } from "../../reducers";
import { isOnboardingSkipped } from "../../utils/app";
interface TraceListProps { interface TraceListProps {
traces: traceResponseNew; traces: traceResponseNew;
@ -106,6 +107,12 @@ const _TraceList = (props: TraceListProps) => {
return <Table dataSource={dataSource} columns={columns} size="middle" />; return <Table dataSource={dataSource} columns={columns} size="middle" />;
} else { } else {
if(isOnboardingSkipped()){
return ( <Space style={{width: '100%', margin: "40px 0", justifyContent: "center"}}>
No spans found. Please add instrumentation (follow this
<a href={"https://signoz.io/docs/instrumentation/overview"} target={"_blank"} style={{marginLeft: 3}}>guide</a>)</Space>)
}
return <div> No spans found for given filter!</div>; return <div> No spans found for given filter!</div>;
} }
}; // end of renderTraces }; // end of renderTraces

View File

@ -1,24 +1,56 @@
import React, { useEffect } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { Bar } from "react-chartjs-2"; import { Bar } from "react-chartjs-2";
import { Card } from "antd"; import { Card, Form, Select, Space } from "antd";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { getUsageData, GlobalTime, usageDataItem } from "../../actions"; import { getServicesList, getUsageData, GlobalTime, servicesListItem, usageDataItem } from "../../actions";
import { StoreState } from "../../reducers"; import { StoreState } from "../../reducers";
import moment from "moment";
import { isOnboardingSkipped } from "../../utils/app";
const { Option } = Select;
interface UsageExplorerProps { interface UsageExplorerProps {
usageData: usageDataItem[]; usageData: usageDataItem[];
getUsageData: Function; getUsageData: Function;
getServicesList: Function;
globalTime: GlobalTime; globalTime: GlobalTime;
servicesList: servicesListItem[];
totalCount: number;
} }
const timeDaysOptions = [
{ value: 30, label: "Last 30 Days" },
{ value: 7, label: "Last week" },
{ value: 1, label: "Last day" }
];
const interval = [
{ value: 604800, chartDivideMultiplier: 1, label: "Weekly", applicableOn: [timeDaysOptions[0]] },
{ value: 86400, chartDivideMultiplier: 30,label: "Daily", applicableOn: [timeDaysOptions[0],timeDaysOptions[1]] },
{ value: 3600, chartDivideMultiplier: 10,label: "Hours", applicableOn: [timeDaysOptions[2],timeDaysOptions[1]] },,
];
const _UsageExplorer = (props: UsageExplorerProps) => { const _UsageExplorer = (props: UsageExplorerProps) => {
const [selectedTime, setSelectedTime] = useState(timeDaysOptions[1])
const [selectedInterval, setSelectedInterval] = useState(interval[2])
const [selectedService, setSelectedService] = useState<string>("")
useEffect(() => { useEffect(() => {
props.getUsageData(props.globalTime); if(selectedTime && selectedInterval) {
}, [props.globalTime]); const maxTime = new Date().getTime() * 1000000 ;
const minTime = (maxTime - (selectedTime.value * 24 * 3600000 * 1000000 ) );
props.getUsageData(minTime, maxTime, selectedInterval!.value, selectedService);
}
}, [selectedTime,selectedInterval,selectedService]);
useEffect(() => {
props.getServicesList(props.globalTime);
}, []);
const data = { const data = {
labels: props.usageData.map((s) => new Date(s.timestamp / 1000000)), labels: props.usageData.map((s) => moment(s.timestamp/1000000).format("MMM Do h a")),
datasets: [ datasets: [
{ {
label: "Span Count", label: "Span Count",
@ -30,6 +62,7 @@ const _UsageExplorer = (props: UsageExplorerProps) => {
], ],
}; };
const options = { const options = {
scales: { scales: {
yAxes: [ yAxes: [
@ -40,17 +73,6 @@ const _UsageExplorer = (props: UsageExplorerProps) => {
}, },
}, },
], ],
xAxes: [
{
type: "time",
// distribution: 'linear', // Bar graph doesn't take lineardistribution type?
ticks: {
beginAtZero: true,
fontSize: 10,
},
},
],
}, },
legend: { legend: {
display: false, display: false,
@ -60,7 +82,51 @@ const _UsageExplorer = (props: UsageExplorerProps) => {
return ( return (
<React.Fragment> <React.Fragment>
{/* PNOTE - TODO - Keep it in reponsive row column tab */} {/* PNOTE - TODO - Keep it in reponsive row column tab */}
<Card style={{ width: "50%", margin: 20 }} bodyStyle={{ padding: 20 }}> <Space style={{marginTop: 40, marginLeft: 20}}>
<Space>
<Select onSelect={(value, option)=>{
setSelectedTime(timeDaysOptions.filter((item)=>item.value == parseInt(value))[0])
}} value={selectedTime.label}>
{timeDaysOptions.map(({ value, label }) => (
<Option value={value}>{label}</Option>
))}
</Select>
</Space>
<Space>
<Select onSelect={(value)=>{
setSelectedInterval(interval.filter((item)=>item!.value === parseInt(value))[0])
}} value={selectedInterval!.label}>
{interval.filter((interval)=>interval!.applicableOn.includes(selectedTime)).map((item) => (
<Option value={item!.value}>{item!.label}</Option>
))}
</Select>
</Space>
<Space>
<Select onSelect={(value) => {
setSelectedService(value)
}} value={ selectedService || "All Services"}>
<Option value={""}>All Services</Option>
{props.servicesList.map((service)=><Option value={service.serviceName}>{service.serviceName}</Option>)}
</Select>
</Space>
{ isOnboardingSkipped() && props.totalCount === 0 ? (
<Space style={{width: '100%', margin: "40px 0", marginLeft: 20, justifyContent: "center"}}>
No spans found. Please add instrumentation (follow this
<a href={"https://signoz.io/docs/instrumentation/overview"} target={"_blank"} style={{marginLeft: 3}}>guide</a>)</Space>
): (
<Space style={{display: "block", marginLeft: 20, width:200}}>
{`Total count is ${props.totalCount}`}
</Space>
)
}
</Space>
<Card style={{ width: "90%", margin: 20 }} bodyStyle={{ padding: 20 }}>
<Bar data={data} options={options} /> <Bar data={data} options={options} />
</Card> </Card>
</React.Fragment> </React.Fragment>
@ -68,11 +134,16 @@ const _UsageExplorer = (props: UsageExplorerProps) => {
}; };
const mapStateToProps = ( const mapStateToProps = (
state: StoreState, state: StoreState
): { usageData: usageDataItem[]; globalTime: GlobalTime } => { ): { totalCount: number; globalTime: GlobalTime; servicesList: servicesListItem[]; usageData: usageDataItem[]; } => {
return { usageData: state.usageDate, globalTime: state.globalTime }; let totalCount = 0;
for(let item of state.usageDate){
totalCount = totalCount+item.count;
}
return {totalCount:totalCount, usageData: state.usageDate, globalTime: state.globalTime, servicesList: state.servicesList };
}; };
export const UsageExplorer = connect(mapStateToProps, { export const UsageExplorer = connect(mapStateToProps, {
getUsageData: getUsageData, getUsageData: getUsageData,
getServicesList: getServicesList
})(_UsageExplorer); })(_UsageExplorer);

View File

@ -0,0 +1,7 @@
export const WITHOUT_SESSION_PATH = [
"/redirect"
]
export const AUTH0_REDIRECT_PATH = "/redirect";
export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = "/application";

View File

@ -1,4 +0,0 @@
export const ENVIRONMENT = {
baseURL: "",
// baseURL: "http://104.211.113.204:8080",
};

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title data-react-helmet="true">Open source Observability platform | SigNoz</title><meta data-react-helmet="true" property="og:title" content="Open source Observability platform | SigNoz"><meta data-react-helmet="true" name="description" content="SigNoz is an opensource observability platform to help you find issues in your deployed applications &amp; solve them quickly. It provides an integrated UI for metrics and traces with deep filtering and aggregation to pin down specific issues very quickly. Built on Kafka and Druid, it is designed to handle enterprise scale."><meta data-react-helmet="true" property="og:description" content="SigNoz is an opensource observability platform to help you find issues in your deployed applications &amp; solve them quickly. It provides an integrated UI for metrics and traces with deep filtering and aggregation to pin down specific issues very quickly. Built on Kafka and Druid, it is designed to handle enterprise scale."><meta data-react-helmet="true" property="og:image" content="https://signoz.io/img/HeroShot-3.jpg"><meta data-react-helmet="true" name="twitter:image" content="https://signoz.io/img/HeroShot-3.jpg"><meta data-react-helmet="true" name="twitter:image:alt" content="Image for Open source Observability platform | SigNoz"><meta data-react-helmet="true" name="twitter:card" content="summary_large_image"><meta data-react-helmet="true" name="docusaurus_locale" content="en"><meta data-react-helmet="true" name="docusaurus_tag" content="default"><link data-react-helmet="true" rel="shortcut icon" href="/img/favicon.ico">
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous"
/>
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<!--
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Signoz</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@ -5,28 +5,33 @@ import { createStore, applyMiddleware, compose } from "redux";
import { ThemeSwitcherProvider } from "react-css-theme-switcher"; import { ThemeSwitcherProvider } from "react-css-theme-switcher";
import thunk from "redux-thunk"; import thunk from "redux-thunk";
// import { NavLink, BrowserRouter as Router, Route, Switch } from 'react-router-dom'; // import { NavLink, BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Auth0Provider } from "@auth0/auth0-react";
import AppWrapper from "./components/AppWrapper"; import AppWrapper from "Src/components/AppWrapper";
import "./assets/index.css"; import "Src/assets/index.css";
import { reducers } from "./reducers"; import { reducers } from "./reducers";
import {BrowserRouter as Router} from "react-router-dom";
import { AUTH0_CLIENT_ID, AUTH0_DOMAIN } from "./constants/env";
// import Signup from './components/Signup'; // import Signup from './components/Signup';
// @ts-ignore // @ts-ignore
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, composeEnhancers(applyMiddleware(thunk))); const store = createStore(reducers, composeEnhancers(applyMiddleware(thunk)));
const themes = { const themes = {
dark: `${process.env.PUBLIC_URL}/dark-theme.css`, dark: `/dark-theme.css`,
light: `${process.env.PUBLIC_URL}/light-theme.css`, light: `/light-theme.css`,
}; };
ReactDOM.render( ReactDOM.render(
<Provider store={store}> <Provider store={store}>
<React.StrictMode> <React.StrictMode>
<ThemeSwitcherProvider themeMap={themes} defaultTheme="dark"> <ThemeSwitcherProvider themeMap={themes} defaultTheme="dark">
<Router basename="/">
<AppWrapper /> <AppWrapper />
{/* <App /> */} </Router>
</ThemeSwitcherProvider> </ThemeSwitcherProvider>
</React.StrictMode> </React.StrictMode>
</Provider>, </Provider>
,
document.querySelector("#root"), document.querySelector("#root"),
); );

View File

@ -0,0 +1 @@
export const isOnboardingSkipped = ()=>{return localStorage.getItem('skip_onboarding') === "true"}

View File

@ -0,0 +1,4 @@
export const toUTCEpoch = (time: number):number=>{
const x = new Date()
return (time + x.getTimezoneOffset()*60*1000);
}

View File

@ -0,0 +1,3 @@
export class Token{
static auth0Token = ""
};

View File

@ -0,0 +1,58 @@
// shared config (dev and prod)
const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
console.log(resolve(__dirname, './src/'));
module.exports = {
mode: "development",
devtool: "source-map",
entry: resolve(__dirname, "./src/index.tsx"),
devServer: {
historyApiFallback: true,
publicPath: "/",
transportMode: 'ws',
contentBase: [resolve(__dirname, "./public")],
hot: true,
liveReload: false,
inline:true,
port: 3000,
},
output: {
filename: "js/bundle.[chunkhash].min.js",
path: resolve(__dirname, "./build"),
publicPath: "/",
},
resolve: {
alias: {
Src: resolve(__dirname, './src/')
},
extensions: [".ts", ".tsx",".js", ".jsx"],
},
module: {
rules: [
{
test: [/\.jsx?$/, /\.tsx?$/],
use: ["babel-loader"],
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(scss|sass)$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
"file-loader?hash=sha512&digest=hex&name=img/[chunkhash].[ext]",
"image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false",
],
},
],
},
plugins: [new HtmlWebpackPlugin({ template: "src/index.html.ejs" })],
performance: {
hints: false,
},
};

View File

@ -0,0 +1,57 @@
// shared config (dev and prod)
const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "production",
devtool: "source-map",
entry: resolve(__dirname, "./src/index.tsx"),
output: {
filename: "js/bundle.[chunkhash].min.js",
path: resolve(__dirname, "./build"),
publicPath: "/",
},
resolve: {
alias: {
Src: resolve(__dirname, './src/'),
},
extensions: [".ts", ".tsx",".js", ".jsx"],
},
module: {
rules: [
{
test: [/\.jsx?$/, /\.tsx?$/],
use: ["babel-loader"],
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(scss|sass)$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
"file-loader?hash=sha512&digest=hex&name=img/[chunkhash].[ext]",
"image-webpack-loader?bypassOnDebug&optipng.optimizationLevel=7&gifsicle.interlaced=false",
],
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: "src/index.html.ejs" }),
new CopyPlugin({
patterns: [
{ from: resolve(__dirname, "public/"), to: "." },
],
})
],
performance: {
hints: false,
},
};

File diff suppressed because it is too large Load Diff