diff --git a/charts/helm/checkmate/templates/client-deployment.yaml b/charts/helm/checkmate/templates/client-deployment.yaml
index 6c1f70fd7..a5d487dc8 100644
--- a/charts/helm/checkmate/templates/client-deployment.yaml
+++ b/charts/helm/checkmate/templates/client-deployment.yaml
@@ -1,16 +1,16 @@
apiVersion: apps/v1
kind: Deployment
metadata:
- name: client
+ name: checkmate-client
spec:
replicas: 1
selector:
matchLabels:
- app: client
+ app: checkmate-client
template:
metadata:
labels:
- app: client
+ app: checkmate-client
spec:
containers:
- name: client
@@ -20,3 +20,13 @@ spec:
env:
- name: UPTIME_APP_API_BASE_URL
value: "{{ .Values.server.protocol }}://{{ .Values.server.ingress.host }}/api/v1"
+ - name: UPTIME_APP_CLIENT_HOST
+ value: "http://{{ .Values.client.ingress.host }}"
+ volumeMounts:
+ - name: config-volume
+ mountPath: /etc/nginx/conf.d/default.conf
+ subPath: default.conf
+ volumes:
+ - name: config-volume
+ configMap:
+ name: checkmate-server-nginx-cm
diff --git a/charts/helm/checkmate/templates/client-ingress.yaml b/charts/helm/checkmate/templates/client-ingress.yaml
index da059a863..89e001e15 100644
--- a/charts/helm/checkmate/templates/client-ingress.yaml
+++ b/charts/helm/checkmate/templates/client-ingress.yaml
@@ -2,10 +2,15 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
- name: client-ingress
+ name: checkmate-client-ingress
+ {{- if .Values.client.ingress.annotations }}
annotations:
- nginx.ingress.kubernetes.io/rewrite-target: /
+ {{- range $key, $value := .Values.client.ingress.annotations }}
+ {{ $key }}: {{ $value | quote }}
+ {{- end }}
+ {{- end }}
spec:
+ ingressClassName: {{ .Values.client.ingress.className }}
rules:
- host: {{ .Values.client.ingress.host }}
http:
@@ -14,7 +19,7 @@ spec:
pathType: Prefix
backend:
service:
- name: client
+ name: checkmate-client
port:
number: {{ .Values.client.port }}
{{- end }}
diff --git a/charts/helm/checkmate/templates/client-service.yaml b/charts/helm/checkmate/templates/client-service.yaml
index afb88188c..b56eaeedf 100644
--- a/charts/helm/checkmate/templates/client-service.yaml
+++ b/charts/helm/checkmate/templates/client-service.yaml
@@ -1,9 +1,9 @@
apiVersion: v1
kind: Service
metadata:
- name: client
+ name: checkmate-client
spec:
selector:
- app: client
+ app: checkmate-client
ports:
- port: {{ .Values.client.port }}
diff --git a/charts/helm/checkmate/templates/mongodb-deployment.yaml b/charts/helm/checkmate/templates/mongodb-deployment.yaml
deleted file mode 100644
index 4c906fe09..000000000
--- a/charts/helm/checkmate/templates/mongodb-deployment.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: mongodb
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: mongodb
- template:
- metadata:
- labels:
- app: mongodb
- spec:
- containers:
- - name: mongodb
- image: {{ .Values.mongodb.image }}
- ports:
- - containerPort: {{ .Values.mongodb.port }}
- command: ["mongod", "--quiet", "--bind_ip_all"]
- volumeMounts:
- - name: mongodb-data
- mountPath: /data/db
- volumes:
- - name: mongodb-data
- persistentVolumeClaim:
- claimName: mongodb-pvc
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/mongodb-pvc.yaml b/charts/helm/checkmate/templates/mongodb-pvc.yaml
deleted file mode 100644
index 435b662e6..000000000
--- a/charts/helm/checkmate/templates/mongodb-pvc.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: mongodb-pvc
-spec:
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: {{ .Values.persistence.mongodbSize }}
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/mongodb-service.yaml b/charts/helm/checkmate/templates/mongodb-service.yaml
index 2911dc3eb..b96fe1a82 100644
--- a/charts/helm/checkmate/templates/mongodb-service.yaml
+++ b/charts/helm/checkmate/templates/mongodb-service.yaml
@@ -1,9 +1,9 @@
apiVersion: v1
kind: Service
metadata:
- name: mongodb
+ name: checkmate-mongodb
spec:
selector:
- app: mongodb
+ app: checkmate-mongodb
ports:
- port: {{ .Values.mongodb.port }}
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/mongodb-statefulsets.yaml b/charts/helm/checkmate/templates/mongodb-statefulsets.yaml
new file mode 100644
index 000000000..1ffd1c228
--- /dev/null
+++ b/charts/helm/checkmate/templates/mongodb-statefulsets.yaml
@@ -0,0 +1,32 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: checkmate-mongodb
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: checkmate-mongodb
+ template:
+ metadata:
+ labels:
+ app: checkmate-mongodb
+ spec:
+ containers:
+ - name: mongodb
+ image: {{ .Values.mongodb.image }}
+ ports:
+ - containerPort: {{ .Values.mongodb.port }}
+ command: ["mongod", "--quiet", "--bind_ip_all"]
+ volumeMounts:
+ - name: checkmate-mongo-persistent-storage
+ mountPath: /data/db
+ volumeClaimTemplates:
+ - metadata:
+ name: checkmate-mongo-persistent-storage
+ spec:
+ storageClassName: "gp3"
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: {{ .Values.persistence.mongodbSize }}
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/redis-deployment.yaml b/charts/helm/checkmate/templates/redis-deployment.yaml
deleted file mode 100644
index cc9ffdaf9..000000000
--- a/charts/helm/checkmate/templates/redis-deployment.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: redis
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: redis
- template:
- metadata:
- labels:
- app: redis
- spec:
- containers:
- - name: redis
- image: {{ .Values.redis.image }}
- ports:
- - containerPort: {{ .Values.redis.port }}
- volumeMounts:
- - name: redis-data
- mountPath: /data
- volumes:
- - name: redis-data
- persistentVolumeClaim:
- claimName: redis-pvc
diff --git a/charts/helm/checkmate/templates/redis-pvc.yaml b/charts/helm/checkmate/templates/redis-pvc.yaml
deleted file mode 100644
index 2f0d81960..000000000
--- a/charts/helm/checkmate/templates/redis-pvc.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: redis-pvc
-spec:
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: {{ .Values.persistence.redisSize }}
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/redis-service.yaml b/charts/helm/checkmate/templates/redis-service.yaml
index f29fa46de..2720350da 100644
--- a/charts/helm/checkmate/templates/redis-service.yaml
+++ b/charts/helm/checkmate/templates/redis-service.yaml
@@ -1,9 +1,11 @@
+{{- if .Values.redis.enabled }}
apiVersion: v1
kind: Service
metadata:
- name: redis
+ name: checkmate-redis
spec:
selector:
- app: redis
+ app: checkmate-redis
ports:
- port: {{ .Values.redis.port }}
+{{- end }}
diff --git a/charts/helm/checkmate/templates/redis-statefulsets.yaml b/charts/helm/checkmate/templates/redis-statefulsets.yaml
new file mode 100644
index 000000000..252361730
--- /dev/null
+++ b/charts/helm/checkmate/templates/redis-statefulsets.yaml
@@ -0,0 +1,33 @@
+{{- if .Values.redis.enabled }}
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: checkmate-redis
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: checkmate-redis
+ template:
+ metadata:
+ labels:
+ app: checkmate-redis
+ spec:
+ containers:
+ - name: redis
+ image: {{ .Values.redis.image }}
+ ports:
+ - containerPort: {{ .Values.redis.port }}
+ volumeMounts:
+ - name: redis-data
+ mountPath: /data
+ volumeClaimTemplates:
+ - metadata:
+ name: checkmate-redis-persistent-storage
+ spec:
+ storageClassName: "gp3"
+ accessModes: ["ReadWriteOnce"]
+ resources:
+ requests:
+ storage: {{ .Values.persistence.redisSize }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/secrets.yaml b/charts/helm/checkmate/templates/secrets.yaml
index 0a3f1ace5..af104e7d1 100644
--- a/charts/helm/checkmate/templates/secrets.yaml
+++ b/charts/helm/checkmate/templates/secrets.yaml
@@ -4,10 +4,6 @@
{{- fail "secrets.JWT_SECRET must be overridden and cannot be 'change_me'" }}
{{- end }}
-{{- if or (not $secrets.REFRESH_TOKEN_SECRET) (eq $secrets.REFRESH_TOKEN_SECRET "change_me") }}
- {{- fail "secrets.REFRESH_TOKEN_SECRET must be overridden and cannot be 'change_me'" }}
-{{- end }}
-
apiVersion: v1
kind: Secret
metadata:
diff --git a/charts/helm/checkmate/templates/server-deployment.yaml b/charts/helm/checkmate/templates/server-deployment.yaml
index 6f21f86f3..8c9403916 100644
--- a/charts/helm/checkmate/templates/server-deployment.yaml
+++ b/charts/helm/checkmate/templates/server-deployment.yaml
@@ -1,16 +1,16 @@
apiVersion: apps/v1
kind: Deployment
metadata:
- name: server
+ name: checkmate-server
spec:
replicas: 1
selector:
matchLabels:
- app: server
+ app: checkmate-server
template:
metadata:
labels:
- app: server
+ app: checkmate-server
spec:
containers:
- name: server
@@ -20,3 +20,6 @@ spec:
envFrom:
- secretRef:
name: checkmate-secrets
+ env:
+ - name: CLIENT_HOST
+ value: "http://{{ .Values.client.ingress.host }}"
diff --git a/charts/helm/checkmate/templates/server-ingress.yaml b/charts/helm/checkmate/templates/server-ingress.yaml
index c1982d451..0d7ca867b 100644
--- a/charts/helm/checkmate/templates/server-ingress.yaml
+++ b/charts/helm/checkmate/templates/server-ingress.yaml
@@ -2,15 +2,22 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
- name: server-ingress
+ name: checkmate-server-ingress
+ {{- if .Values.server.ingress.annotations }}
annotations:
- nginx.ingress.kubernetes.io/rewrite-target: /
- nginx.ingress.kubernetes.io/enable-cors: "true"
- nginx.ingress.kubernetes.io/cors-allow-origin: "http://{{ .Values.client.ingress.host }},https://{{ .Values.client.ingress.host }}"
- nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS"
- nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
- nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
+ {{- range $key, $value := .Values.server.ingress.annotations }}
+ {{ $key }}: {{ $value | quote }}
+ {{- end }}
+ {{- end }}
+ {{/*#annotations:
+ #nginx.ingress.kubernetes.io/rewrite-target: /
+ #nginx.ingress.kubernetes.io/enable-cors: "true"
+ #nginx.ingress.kubernetes.io/cors-allow-origin: "http://{{ .Values.client.ingress.host }},https://{{ .Values.client.ingress.host }}"
+ #nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS"
+ #nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization"
+ #nginx.ingress.kubernetes.io/cors-allow-credentials: "true"*/}}
spec:
+ ingressClassName: {{ .Values.server.ingress.className }}
rules:
- host: {{ .Values.server.ingress.host }}
http:
@@ -19,7 +26,7 @@ spec:
pathType: Prefix
backend:
service:
- name: server
+ name: checkmate-server
port:
number: {{ .Values.server.port }}
{{- end }}
diff --git a/charts/helm/checkmate/templates/server-nginx-cm.yaml b/charts/helm/checkmate/templates/server-nginx-cm.yaml
new file mode 100644
index 000000000..84da55299
--- /dev/null
+++ b/charts/helm/checkmate/templates/server-nginx-cm.yaml
@@ -0,0 +1,41 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: checkmate-server-nginx-cm
+data:
+ default.conf: |
+ server {
+ listen 80;
+ listen [::]:80;
+
+ server_name checkmate-demo.bluewavelabs.ca;
+ server_tokens off;
+
+ location /.well-known/acme-challenge/ {
+ root /var/www/certbot;
+ }
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ try_files $uri $uri/ /index.html;
+ }
+
+ # location /api/ {
+ # proxy_pass http://{{ .Values.server.ingress.host }}:5000/api/;
+ # proxy_http_version 1.1;
+ # proxy_set_header Host $host;
+ # proxy_set_header X-Real-IP $remote_addr;
+ # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ # proxy_set_header X-Forwarded-Proto $scheme;
+ # }
+
+ location /api-docs/ {
+ proxy_pass http://{{ .Values.server.ingress.host }}:5000/api-docs/;
+ proxy_http_version 1.1;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+ }
\ No newline at end of file
diff --git a/charts/helm/checkmate/templates/server-service.yaml b/charts/helm/checkmate/templates/server-service.yaml
index 64494ff90..d8a7fbe86 100644
--- a/charts/helm/checkmate/templates/server-service.yaml
+++ b/charts/helm/checkmate/templates/server-service.yaml
@@ -1,9 +1,9 @@
apiVersion: v1
kind: Service
metadata:
- name: server
+ name: checkmate-server
spec:
selector:
- app: server
+ app: checkmate-server
ports:
- port: {{ .Values.server.port }}
diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml
index 79c77a49e..1840cf909 100644
--- a/charts/helm/checkmate/values.yaml
+++ b/charts/helm/checkmate/values.yaml
@@ -1,40 +1,46 @@
client:
- image: ghcr.io/bluewave-labs/checkmate-client:v2.3
+ image: ghcr.io/bluewave-labs/checkmate-client:v3.2.0
port: 80
ingress:
enabled: true
host: change_me
+ className: nginx
+ annotations: {}
server:
- image: ghcr.io/bluewave-labs/checkmate-backend:v2.3
+ image: ghcr.io/bluewave-labs/checkmate-backend:v3.2.0
port: 52345
- protocol: change_me
+ protocol: http
ingress:
enabled: true
host: change_me
+ className: nginx
+ annotations: {}
redis:
+ enabled: false
image: redis:7.2
port: 6379
mongodb:
- image: mongo:7.0
+ image: ghcr.io/bluewave-labs/checkmate-mongo:v3.2.0
port: 27017
secrets:
JWT_SECRET: change_me
- REFRESH_TOKEN_SECRET: change_me
- SYSTEM_EMAIL_ADDRESS: test@example.com
- SYSTEM_EMAIL_PASSWORD: change_me
- SYSTEM_EMAIL_HOST: smtp.example.com
- SYSTEM_EMAIL_PORT: "587"
- PAGESPEED_API_KEY: change_me
- DB_CONNECTION_STRING: mongodb://mongodb:27017/uptime_db
- REDIS_HOST: redis
- REDIS_PORT: "6379"
- DB_TYPE: MongoDB
- TOKEN_TTL: 99d
- REFRESH_TOKEN_TTL: 99d
+# REFRESH_TOKEN_SECRET: change_me
+# SYSTEM_EMAIL_ADDRESS: test@example.com
+# SYSTEM_EMAIL_PASSWORD: change_me
+# SYSTEM_EMAIL_HOST: smtp.example.com
+# SYSTEM_EMAIL_PORT: "587"
+# PAGESPEED_API_KEY: change_me
+ DB_CONNECTION_STRING: mongodb://checkmate-mongodb.namespace.svc:27017/uptime_db
+ CLIENT_HOST: change_me
+# REDIS_HOST: redis
+# REDIS_PORT: "6379"
+# DB_TYPE: MongoDB
+# TOKEN_TTL: 99d
+# REFRESH_TOKEN_TTL: 99d
persistence:
mongodbSize: 5Gi
diff --git a/client/src/Components/v2/Arrows/ArrowLeft.tsx b/client/src/Components/v2/Arrows/ArrowLeft.tsx
new file mode 100644
index 000000000..01d1342c8
--- /dev/null
+++ b/client/src/Components/v2/Arrows/ArrowLeft.tsx
@@ -0,0 +1,36 @@
+import LeftArrow from "@/assets/icons/left-arrow.svg?react";
+import LeftArrowDouble from "@/assets/icons/left-arrow-double.svg?react";
+import LeftArrowLong from "@/assets/icons/left-arrow-long.svg?react";
+
+export const ArrowLeft = ({
+ type,
+ color = "#667085",
+ ...props
+}: {
+ type?: string;
+ color?: string | undefined;
+ [key: string]: any;
+}) => {
+ if (type === "double") {
+ return (
+
+ );
+ } else if (type === "long") {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+};
diff --git a/client/src/Components/v2/Arrows/ArrowRight.tsx b/client/src/Components/v2/Arrows/ArrowRight.tsx
new file mode 100644
index 000000000..c73165f16
--- /dev/null
+++ b/client/src/Components/v2/Arrows/ArrowRight.tsx
@@ -0,0 +1,28 @@
+import RightArrow from "@/assets/icons/right-arrow.svg?react";
+import RightArrowDouble from "@/assets/icons/right-arrow-double.svg?react";
+
+export const ArrowRight = ({
+ type,
+ color = "#667085",
+ ...props
+}: {
+ type?: string;
+ color?: string | undefined;
+ [key: string]: any;
+}) => {
+ if (type === "double") {
+ return (
+
+ );
+ } else {
+ return (
+
+ );
+ }
+};
diff --git a/client/src/Components/v2/Layouts/RootLayout.tsx b/client/src/Components/v2/Layouts/RootLayout.tsx
index be2f42e30..dbc6f2e5e 100644
--- a/client/src/Components/v2/Layouts/RootLayout.tsx
+++ b/client/src/Components/v2/Layouts/RootLayout.tsx
@@ -1,39 +1,6 @@
-import { useState, useEffect } from "react";
-import useMediaQuery from "@mui/material/useMediaQuery";
-import { useTheme } from "@mui/material/styles";
-
import { Outlet } from "react-router";
import Stack from "@mui/material/Stack";
-import Box from "@mui/material/Box";
-
-const COLLAPSED_WIDTH = 50;
-const EXPANDED_WIDTH = 250;
-
-const SideBar = () => {
- const theme = useTheme();
- const isSmall = useMediaQuery(theme.breakpoints.down("md"));
-
- const [collapsed, setCollapsed] = useState(false);
- useEffect(() => {
- setCollapsed(isSmall);
- }, [isSmall]);
- return (
-
- setCollapsed(!collapsed)}
- >
- Sidebar Content
-
-
- );
-};
+import { SideBar } from "@/Components/v2/Layouts/Sidebar";
const RootLayout = () => {
return (
diff --git a/client/src/Components/v2/Layouts/Sidebar/BottomControls.tsx b/client/src/Components/v2/Layouts/Sidebar/BottomControls.tsx
new file mode 100644
index 000000000..a07ccde6c
--- /dev/null
+++ b/client/src/Components/v2/Layouts/Sidebar/BottomControls.tsx
@@ -0,0 +1,16 @@
+import { useTheme } from "@mui/material/styles";
+
+import Stack from "@mui/material/Stack";
+
+export const BottomControls = ({}) => {
+ const theme = useTheme();
+ return (
+
+ );
+};
diff --git a/client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx b/client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx
new file mode 100644
index 000000000..564c41591
--- /dev/null
+++ b/client/src/Components/v2/Layouts/Sidebar/CollapseButton.tsx
@@ -0,0 +1,50 @@
+import IconButton from "@mui/material/IconButton";
+import { ArrowRight } from "@/Components/v2/Arrows/ArrowRight";
+import { ArrowLeft } from "@/Components/v2/Arrows/ArrowLeft";
+import { useTheme } from "@mui/material/styles";
+import { useDispatch } from "react-redux";
+import { toggleSidebar } from "../../../../Features/UI/uiSlice.js";
+
+export const CollapseButton = ({ collapsed }: { collapsed: boolean }) => {
+ const theme = useTheme();
+ const dispatch = useDispatch();
+ const arrowIcon = collapsed ? (
+
+ ) : (
+
+ );
+
+ return (
+ {
+ dispatch(toggleSidebar());
+ }}
+ >
+ {arrowIcon}
+
+ );
+};
diff --git a/client/src/Components/v2/Layouts/Sidebar/Logo.tsx b/client/src/Components/v2/Layouts/Sidebar/Logo.tsx
new file mode 100644
index 000000000..6d190b99d
--- /dev/null
+++ b/client/src/Components/v2/Layouts/Sidebar/Logo.tsx
@@ -0,0 +1,59 @@
+import Stack from "@mui/material/Stack";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import { useTheme } from "@mui/material/styles";
+import { useNavigate } from "react-router";
+import { useTranslation } from "react-i18next";
+
+export const Logo = ({ collapsed }: { collapsed: boolean }) => {
+ const { t } = useTranslation();
+ const theme = useTheme();
+ const navigate = useNavigate();
+
+ return (
+ navigate("/")}
+ sx={{ cursor: "pointer" }}
+ >
+
+ C
+
+
+ {" "}
+
+ {t("common.appName")}
+
+
+
+ );
+};
diff --git a/client/src/Components/v2/Layouts/Sidebar/Menu.tsx b/client/src/Components/v2/Layouts/Sidebar/Menu.tsx
new file mode 100644
index 000000000..5b3ed705e
--- /dev/null
+++ b/client/src/Components/v2/Layouts/Sidebar/Menu.tsx
@@ -0,0 +1,58 @@
+import Notifications from "@/assets/icons/notifications.svg?react";
+import Monitors from "@/assets/icons/monitors.svg?react";
+import PageSpeed from "@/assets/icons/page-speed.svg?react";
+import Integrations from "@/assets/icons/integrations.svg?react";
+import Incidents from "@/assets/icons/incidents.svg?react";
+import StatusPages from "@/assets/icons/status-pages.svg?react";
+import Maintenance from "@/assets/icons/maintenance.svg?react";
+import Logs from "@/assets/icons/logs.svg?react";
+import Settings from "@/assets/icons/settings.svg?react";
+import Support from "@/assets/icons/support.svg?react";
+import Discussions from "@/assets/icons/discussions.svg?react";
+import Docs from "@/assets/icons/docs.svg?react";
+import ChangeLog from "@/assets/icons/changeLog.svg?react";
+
+export const getMenu = (t: Function) => [
+ { name: t("menu.uptime"), path: "v2/uptime", icon: },
+ { name: t("menu.pagespeed"), path: "pagespeed", icon: },
+
+ { name: t("menu.infrastructure"), path: "infrastructure", icon: },
+ {
+ name: t("menu.notifications"),
+ path: "notifications",
+ icon: ,
+ },
+ { name: t("menu.incidents"), path: "incidents", icon: },
+
+ { name: t("menu.statusPages"), path: "status", icon: },
+ { name: t("menu.maintenance"), path: "maintenance", icon: },
+ { name: t("menu.logs"), path: "logs", icon: },
+
+ {
+ name: t("menu.settings"),
+ icon: ,
+ path: "settings",
+ },
+];
+
+export const getBottomMenu = (t: Function) => [
+ { name: t("menu.support"), path: "support", icon: , url: "invite" },
+ {
+ name: t("menu.discussions"),
+ path: "discussions",
+ icon: ,
+ url: "https://github.com/bluewave-labs/checkmate/discussions",
+ },
+ {
+ name: t("menu.docs"),
+ path: "docs",
+ icon: ,
+ url: "https://bluewavelabs.gitbook.io/checkmate",
+ },
+ {
+ name: t("menu.changelog"),
+ path: "changelog",
+ icon: ,
+ url: "https://github.com/bluewave-labs/checkmate/releases",
+ },
+];
diff --git a/client/src/Components/v2/Layouts/Sidebar/NavItem.tsx b/client/src/Components/v2/Layouts/Sidebar/NavItem.tsx
new file mode 100644
index 000000000..8f7f5d68c
--- /dev/null
+++ b/client/src/Components/v2/Layouts/Sidebar/NavItem.tsx
@@ -0,0 +1,103 @@
+import Tooltip from "@mui/material/Tooltip";
+import ListItemButton from "@mui/material/ListItemButton";
+import ListItemIcon from "@mui/material/ListItemIcon";
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+import { useTheme } from "@mui/material/styles";
+
+export interface NavData {
+ name: string;
+ icon: JSX.Element;
+}
+
+export const NavItem = ({
+ item,
+ collapsed,
+ selected,
+ onClick,
+}: {
+ item: NavData;
+ collapsed: boolean;
+ selected: boolean;
+ onClick: (event: React.MouseEvent) => void;
+}) => {
+ const theme = useTheme();
+ const iconStroke = selected
+ ? theme.palette.primary.contrastText
+ : theme.palette.primary.contrastTextTertiary;
+
+ const buttonBgColor = selected ? theme.palette.secondary.main : "transparent";
+ const buttonBgHoverColor = selected
+ ? theme.palette.secondary.main
+ : theme.palette.tertiary.main;
+ const fontWeight = selected ? 600 : 400;
+ return (
+
+
+
+ {item.icon}
+
+
+
+ {item.name}
+
+
+
+
+ );
+};
diff --git a/client/src/Components/v2/Layouts/Sidebar/index.tsx b/client/src/Components/v2/Layouts/Sidebar/index.tsx
new file mode 100644
index 000000000..b05716aee
--- /dev/null
+++ b/client/src/Components/v2/Layouts/Sidebar/index.tsx
@@ -0,0 +1,103 @@
+import { useEffect } from "react";
+import { useSelector, useDispatch } from "react-redux";
+import { setCollapsed } from "@/Features/UI/uiSlice";
+import useMediaQuery from "@mui/material/useMediaQuery";
+import { useTheme } from "@mui/material/styles";
+import { useTranslation } from "react-i18next";
+import { useNavigate, useLocation } from "react-router-dom";
+
+import { CollapseButton } from "@/Components/v2/Layouts/Sidebar/CollapseButton";
+import Stack from "@mui/material/Stack";
+import List from "@mui/material/List";
+import Divider from "@mui/material/Divider";
+import { Logo } from "@/Components/v2/Layouts/Sidebar/Logo";
+import { getMenu, getBottomMenu } from "@/Components/v2/Layouts/Sidebar/Menu";
+import { NavItem } from "@/Components/v2/Layouts/Sidebar/NavItem";
+import { BottomControls } from "@/Components/v2/Layouts/Sidebar/BottomControls";
+
+export const COLLAPSED_WIDTH = 64;
+export const EXPANDED_WIDTH = 250;
+
+export const SideBar = () => {
+ const theme = useTheme();
+ const isSmall = useMediaQuery(theme.breakpoints.down("md"));
+ const dispatch = useDispatch();
+ const collapsed = useSelector((state: any) => state.ui.sidebar.collapsed);
+ const { t } = useTranslation();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const menu = getMenu(t);
+ const bottomMenu = getBottomMenu(t);
+
+ useEffect(() => {
+ dispatch(setCollapsed({ collapsed: isSmall }));
+ }, [isSmall]);
+
+ return (
+
+
+
+
+ {menu.map((item) => {
+ const selected = location.pathname.startsWith(`/${item.path}`);
+ return (
+ navigate(`/${item.path}`)}
+ />
+ );
+ })}
+
+
+ {bottomMenu.map((item) => {
+ const selected = location.pathname.startsWith(`/${item.path}`);
+
+ return (
+ {
+ if (item.url) {
+ window.open(item.url, "_blank", "noreferrer");
+ } else {
+ navigate(`/${item.path}`);
+ }
+ }}
+ />
+ );
+ })}
+
+
+
+
+ );
+};
diff --git a/client/src/Features/Auth/v2AuthSlice.ts b/client/src/Features/Auth/v2AuthSlice.ts
new file mode 100644
index 000000000..28a18b659
--- /dev/null
+++ b/client/src/Features/Auth/v2AuthSlice.ts
@@ -0,0 +1,19 @@
+import { createSlice } from "@reduxjs/toolkit";
+
+const initialState = {
+ isAuthenticated: false,
+};
+
+const v2AuthSlice = createSlice({
+ name: "v2Auth",
+ initialState,
+ reducers: {
+ setIsAuthenticated: (state, action) => {
+ const { authenticated } = action.payload;
+ state.isAuthenticated = authenticated;
+ },
+ },
+});
+
+export default v2AuthSlice.reducer;
+export const { setIsAuthenticated } = v2AuthSlice.actions;
diff --git a/client/src/Features/UI/uiSlice.js b/client/src/Features/UI/uiSlice.js
index 45ba267b0..63f7ae90e 100644
--- a/client/src/Features/UI/uiSlice.js
+++ b/client/src/Features/UI/uiSlice.js
@@ -47,6 +47,10 @@ const uiSlice = createSlice({
toggleSidebar: (state) => {
state.sidebar.collapsed = !state.sidebar.collapsed;
},
+ setCollapsed: (state, action) => {
+ const { collapsed } = action.payload;
+ state.sidebar.collapsed = collapsed;
+ },
setMode: (state, action) => {
state.mode = action.payload;
},
@@ -73,6 +77,7 @@ export default uiSlice.reducer;
export const {
setRowsPerPage,
toggleSidebar,
+ setCollapsed,
setMode,
setShowURL,
setGreeting,
diff --git a/client/src/Pages/v2/Auth/Login.tsx b/client/src/Pages/v2/Auth/Login.tsx
index fd4a08e93..a41750c27 100644
--- a/client/src/Pages/v2/Auth/Login.tsx
+++ b/client/src/Pages/v2/Auth/Login.tsx
@@ -3,7 +3,9 @@ import { z } from "zod";
import { useTranslation } from "react-i18next";
import { useForm, Controller } from "react-hook-form";
import { useTheme } from "@mui/material/styles";
-
+import { useDispatch } from "react-redux";
+import { setIsAuthenticated } from "@/Features/Auth/v2AuthSlice";
+import { useNavigate } from "react-router";
import { TextInput } from "@/Components/v2/Inputs/TextInput";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
@@ -19,8 +21,10 @@ type FormData = z.infer;
const Login = () => {
const { t } = useTranslation();
+ const dispatch = useDispatch();
const theme = useTheme();
- const { post, loading, error } = usePost("/auth/login");
+ const { post, loading } = usePost("/auth/login");
+ const navigate = useNavigate();
const {
handleSubmit,
@@ -37,9 +41,10 @@ const Login = () => {
const onSubmit = async (data: FormData) => {
const result = await post(data);
if (result) {
- console.log(result.message);
+ dispatch(setIsAuthenticated({ authenticated: true }));
+ navigate("/v2/uptime");
} else {
- console.error("Login failed:", error);
+ dispatch(setIsAuthenticated({ authenticated: false }));
}
};
diff --git a/client/src/Routes/v2router.tsx b/client/src/Routes/v2router.tsx
index 941550026..12d3eb595 100644
--- a/client/src/Routes/v2router.tsx
+++ b/client/src/Routes/v2router.tsx
@@ -25,7 +25,11 @@ const V2Routes = ({ mode = "light" }) => {
element={}
>
Uptime}
+ />
+ Test Page}
/>
diff --git a/client/src/Utils/Theme/v2/palette.ts b/client/src/Utils/Theme/v2/palette.ts
index 2875f750d..855f7b6b8 100644
--- a/client/src/Utils/Theme/v2/palette.ts
+++ b/client/src/Utils/Theme/v2/palette.ts
@@ -54,12 +54,18 @@ export const lightPalette = {
main: colors.offWhite,
contrastText: colors.blueGray800,
contrastTextSecondary: colors.blueGray600,
+ contrastTextTertiary: colors.blueGray500,
+ lowContrast: colors.gray250,
},
secondary: {
main: colors.gray200,
light: colors.lightBlueWave,
contrastText: colors.blueGray600,
},
+ tertiary: {
+ main: colors.gray100,
+ contrastText: colors.blueGray800,
+ },
};
export const darkPalette = {
@@ -73,10 +79,16 @@ export const darkPalette = {
main: colors.offBlack,
contrastText: colors.blueGray50,
contrastTextSecondary: colors.gray200,
+ contrastTextTertiary: colors.gray500,
+ lowContrast: colors.blueGray600,
},
secondary: {
main: "#313131",
light: colors.lightBlueWave,
contrastText: colors.gray200,
},
+ tertiary: {
+ main: colors.blueGray800,
+ contrastText: colors.gray100,
+ },
};
diff --git a/client/src/Utils/Theme/v2/theme.ts b/client/src/Utils/Theme/v2/theme.ts
index 55d2b9bb9..77bab3e70 100644
--- a/client/src/Utils/Theme/v2/theme.ts
+++ b/client/src/Utils/Theme/v2/theme.ts
@@ -67,6 +67,9 @@ export const theme = (mode: string, palette: any) =>
},
},
},
+ shape: {
+ borderRadius: 2,
+ },
});
export const lightTheme = createTheme(theme("light", lightPalette));
diff --git a/client/src/store.js b/client/src/store.js
index 69aa3d50b..565d68519 100644
--- a/client/src/store.js
+++ b/client/src/store.js
@@ -1,6 +1,7 @@
import { configureStore, combineReducers } from "@reduxjs/toolkit";
import authReducer from "./Features/Auth/authSlice";
+import v2AuthReducer from "./Features/Auth/v2AuthSlice";
import uiReducer from "./Features/UI/uiSlice";
import storage from "redux-persist/lib/storage";
import { persistReducer, persistStore, createTransform } from "redux-persist";
@@ -19,12 +20,13 @@ const authTransform = createTransform(
const persistConfig = {
key: "root",
storage,
- whitelist: ["auth", "ui"],
+ whitelist: ["auth", "v2Auth", "ui"],
transforms: [authTransform],
};
const rootReducer = combineReducers({
auth: authReducer,
+ v2Auth: v2AuthReducer,
ui: uiReducer,
});
diff --git a/client/src/types/env.d.ts b/client/src/types/env.d.ts
index e2bce23dd..74a2a29e7 100644
--- a/client/src/types/env.d.ts
+++ b/client/src/types/env.d.ts
@@ -7,3 +7,11 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv;
}
+
+declare module "*.svg?react" {
+ import * as React from "react";
+ const ReactComponent: React.FunctionComponent<
+ React.SVGProps & { title?: string }
+ >;
+ export default ReactComponent;
+}
diff --git a/client/src/types/mui.d.ts b/client/src/types/mui.d.ts
index 851ffea70..84bcd799f 100644
--- a/client/src/types/mui.d.ts
+++ b/client/src/types/mui.d.ts
@@ -3,13 +3,17 @@ import "@mui/material/Button";
declare module "@mui/material/styles" {
interface Palette {
accent: Palette["primary"];
+ tertiary: Palette["primary"];
}
interface PaletteOptions {
accent?: PaletteOptions["primary"];
+ tertiary?: PaletteOptions["primary"];
}
interface PaletteColor {
contrastTextSecondary?: string;
+ contrastTextTertiary?: string;
+ lowContrast?: string;
}
}