|
@ -20,6 +20,7 @@ config.yml
|
||||||
*.db
|
*.db
|
||||||
tmp
|
tmp
|
||||||
frontend/node_modules
|
frontend/node_modules
|
||||||
|
react-frontend/node_modules
|
||||||
.next
|
.next
|
||||||
node_modules
|
node_modules
|
||||||
Dockerfile
|
Dockerfile
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: CI
|
name: CI
|
||||||
on: [ push ]
|
on: [push]
|
||||||
jobs:
|
jobs:
|
||||||
cancel:
|
cancel:
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
|
@ -18,7 +18,6 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
echo "Don't cancel old workflow"
|
echo "Don't cancel old workflow"
|
||||||
|
|
||||||
|
|
||||||
build-statping-docker-image:
|
build-statping-docker-image:
|
||||||
name: Docker image - statping
|
name: Docker image - statping
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
|
@ -42,6 +41,19 @@ jobs:
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.PUBLIC_DOCKER_USERNAME }}
|
username: ${{ secrets.PUBLIC_DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.PUBLIC_DOCKER_PASSWORD }}
|
password: ${{ secrets.PUBLIC_DOCKER_PASSWORD }}
|
||||||
|
- name: build statping base_image
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
tags: razorpay/statping:base_${{ github.sha }}
|
||||||
|
push: true
|
||||||
|
file: ./Dockerfile.base
|
||||||
|
build-args: GIT_COMMIT_HASH=${{ github.sha }}
|
||||||
|
cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
|
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||||
|
env:
|
||||||
|
GIT_COMMIT_HASH: ${{ github.sha }}
|
||||||
|
APP_ENV: dev_docker
|
||||||
|
EXEC_MODE: test
|
||||||
- name: build statping image
|
- name: build statping image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
with:
|
with:
|
||||||
|
@ -75,7 +87,7 @@ jobs:
|
||||||
workflow_status:
|
workflow_status:
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
name: Update Status Check
|
name: Update Status Check
|
||||||
needs: [ build-statping-docker-image]
|
needs: [build-statping-docker-image]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: Failed
|
- name: Failed
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [ opened, reopened, edited, synchronize]
|
|
||||||
name: Mandatory-Jira-Check
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
Find-Jira-Id:
|
|
||||||
name: Find-Jira-Id
|
|
||||||
runs-on: [ubuntu-latest]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@master
|
|
||||||
|
|
||||||
- name: Checkout GitHub Action Repo
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
repository: Razorpay/check-commit-jira
|
|
||||||
path: .github/actions/check-commit-jira
|
|
||||||
ref: combined_check
|
|
||||||
token: ${{ secrets.GIT_TOKEN }}
|
|
||||||
- name: Login
|
|
||||||
uses: ./.github/actions/check-commit-jira/jira-login
|
|
||||||
env:
|
|
||||||
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
|
|
||||||
JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
|
|
||||||
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
|
|
||||||
|
|
||||||
# Find Issue on JIRA
|
|
||||||
- name: Get Issue Key
|
|
||||||
id: find
|
|
||||||
uses: ./.github/actions/check-commit-jira/jira-issue-check
|
|
||||||
with:
|
|
||||||
from: pull_request
|
|
||||||
# Print JIRA ID found on jira
|
|
||||||
- name: Find issue info
|
|
||||||
run: echo "Issue ${{ steps.find.outputs.issue }} was found"
|
|
|
@ -22,6 +22,7 @@ dart-sass
|
||||||
logs
|
logs
|
||||||
tmp
|
tmp
|
||||||
source/dist
|
source/dist
|
||||||
|
react/build
|
||||||
/dev/test/node_modules
|
/dev/test/node_modules
|
||||||
dev/test/cypress/videos
|
dev/test/cypress/videos
|
||||||
dev/test/cypress/screenshots
|
dev/test/cypress/screenshots
|
||||||
|
@ -40,3 +41,4 @@ tmp
|
||||||
/frontend/cypress/videos/
|
/frontend/cypress/videos/
|
||||||
services.yml
|
services.yml
|
||||||
statping.wiki
|
statping.wiki
|
||||||
|
assets/
|
13
Dockerfile
|
@ -1,8 +1,10 @@
|
||||||
FROM statping/statping:base AS base
|
|
||||||
ARG BUILDPLATFORM
|
ARG BUILDPLATFORM
|
||||||
|
ARG GIT_COMMIT_HASH
|
||||||
|
FROM razorpay/statping:base_$GIT_COMMIT_HASH AS base
|
||||||
# Statping main Docker image that contains all required libraries
|
# Statping main Docker image that contains all required libraries
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk --no-cache add libgcc libstdc++ ca-certificates curl jq && update-ca-certificates
|
RUN apk --no-cache add libgcc libstdc++ ca-certificates curl jq && update-ca-certificates
|
||||||
|
RUN apk add --update tzdata
|
||||||
|
|
||||||
COPY --from=base /go/bin/statping /usr/local/bin/
|
COPY --from=base /go/bin/statping /usr/local/bin/
|
||||||
COPY --from=base /root/sassc/bin/sassc /usr/local/bin/
|
COPY --from=base /root/sassc/bin/sassc /usr/local/bin/
|
||||||
|
@ -11,12 +13,19 @@ COPY --from=base /usr/local/share/ca-certificates /usr/local/share/
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
VOLUME /app
|
VOLUME /app
|
||||||
|
|
||||||
|
COPY --from=base /go/src/github.com/statping/statping/react/ ./react/
|
||||||
|
|
||||||
|
COPY --from=base /go/src/github.com/statping/statping/configs/*.yml ./configs/
|
||||||
|
|
||||||
ENV IS_DOCKER=true
|
ENV IS_DOCKER=true
|
||||||
ENV SASS=/usr/local/bin/sassc
|
ENV SASS=/usr/local/bin/sassc
|
||||||
ENV STATPING_DIR=/app
|
ENV STATPING_DIR=/app
|
||||||
ENV PORT=8080
|
ENV PORT=80
|
||||||
|
ENV PROMETHEUS_PORT=9000
|
||||||
|
ENV TZ="Asia/Kolkata"
|
||||||
|
|
||||||
EXPOSE $PORT
|
EXPOSE $PORT
|
||||||
|
EXPOSE $PROMETHEUS_PORT
|
||||||
|
|
||||||
HEALTHCHECK --interval=60s --timeout=10s --retries=3 CMD curl -s "http://localhost:$PORT/health" | jq -r -e ".online==true"
|
HEALTHCHECK --interval=60s --timeout=10s --retries=3 CMD curl -s "http://localhost:$PORT/health" | jq -r -e ".online==true"
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
FROM node:12.18.2-alpine AS frontend
|
FROM node:12.18.2-alpine AS frontend
|
||||||
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
||||||
ARG BUILDPLATFORM
|
ARG BUILDPLATFORM
|
||||||
|
|
||||||
|
# Build vue frontent
|
||||||
WORKDIR /statping
|
WORKDIR /statping
|
||||||
COPY ./frontend/package.json .
|
COPY ./frontend/package.json .
|
||||||
COPY ./frontend/yarn.lock .
|
COPY ./frontend/yarn.lock .
|
||||||
RUN yarn install --pure-lockfile --network-timeout 1000000
|
RUN yarn install --pure-lockfile --network-timeout 1000000
|
||||||
COPY ./frontend .
|
COPY ./frontend/ .
|
||||||
|
RUN yarn build && yarn cache clean
|
||||||
|
|
||||||
|
# Build react frontent
|
||||||
|
WORKDIR /statping-react
|
||||||
|
COPY ./react-frontend/package.json .
|
||||||
|
COPY ./react-frontend/yarn.lock .
|
||||||
|
RUN yarn install --pure-lockfile --network-timeout 1000000
|
||||||
|
COPY ./react-frontend/ .
|
||||||
RUN yarn build && yarn cache clean
|
RUN yarn build && yarn cache clean
|
||||||
|
|
||||||
# Statping Golang BACKEND building from source
|
# Statping Golang BACKEND building from source
|
||||||
|
@ -37,7 +47,9 @@ RUN go get github.com/stretchr/testify/assert && \
|
||||||
go get github.com/crazy-max/xgo
|
go get github.com/crazy-max/xgo
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=frontend /statping/dist/ ./source/dist/
|
COPY --from=frontend /statping/dist/ ./source/dist/
|
||||||
|
COPY --from=frontend /statping-react/build/ ./react/build/
|
||||||
RUN make clean generate embed
|
RUN make clean generate embed
|
||||||
|
|
||||||
RUN go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -o statping --tags "netgo linux" ./cmd
|
RUN go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -o statping --tags "netgo linux" ./cmd
|
||||||
RUN chmod a+x statping && mv statping /go/bin/statping
|
RUN chmod a+x statping && mv statping /go/bin/statping
|
||||||
# /go/bin/statping - statping binary
|
# /go/bin/statping - statping binary
|
||||||
|
|
17
cmd/main.go
|
@ -15,6 +15,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -28,6 +29,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
os.Setenv("TZ", "Asia/Kolkata")
|
||||||
|
if loc, err := time.LoadLocation("Asia/Kolkata"); err != nil {
|
||||||
|
log.Errorf("setting timezone globally : %s", loc)
|
||||||
|
time.Local = loc
|
||||||
|
}
|
||||||
|
|
||||||
stopped = make(chan bool, 1)
|
stopped = make(chan bool, 1)
|
||||||
core.New(VERSION, COMMIT)
|
core.New(VERSION, COMMIT)
|
||||||
utils.InitEnvs()
|
utils.InitEnvs()
|
||||||
|
@ -96,22 +104,31 @@ func start() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Done: LoadConfigs")
|
||||||
|
|
||||||
if err = configs.ConnectConfigs(confgs, true); err != nil {
|
if err = configs.ConnectConfigs(confgs, true); err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
log.Infof("Done: ConnectConfigs")
|
||||||
|
|
||||||
if err = confgs.ResetCore(); err != nil {
|
if err = confgs.ResetCore(); err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Done: ResetCore")
|
||||||
|
|
||||||
if err = confgs.DatabaseChanges(); err != nil {
|
if err = confgs.DatabaseChanges(); err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Done: DatabaseChanges")
|
||||||
|
|
||||||
if err := confgs.MigrateDatabase(); err != nil {
|
if err := confgs.MigrateDatabase(); err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("Done: MigrateDatabase")
|
||||||
|
|
||||||
if err := mainProcess(); err != nil {
|
if err := mainProcess(); err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
admin_email: ankit.gupta@razorpay.com
|
||||||
|
admin_password: admin
|
||||||
|
admin_user: admin
|
||||||
|
allow_reports: false
|
||||||
|
base_path: ""
|
||||||
|
cleanup_interval: 0h30m0s
|
||||||
|
cmd_file: /bin/bash
|
||||||
|
db_conn: postgres
|
||||||
|
db_database: ${DATABASE_NAME}
|
||||||
|
db_host: ${DATABASE_URL}
|
||||||
|
db_port: 5432
|
||||||
|
db_user: ${DATABASE_USERNAME}
|
||||||
|
db_pass: ${DATABASE_PASSWORD}
|
||||||
|
description: Razorpay Status Page
|
||||||
|
disable_logs: false
|
||||||
|
domain: statping.stage.razorpay.in
|
||||||
|
go_env: ""
|
||||||
|
is_aws: false
|
||||||
|
is_docker: false
|
||||||
|
max_idle_conn: 100
|
||||||
|
max_life_conn: 180
|
||||||
|
max_open_conn: 100
|
||||||
|
name: Razorpay Status Page
|
||||||
|
postgres_sslmode: disable
|
||||||
|
remove_after: 24h0m0s
|
||||||
|
sample_data: true
|
||||||
|
sass: /usr/local/bin/sass
|
||||||
|
use_assets: true
|
||||||
|
use_cdn: false
|
||||||
|
version: ""
|
|
@ -0,0 +1,30 @@
|
||||||
|
admin_email: ankit.gupta@razorpay.com
|
||||||
|
admin_password: admin
|
||||||
|
admin_user: superadmin
|
||||||
|
allow_reports: false
|
||||||
|
base_path: ""
|
||||||
|
cleanup_interval: 12h0m0s
|
||||||
|
cmd_file: /bin/bash
|
||||||
|
db_conn: postgres
|
||||||
|
db_database: ${DATABASE_NAME}
|
||||||
|
db_host: ${DATABASE_URL}
|
||||||
|
db_port: 5432
|
||||||
|
db_user: ${DATABASE_USERNAME}
|
||||||
|
db_pass: ${DATABASE_PASSWORD}
|
||||||
|
description: Razorpay Status Page
|
||||||
|
disable_logs: false
|
||||||
|
domain: statping.stage.razorpay.in
|
||||||
|
go_env: ""
|
||||||
|
is_aws: false
|
||||||
|
is_docker: false
|
||||||
|
max_idle_conn: 25
|
||||||
|
max_life_conn: 180
|
||||||
|
max_open_conn: 25
|
||||||
|
name: Razorpay Status Page
|
||||||
|
postgres_sslmode: require
|
||||||
|
remove_after: 2160h0m0s
|
||||||
|
sample_data: true
|
||||||
|
sass: /usr/local/bin/sass
|
||||||
|
use_assets: true
|
||||||
|
use_cdn: false
|
||||||
|
version: ""
|
|
@ -0,0 +1,30 @@
|
||||||
|
admin_email: ankit.gupta@razorpay.com
|
||||||
|
admin_password: admin
|
||||||
|
admin_user: admin
|
||||||
|
allow_reports: false
|
||||||
|
base_path: ""
|
||||||
|
cleanup_interval: 0h30m0s
|
||||||
|
cmd_file: /bin/bash
|
||||||
|
db_conn: postgres
|
||||||
|
db_database: ${DATABASE_NAME}
|
||||||
|
db_host: ${DATABASE_URL}
|
||||||
|
db_port: 5432
|
||||||
|
db_user: ${DATABASE_USERNAME}
|
||||||
|
db_pass: ${DATABASE_PASSWORD}
|
||||||
|
description: Razorpay Status Page
|
||||||
|
disable_logs: false
|
||||||
|
domain: statping.stage.razorpay.in
|
||||||
|
go_env: ""
|
||||||
|
is_aws: false
|
||||||
|
is_docker: false
|
||||||
|
max_idle_conn: 25
|
||||||
|
max_life_conn: 180
|
||||||
|
max_open_conn: 25
|
||||||
|
name: Razorpay Status Page
|
||||||
|
postgres_sslmode: disable
|
||||||
|
remove_after: 24h0m0s
|
||||||
|
sample_data: true
|
||||||
|
sass: /usr/local/bin/sass
|
||||||
|
use_assets: true
|
||||||
|
use_cdn: false
|
||||||
|
version: ""
|
|
@ -211,7 +211,7 @@ func OpenTester() (Database, error) {
|
||||||
utils.Params.GetString("DB_DATABASE"),
|
utils.Params.GetString("DB_DATABASE"),
|
||||||
)
|
)
|
||||||
case "postgres":
|
case "postgres":
|
||||||
dbString = fmt.Sprintf("host=%s port=%v user=%s dbname=%s password=%s sslmode=disable timezone=UTC",
|
dbString = fmt.Sprintf("host=%s port=%v user=%s dbname=%s password=%s sslmode=disable",
|
||||||
utils.Params.GetString("DB_HOST"),
|
utils.Params.GetString("DB_HOST"),
|
||||||
utils.Params.GetInt("DB_PORT"),
|
utils.Params.GetInt("DB_PORT"),
|
||||||
utils.Params.GetString("DB_USER"),
|
utils.Params.GetString("DB_USER"),
|
||||||
|
|
|
@ -87,6 +87,17 @@ func (b *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
||||||
return caller.ToValues()
|
return caller.ToValues()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *GroupQuery) NoFailureGraphData(by By) ([]*TimeValue, error) {
|
||||||
|
|
||||||
|
caller := &TimeVar{b, nil}
|
||||||
|
|
||||||
|
if b.FillEmpty {
|
||||||
|
return caller.FillMissing(b.Start, b.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
return caller.ToValues()
|
||||||
|
}
|
||||||
|
|
||||||
// ToTimeValue will format the SQL rows into a JSON format for the API.
|
// ToTimeValue will format the SQL rows into a JSON format for the API.
|
||||||
// [{"timestamp": "2006-01-02T15:04:05Z", "amount": 468293}]
|
// [{"timestamp": "2006-01-02T15:04:05Z", "amount": 468293}]
|
||||||
// TODO redo this entire function, use better SQL query to group by time
|
// TODO redo this entire function, use better SQL query to group by time
|
||||||
|
@ -169,8 +180,8 @@ func ParseRequest(r *http.Request) (*GroupQuery, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
query := &GroupQuery{
|
query := &GroupQuery{
|
||||||
Start: time.Unix(startField, 0).UTC(),
|
Start: time.Unix(startField, 0).Local(),
|
||||||
End: time.Unix(endField, 0).UTC(),
|
End: time.Unix(endField, 0).Local(),
|
||||||
Group: groupDur,
|
Group: groupDur,
|
||||||
Order: orderBy,
|
Order: orderBy,
|
||||||
Limit: int(limit),
|
Limit: int(limit),
|
||||||
|
@ -213,8 +224,8 @@ func ParseQueries(r *http.Request, o isObject) (*GroupQuery, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
query := &GroupQuery{
|
query := &GroupQuery{
|
||||||
Start: time.Unix(startField, 0).UTC(),
|
Start: time.Unix(startField, 0).Local(),
|
||||||
End: time.Unix(endField, 0).UTC(),
|
End: time.Unix(endField, 0).Local(),
|
||||||
Group: groupDur,
|
Group: groupDur,
|
||||||
Order: orderBy,
|
Order: orderBy,
|
||||||
Limit: int(limit),
|
Limit: int(limit),
|
||||||
|
|
|
@ -1,57 +1,68 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div v-if="!ready" class="row mt-5">
|
<div v-if="!ready" class="row mt-5">
|
||||||
<div class="col-12 text-center">
|
<div class="col-12 text-center">
|
||||||
<font-awesome-icon icon="circle-notch" size="3x" spin/>
|
<font-awesome-icon icon="circle-notch" size="3x" spin />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 text-center mt-3 mb-3">
|
<div class="col-12 text-center mt-3 mb-3">
|
||||||
<span class="text-muted">Loading Service</span>
|
<span class="text-muted">Loading Service</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<FormService v-if="ready" :in_service="service"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<FormService v-if="ready" :in_service="service" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const FormGroup = () => import(/* webpackChunkName: "dashboard" */ "../../forms/Group");
|
const FormGroup = () =>
|
||||||
import Api from "../../API";
|
import(/* webpackChunkName: "dashboard" */ "../../forms/Group");
|
||||||
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ "../../forms/ToggleSwitch");
|
import Api from "../../API";
|
||||||
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable')
|
const ToggleSwitch = () =>
|
||||||
const FormService = () => import(/* webpackChunkName: "dashboard" */ "../../forms/Service");
|
import(/* webpackChunkName: "dashboard" */ "../../forms/ToggleSwitch");
|
||||||
|
const draggable = () =>
|
||||||
|
import(/* webpackChunkName: "dashboard" */ "vuedraggable");
|
||||||
|
const FormService = () =>
|
||||||
|
import(/* webpackChunkName: "dashboard" */ "../../forms/Service");
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'EditService',
|
name: "EditService",
|
||||||
components: {
|
components: {
|
||||||
FormService,
|
FormService,
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
draggable
|
draggable,
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetchData()
|
this.fetchData();
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'$route': 'fetchData'
|
$route: "fetchData",
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
service: null,
|
service: null,
|
||||||
ready: false
|
ready: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchData() {
|
||||||
|
if (!this.$route.params.id) {
|
||||||
|
this.ready = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
const data = await Api.service(this.$route.params.id);
|
||||||
|
this.service = {
|
||||||
|
...data,
|
||||||
|
sub_services_detail:
|
||||||
|
Object.prototype.toString.call(data.sub_services_detail) ===
|
||||||
|
"[object Object]"
|
||||||
|
? JSON.stringify(data.sub_services_detail)
|
||||||
|
: data.sub_services_detail,
|
||||||
|
};
|
||||||
|
this.ready = true;
|
||||||
},
|
},
|
||||||
methods: {
|
},
|
||||||
async fetchData () {
|
};
|
||||||
if (!this.$route.params.id) {
|
|
||||||
this.ready = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.service = await Api.service(this.$route.params.id)
|
|
||||||
this.ready = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,142 +1,145 @@
|
||||||
const english = {
|
const english = {
|
||||||
settings: "Settings",
|
settings: "Settings",
|
||||||
dashboard: "Dashboard",
|
dashboard: "Dashboard",
|
||||||
services: "Services",
|
services: "Services",
|
||||||
service: "Service",
|
service: "Service",
|
||||||
failures: "Failures",
|
failures: "Failures",
|
||||||
users: "Users",
|
users: "Users",
|
||||||
login: "Login",
|
login: "Login",
|
||||||
logout: "Logout",
|
logout: "Logout",
|
||||||
online: "Online",
|
online: "Online",
|
||||||
offline: "Offline",
|
offline: "Offline",
|
||||||
configs: "Configuration",
|
configs: "Configuration",
|
||||||
username: "Username",
|
username: "Username",
|
||||||
password: "Password",
|
password: "Password",
|
||||||
email: "Email",
|
email: "Email",
|
||||||
confirm_password: "Confirm Password",
|
confirm_password: "Confirm Password",
|
||||||
uptime: "Uptime",
|
uptime: "Uptime",
|
||||||
name: "Name",
|
name: "Name",
|
||||||
copy: "Copy",
|
copy: "Copy",
|
||||||
close: "Close",
|
close: "Close",
|
||||||
secret: "Secret",
|
secret: "Secret",
|
||||||
regen_api: "Regenerate API Keys",
|
regen_api: "Regenerate API Keys",
|
||||||
regen_desc: "API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.",
|
regen_desc:
|
||||||
visibility: "Visibility",
|
"API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.",
|
||||||
group: "Group",
|
visibility: "Visibility",
|
||||||
group_create: "Create Group",
|
group: "Group",
|
||||||
group_update: "Update Group",
|
group_create: "Create Group",
|
||||||
group_public_desc: "Show group services to the public",
|
group_update: "Update Group",
|
||||||
groups: "Groups",
|
group_public_desc: "Show group services to the public",
|
||||||
no_group: "No Group",
|
groups: "Groups",
|
||||||
public: "Public",
|
no_group: "No Group",
|
||||||
private: "Private",
|
public: "Public",
|
||||||
announcements: "Announcements",
|
private: "Private",
|
||||||
notifiers: "Notifiers",
|
announcements: "Announcements",
|
||||||
logs: "Logs",
|
notifiers: "Notifiers",
|
||||||
help: "Help",
|
logs: "Logs",
|
||||||
type: "Type",
|
help: "Help",
|
||||||
edit: "Edit",
|
type: "Type",
|
||||||
update: "Update",
|
edit: "Edit",
|
||||||
create: "Create",
|
update: "Update",
|
||||||
view: "View",
|
create: "Create",
|
||||||
save: "Save",
|
view: "View",
|
||||||
title: "Title",
|
save: "Save",
|
||||||
status: "Status",
|
title: "Title",
|
||||||
begins: "Begins",
|
status: "Status",
|
||||||
total_services: "Total Services",
|
begins: "Begins",
|
||||||
online_services: "Online Services",
|
total_services: "Total Services",
|
||||||
request_timeout: "Request Timeout",
|
online_services: "Online Services",
|
||||||
service_never_online: "Service has never been online",
|
request_timeout: "Request Timeout",
|
||||||
service_online_check: "Online checked",
|
service_never_online: "Service has never been online",
|
||||||
service_offline_time: "Service has been offline for",
|
service_online_check: "Online checked",
|
||||||
days_ago: "Days Ago",
|
service_offline_time: "Service has been offline for",
|
||||||
today: "Today",
|
days_ago: "Days Ago",
|
||||||
week: "Week",
|
today: "Today",
|
||||||
month: "Month",
|
week: "Week",
|
||||||
day: "Day",
|
month: "Month",
|
||||||
hour: "Hour",
|
day: "Day",
|
||||||
minute: "Minute",
|
hour: "Hour",
|
||||||
failures_24_hours: "Failures last 24 hours",
|
minute: "Minute",
|
||||||
no_services: "You currently don't have any services!",
|
failures_24_hours: "Failures last 24 hours",
|
||||||
theme: "Theme",
|
no_services: "You currently don't have any services!",
|
||||||
cache: "Cache",
|
theme: "Theme",
|
||||||
authentication: "Authentication",
|
cache: "Cache",
|
||||||
import: "Import",
|
authentication: "Authentication",
|
||||||
main_settings: "Main Settings",
|
import: "Import",
|
||||||
variables: "Variables",
|
main_settings: "Main Settings",
|
||||||
docs: "Documentation",
|
variables: "Variables",
|
||||||
links: "Links",
|
docs: "Documentation",
|
||||||
changelog: "Change Log",
|
links: "Links",
|
||||||
repo: "Repository",
|
changelog: "Change Log",
|
||||||
language: "Language",
|
repo: "Repository",
|
||||||
db_connection: "Database Connection",
|
language: "Language",
|
||||||
db_host: "Database Host",
|
db_connection: "Database Connection",
|
||||||
db_port: "Database Port",
|
db_host: "Database Host",
|
||||||
db_username: "Database Username",
|
db_port: "Database Port",
|
||||||
db_password: "Database Password",
|
db_username: "Database Username",
|
||||||
db_database: "Database Name",
|
db_password: "Database Password",
|
||||||
send_reports: "Send Error Reports",
|
db_database: "Database Name",
|
||||||
send_reports_desc: "Send errors to Statping for debugging",
|
send_reports: "Send Error Reports",
|
||||||
project_name: "Status Page Name",
|
send_reports_desc: "Send errors to Statping for debugging",
|
||||||
description: "Description",
|
project_name: "Status Page Name",
|
||||||
domain: "Domain",
|
description: "Description",
|
||||||
enable_cdn: "Enable CDN",
|
domain: "Domain",
|
||||||
newsletter: "Newsletter",
|
enable_cdn: "Enable CDN",
|
||||||
newsletter_note: "We will only send you an email for major changes",
|
newsletter: "Newsletter",
|
||||||
loading: "Loading",
|
newsletter_note: "We will only send you an email for major changes",
|
||||||
save_settings: "Save Settings",
|
loading: "Loading",
|
||||||
average_response: "Average Response",
|
save_settings: "Save Settings",
|
||||||
last_uptime: "Uptime last",
|
average_response: "Average Response",
|
||||||
sign_in: "Sign In",
|
last_uptime: "Uptime last",
|
||||||
last_login: "Last Login",
|
sign_in: "Sign In",
|
||||||
admin: "Admin",
|
last_login: "Last Login",
|
||||||
user: "User",
|
admin: "Admin",
|
||||||
failed: "Failed",
|
user: "User",
|
||||||
wrong_login: "Incorrect username or password",
|
failed: "Failed",
|
||||||
theme_editor: "Theme Editor",
|
wrong_login: "Incorrect username or password",
|
||||||
enable_assets: "Enable Local Assets",
|
theme_editor: "Theme Editor",
|
||||||
assets_desc: "Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.",
|
enable_assets: "Enable Local Assets",
|
||||||
assets_btn: "Enable Local Assets",
|
assets_desc:
|
||||||
assets_loading: "Creating Assets",
|
"Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.",
|
||||||
assets_dir: "Assets Directory",
|
assets_btn: "Enable Local Assets",
|
||||||
footer: "Footer",
|
assets_loading: "Creating Assets",
|
||||||
footer_notes: "You can use HTML tags in footer",
|
assets_dir: "Assets Directory",
|
||||||
global_announcement: "Global Announcement",
|
footer: "Footer",
|
||||||
announcement_date: "Announcement Date Range",
|
footer_notes: "You can use HTML tags in footer",
|
||||||
notify_users: "Notify Users",
|
global_announcement: "Global Announcement",
|
||||||
notify_desc: "Notify Users Before Scheduled Time",
|
announcement_date: "Announcement Date Range",
|
||||||
notify_method: "Notification Method",
|
notify_users: "Notify Users",
|
||||||
notify_before: "Notify Before",
|
notify_desc: "Notify Users Before Scheduled Time",
|
||||||
message_create: "Create Announcement",
|
notify_method: "Notification Method",
|
||||||
message_edit: "Edit Announcement",
|
notify_before: "Notify Before",
|
||||||
minutes: "Minutes",
|
message_create: "Create Announcement",
|
||||||
hours: "Hours",
|
message_edit: "Edit Announcement",
|
||||||
days: "Days",
|
minutes: "Minutes",
|
||||||
user_create: "Create User",
|
hours: "Hours",
|
||||||
user_update: "Update User",
|
days: "Days",
|
||||||
administrator: "Administrator",
|
user_create: "Create User",
|
||||||
checkins: "Checkins",
|
user_update: "Update User",
|
||||||
incidents: "Incidents",
|
administrator: "Administrator",
|
||||||
service_info: "Service Info",
|
checkins: "Checkins",
|
||||||
service_name: "Service Name",
|
incidents: "Incidents",
|
||||||
service_type: "Service Type",
|
service_info: "Service Info",
|
||||||
permalink: "Permalink URL",
|
service_name: "Service Name",
|
||||||
service_public: "Public Service",
|
service_description: "Service Description",
|
||||||
check_interval: "Check Interval",
|
service_type: "Service Type",
|
||||||
service_endpoint: "Service Endpoint",
|
permalink: "Permalink URL",
|
||||||
service_check: "Service Check Type",
|
service_public: "Public Service",
|
||||||
service_timeout: "Request Timeout",
|
check_interval: "Check Interval",
|
||||||
expected_resp: "Expected Response",
|
service_endpoint: "Service Endpoint",
|
||||||
expected_code: "Expected Status Code",
|
service_check: "Service Check Type",
|
||||||
follow_redir: "Follow Redirects",
|
service_timeout: "Request Timeout",
|
||||||
verify_ssl: "Verify SSL",
|
expected_resp: "Expected Response",
|
||||||
tls_cert: "Use TLS Cert",
|
expected_code: "Expected Status Code",
|
||||||
notification_opts: "Notification Options",
|
follow_redir: "Follow Redirects",
|
||||||
notifications_enable: "Enable Notifications",
|
verify_ssl: "Verify SSL",
|
||||||
notify_after: "Notify After Failures",
|
tls_cert: "Use TLS Cert",
|
||||||
notify_all: "Notify All Changes",
|
notification_opts: "Notification Options",
|
||||||
service_update: "Update Service",
|
notifications_enable: "Enable Notifications",
|
||||||
service_create: "Create Service"
|
notify_after: "Notify After Failures",
|
||||||
}
|
notify_all: "Notify All Changes",
|
||||||
|
service_update: "Update Service",
|
||||||
|
service_create: "Create Service",
|
||||||
|
};
|
||||||
|
|
||||||
export default english
|
export default english;
|
||||||
|
|
|
@ -42,7 +42,7 @@ func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
service, err := services.Find(checkin.ServiceId)
|
service, err := services.FindOne(checkin.ServiceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
|
|
|
@ -122,6 +122,23 @@ func authenticated(handler func(w http.ResponseWriter, r *http.Request), redirec
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authenticatedV2(handler func(r *http.Request) interface{}) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !IsFullAuthenticated(r) {
|
||||||
|
sendUnauthorizedJson(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data := handler(r)
|
||||||
|
err, ok := data.(error)
|
||||||
|
if ok {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(scope{data: data, scope: ScopeName(r)})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// readOnly is a middleware function to check if user is a User before running original request
|
// readOnly is a middleware function to check if user is a User before running original request
|
||||||
func readOnly(handler http.Handler, redirect bool) http.Handler {
|
func readOnly(handler http.Handler, redirect bool) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -21,13 +21,33 @@ func staticAssets(src string) http.Handler {
|
||||||
return http.StripPrefix(src+"/", http.FileServer(http.Dir(utils.Directory+"/assets/"+src)))
|
return http.StripPrefix(src+"/", http.FileServer(http.Dir(utils.Directory+"/assets/"+src)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IndexHandler(entrypoint string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
http.ServeFile(w, r, entrypoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
|
||||||
// Router returns all of the routes used in Statping.
|
// Router returns all of the routes used in Statping.
|
||||||
// Server will use static assets if the 'assets' directory is found in the root directory.
|
// Server will use static assets if the 'assets' directory is found in the root directory.
|
||||||
func Router() *mux.Router {
|
func Router() *mux.Router {
|
||||||
dir := utils.Directory
|
dir := utils.Directory
|
||||||
|
|
||||||
|
// metrics
|
||||||
|
mr := mux.NewRouter().StrictSlash(true)
|
||||||
|
mr.Use(prometheusMiddleware)
|
||||||
|
mr.Handle("/metrics", promhttp.Handler())
|
||||||
|
go http.ListenAndServe(":9000", mr)
|
||||||
|
|
||||||
r := mux.NewRouter().StrictSlash(true)
|
r := mux.NewRouter().StrictSlash(true)
|
||||||
r.Use(prometheusMiddleware)
|
//r.Use(prometheusMiddleware)
|
||||||
|
|
||||||
|
r.PathPrefix("/static/").Handler(http.FileServer(http.Dir("./react/build/")))
|
||||||
|
r.Handle("/", http.HandlerFunc(IndexHandler("./react/build/index.html")))
|
||||||
|
r.Handle("/favicon.ico", http.HandlerFunc(IndexHandler("./react/build/favicon.ico")))
|
||||||
|
r.Handle("/manifest.json", http.HandlerFunc(IndexHandler("./react/build/manifest.json")))
|
||||||
|
r.Handle("/robot.txt", http.HandlerFunc(IndexHandler("./react/build/robot.txt")))
|
||||||
|
|
||||||
authUser := utils.Params.GetString("AUTH_USERNAME")
|
authUser := utils.Params.GetString("AUTH_USERNAME")
|
||||||
authPass := utils.Params.GetString("AUTH_PASSWORD")
|
authPass := utils.Params.GetString("AUTH_PASSWORD")
|
||||||
|
@ -35,16 +55,6 @@ func Router() *mux.Router {
|
||||||
r.Use(basicAuthHandler)
|
r.Use(basicAuthHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
bPath := utils.Params.GetString("BASE_PATH")
|
|
||||||
|
|
||||||
if bPath != "" {
|
|
||||||
basePath = "/" + bPath + "/"
|
|
||||||
r = r.PathPrefix("/" + bPath).Subrouter()
|
|
||||||
r.Handle("", http.HandlerFunc(indexHandler))
|
|
||||||
} else {
|
|
||||||
r.Handle("/", http.HandlerFunc(indexHandler))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !utils.Params.GetBool("DISABLE_LOGS") {
|
if !utils.Params.GetBool("DISABLE_LOGS") {
|
||||||
r.Use(sendLog)
|
r.Use(sendLog)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +78,7 @@ func Router() *mux.Router {
|
||||||
|
|
||||||
r.PathPrefix("/css/").Handler(http.StripPrefix(basePath, staticAssets("css")))
|
r.PathPrefix("/css/").Handler(http.StripPrefix(basePath, staticAssets("css")))
|
||||||
r.PathPrefix("/favicon/").Handler(http.StripPrefix(basePath, staticAssets("favicon")))
|
r.PathPrefix("/favicon/").Handler(http.StripPrefix(basePath, staticAssets("favicon")))
|
||||||
r.PathPrefix("/robots.txt").Handler(http.StripPrefix(basePath, indexHandler))
|
//r.PathPrefix("/robots.txt").Handler(http.StripPrefix(basePath, indexHandler))
|
||||||
r.PathPrefix("/banner.png").Handler(http.StripPrefix(basePath, indexHandler))
|
r.PathPrefix("/banner.png").Handler(http.StripPrefix(basePath, indexHandler))
|
||||||
} else {
|
} else {
|
||||||
tmplFileSrv := http.FileServer(source.TmplBox.HTTPBox())
|
tmplFileSrv := http.FileServer(source.TmplBox.HTTPBox())
|
||||||
|
@ -88,7 +98,7 @@ func Router() *mux.Router {
|
||||||
|
|
||||||
// API Routes
|
// API Routes
|
||||||
r.Handle("/api", scoped(apiIndexHandler))
|
r.Handle("/api", scoped(apiIndexHandler))
|
||||||
r.Handle("/api/setup", http.HandlerFunc(processSetupHandler)).Methods("POST")
|
//r.Handle("/api/setup", http.HandlerFunc(processSetupHandler)).Methods("POST")
|
||||||
api.Handle("/api/login", http.HandlerFunc(apiLoginHandler)).Methods("POST")
|
api.Handle("/api/login", http.HandlerFunc(apiLoginHandler)).Methods("POST")
|
||||||
api.Handle("/api/logout", http.HandlerFunc(logoutHandler))
|
api.Handle("/api/logout", http.HandlerFunc(logoutHandler))
|
||||||
api.Handle("/api/renew", authenticated(apiRenewHandler, false))
|
api.Handle("/api/renew", authenticated(apiRenewHandler, false))
|
||||||
|
@ -101,9 +111,9 @@ func Router() *mux.Router {
|
||||||
api.Handle("/api/settings/configs", authenticated(configsSaveHandler, false)).Methods("POST")
|
api.Handle("/api/settings/configs", authenticated(configsSaveHandler, false)).Methods("POST")
|
||||||
|
|
||||||
// API OAUTH Routes
|
// API OAUTH Routes
|
||||||
api.Handle("/api/oauth", scoped(apiOAuthHandler)).Methods("GET")
|
api.Handle("/api/oauth", authenticatedV2(apiOAuthHandler)).Methods("GET")
|
||||||
api.Handle("/api/oauth", authenticated(apiUpdateOAuthHandler, false)).Methods("POST")
|
api.Handle("/api/oauth", authenticated(apiUpdateOAuthHandler, false)).Methods("POST")
|
||||||
api.Handle("/oauth/{provider}", http.HandlerFunc(oauthHandler))
|
api.Handle("/oauth/{provider}", authenticated(oauthHandler, false))
|
||||||
|
|
||||||
// API SCSS and ASSETS Routes
|
// API SCSS and ASSETS Routes
|
||||||
api.Handle("/api/theme", authenticated(apiThemeViewHandler, false)).Methods("GET")
|
api.Handle("/api/theme", authenticated(apiThemeViewHandler, false)).Methods("GET")
|
||||||
|
@ -122,37 +132,42 @@ func Router() *mux.Router {
|
||||||
// API SERVICE Routes
|
// API SERVICE Routes
|
||||||
api.Handle("/api/services", scoped(apiAllServicesHandler)).Methods("GET")
|
api.Handle("/api/services", scoped(apiAllServicesHandler)).Methods("GET")
|
||||||
api.Handle("/api/services", authenticated(apiCreateServiceHandler, false)).Methods("POST")
|
api.Handle("/api/services", authenticated(apiCreateServiceHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/services/{id}", scoped(apiServiceHandler)).Methods("GET")
|
api.Handle("/api/services/{id}", authenticatedV2(apiServiceHandler)).Methods("GET")
|
||||||
|
api.Handle("/api/services/{id}/sub_services", scoped(apiAllSubServicesHandler)).Methods("GET")
|
||||||
|
api.Handle("/api/services/{id}/sub_services/{sub_id}", authenticatedV2(apiServiceHandler)).Methods("GET")
|
||||||
api.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
|
api.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/services/{id}", authenticated(apiServiceUpdateHandler, false)).Methods("POST")
|
api.Handle("/api/services/{id}", authenticated(apiServiceUpdateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/services/{id}", authenticated(apiServicePatchHandler, false)).Methods("PATCH")
|
api.Handle("/api/services/{id}", authenticated(apiServicePatchHandler, false)).Methods("PATCH")
|
||||||
api.Handle("/api/services/{id}", authenticated(apiServiceDeleteHandler, false)).Methods("DELETE")
|
api.Handle("/api/services/{id}", authenticated(apiServiceDeleteHandler, false)).Methods("DELETE")
|
||||||
api.Handle("/api/services/{id}/failures", scoped(apiServiceFailuresHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/failures", authenticatedV2(apiServiceFailuresHandler)).Methods("GET")
|
||||||
api.Handle("/api/services/{id}/failures", authenticated(servicesDeleteFailuresHandler, false)).Methods("DELETE")
|
api.Handle("/api/services/{id}/failures", authenticated(servicesDeleteFailuresHandler, false)).Methods("DELETE")
|
||||||
api.Handle("/api/services/{id}/hits", scoped(apiServiceHitsHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/hits", authenticatedV2(apiServiceHitsHandler)).Methods("GET")
|
||||||
api.Handle("/api/services/{id}/hits", authenticated(apiServiceHitsDeleteHandler, false)).Methods("DELETE")
|
api.Handle("/api/services/{id}/hits", authenticated(apiServiceHitsDeleteHandler, false)).Methods("DELETE")
|
||||||
|
|
||||||
// API SERVICE CHART DATA Routes
|
// API SERVICE CHART DATA Routes
|
||||||
api.Handle("/api/services/{id}/hits_data", http.HandlerFunc(apiServiceDataHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/hits_data", authenticated(apiServiceDataHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/services/{id}/failure_data", http.HandlerFunc(apiServiceFailureDataHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/failure_data", authenticated(apiServiceFailureDataHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/services/{id}/ping_data", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/ping_data", authenticated(apiServicePingDataHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/services/{id}/uptime_data", http.HandlerFunc(apiServiceTimeDataHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/uptime_data", authenticated(apiServiceTimeDataHandler, false)).Methods("GET")
|
||||||
|
|
||||||
|
api.Handle("/api/services/{id}/block_series", http.HandlerFunc(apiServiceBlockSeriesHandler)).Methods("GET")
|
||||||
|
api.Handle("/api/services/{id}/sub_services/{sub_id}/block_series", http.HandlerFunc(apiSubServiceBlockSeriesHandler)).Methods("GET")
|
||||||
|
|
||||||
// API INCIDENTS Routes
|
// API INCIDENTS Routes
|
||||||
api.Handle("/api/services/{id}/incidents", http.HandlerFunc(apiServiceIncidentsHandler)).Methods("GET")
|
api.Handle("/api/services/{id}/incidents", authenticated(apiServiceIncidentsHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/services/{id}/incidents", authenticated(apiCreateIncidentHandler, false)).Methods("POST")
|
api.Handle("/api/services/{id}/incidents", authenticated(apiCreateIncidentHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/incidents/{id}", authenticated(apiIncidentUpdateHandler, false)).Methods("POST")
|
api.Handle("/api/incidents/{id}", authenticated(apiIncidentUpdateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/incidents/{id}", authenticated(apiDeleteIncidentHandler, false)).Methods("DELETE")
|
api.Handle("/api/incidents/{id}", authenticated(apiDeleteIncidentHandler, false)).Methods("DELETE")
|
||||||
|
|
||||||
// API INCIDENTS UPDATES Routes
|
// API INCIDENTS UPDATES Routes
|
||||||
api.Handle("/api/incidents/{id}/updates", http.HandlerFunc(apiIncidentUpdatesHandler)).Methods("GET")
|
api.Handle("/api/incidents/{id}/updates", authenticated(apiIncidentUpdatesHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/incidents/{id}/updates", authenticated(apiCreateIncidentUpdateHandler, false)).Methods("POST")
|
api.Handle("/api/incidents/{id}/updates", authenticated(apiCreateIncidentUpdateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/incidents/{id}/updates/{uid}", authenticated(apiDeleteIncidentUpdateHandler, false)).Methods("DELETE")
|
api.Handle("/api/incidents/{id}/updates/{uid}", authenticated(apiDeleteIncidentUpdateHandler, false)).Methods("DELETE")
|
||||||
|
|
||||||
// API USER Routes
|
// API USER Routes
|
||||||
api.Handle("/api/users", authenticated(apiAllUsersHandler, false)).Methods("GET")
|
api.Handle("/api/users", authenticated(apiAllUsersHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/users", authenticated(apiCreateUsersHandler, false)).Methods("POST")
|
api.Handle("/api/users", authenticated(apiCreateUsersHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/users/token", http.HandlerFunc(apiCheckUserTokenHandler)).Methods("POST")
|
api.Handle("/api/users/token", authenticated(apiCheckUserTokenHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/users/{id}", authenticated(apiUserHandler, false)).Methods("GET")
|
api.Handle("/api/users/{id}", authenticated(apiUserHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/users/{id}", authenticated(apiUserUpdateHandler, false)).Methods("POST")
|
api.Handle("/api/users/{id}", authenticated(apiUserUpdateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/users/{id}", authenticated(apiUserDeleteHandler, false)).Methods("DELETE")
|
api.Handle("/api/users/{id}", authenticated(apiUserDeleteHandler, false)).Methods("DELETE")
|
||||||
|
@ -164,9 +179,9 @@ func Router() *mux.Router {
|
||||||
api.Handle("/api/notifier/{notifier}/test", authenticated(testNotificationHandler, false)).Methods("POST")
|
api.Handle("/api/notifier/{notifier}/test", authenticated(testNotificationHandler, false)).Methods("POST")
|
||||||
|
|
||||||
// API MESSAGES Routes
|
// API MESSAGES Routes
|
||||||
api.Handle("/api/messages", scoped(apiAllMessagesHandler)).Methods("GET")
|
api.Handle("/api/messages", authenticatedV2(apiAllMessagesHandler)).Methods("GET")
|
||||||
api.Handle("/api/messages", authenticated(apiMessageCreateHandler, false)).Methods("POST")
|
api.Handle("/api/messages", authenticated(apiMessageCreateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/messages/{id}", scoped(apiMessageGetHandler)).Methods("GET")
|
api.Handle("/api/messages/{id}", authenticatedV2(apiMessageGetHandler)).Methods("GET")
|
||||||
api.Handle("/api/messages/{id}", authenticated(apiMessageUpdateHandler, false)).Methods("POST")
|
api.Handle("/api/messages/{id}", authenticated(apiMessageUpdateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/messages/{id}", authenticated(apiMessageDeleteHandler, false)).Methods("DELETE")
|
api.Handle("/api/messages/{id}", authenticated(apiMessageDeleteHandler, false)).Methods("DELETE")
|
||||||
|
|
||||||
|
@ -175,12 +190,13 @@ func Router() *mux.Router {
|
||||||
api.Handle("/api/checkins", authenticated(checkinCreateHandler, false)).Methods("POST")
|
api.Handle("/api/checkins", authenticated(checkinCreateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/checkins/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
api.Handle("/api/checkins/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/checkins/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
api.Handle("/api/checkins/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
||||||
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
//r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
||||||
|
|
||||||
// API Generic Routes
|
// API Generic Routes
|
||||||
r.Handle("/metrics", readOnly(promhttp.Handler(), false))
|
|
||||||
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
|
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
|
||||||
r.NotFoundHandler = http.HandlerFunc(baseHandler)
|
r.NotFoundHandler = http.HandlerFunc(baseHandler)
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/statping/statping/database"
|
"github.com/statping/statping/database"
|
||||||
"github.com/statping/statping/types/errors"
|
"github.com/statping/statping/types/errors"
|
||||||
|
@ -9,6 +10,8 @@ import (
|
||||||
"github.com/statping/statping/types/services"
|
"github.com/statping/statping/types/services"
|
||||||
"github.com/statping/statping/utils"
|
"github.com/statping/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type serviceOrder struct {
|
type serviceOrder struct {
|
||||||
|
@ -19,7 +22,7 @@ type serviceOrder struct {
|
||||||
func findService(r *http.Request) (*services.Service, error) {
|
func findService(r *http.Request) (*services.Service, error) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
id := utils.ToInt(vars["id"])
|
id := utils.ToInt(vars["id"])
|
||||||
servicer, err := services.Find(id)
|
servicer, err := services.FindOne(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -29,6 +32,19 @@ func findService(r *http.Request) (*services.Service, error) {
|
||||||
return servicer, nil
|
return servicer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findPublicSubService(r *http.Request, service *services.Service) (*services.Service, error) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
id := utils.ToInt(vars["sub_id"])
|
||||||
|
if val, ok := service.SubServicesDetails[id]; ok && val.Public {
|
||||||
|
sub, err := services.FindOne(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return sub, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Public sub service not mapped to parent")
|
||||||
|
}
|
||||||
|
|
||||||
func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
var newOrder []*serviceOrder
|
var newOrder []*serviceOrder
|
||||||
|
@ -38,13 +54,13 @@ func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range newOrder {
|
for _, s := range newOrder {
|
||||||
service, err := services.Find(s.Id)
|
service, err := services.FindOne(s.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
service.Order = s.Order
|
service.Order = s.Order
|
||||||
service.Update()
|
service.UpdateOrder()
|
||||||
}
|
}
|
||||||
returnJson(newOrder, w, r)
|
returnJson(newOrder, w, r)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +108,7 @@ func apiServicePatchHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.Online = req.Online
|
//service.Online = req.Online
|
||||||
service.Latency = req.Latency
|
service.Latency = req.Latency
|
||||||
|
|
||||||
issueDefault := "Service was triggered to be offline"
|
issueDefault := "Service was triggered to be offline"
|
||||||
|
@ -154,25 +170,29 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if objs, err := apiServiceFailureDataHandlerCore(r); err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
} else {
|
||||||
|
returnJson(objs, w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceFailureDataHandlerCore(r *http.Request) ([]*database.TimeValue, error) {
|
||||||
service, err := findService(r)
|
service, err := findService(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
groupQuery, err := database.ParseQueries(r, service.AllFailures())
|
groupQuery, err := database.ParseQueries(r, service.AllFailures())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
objs, err := groupQuery.GraphData(database.ByCount)
|
objs, err := groupQuery.GraphData(database.ByCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return objs, nil
|
||||||
returnJson(objs, w, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -198,44 +218,246 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiServiceTimeDataHandler(w http.ResponseWriter, r *http.Request) {
|
func apiServiceTimeDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if objs, err := apiServiceTimeDataHandlerCore(r); err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
} else {
|
||||||
|
returnJson(objs, w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceTimeDataHandlerCore(r *http.Request) (*services.UptimeSeries, error) {
|
||||||
service, err := findService(r)
|
service, err := findService(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
groupHits, err := database.ParseQueries(r, service.AllHits())
|
groupHits, err := database.ParseQueries(r, service.AllHits())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
groupFailures, err := database.ParseQueries(r, service.AllFailures())
|
groupFailures, err := database.ParseQueries(r, service.AllFailures())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var allFailures []*failures.Failure
|
var allFailures []*failures.Failure
|
||||||
var allHits []*hits.Hit
|
var allHits []*hits.Hit
|
||||||
|
|
||||||
if err := groupHits.Find(&allHits); err != nil {
|
if err := groupHits.Find(&allHits); err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := groupFailures.Find(&allFailures); err != nil {
|
if err := groupFailures.Find(&allFailures); err != nil {
|
||||||
sendErrorJson(err, w, r)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uptimeData, err := service.UptimeData(allHits, allFailures)
|
uptimeData, err := service.UptimeData(allHits, allFailures)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return uptimeData, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiSubServiceBlockSeriesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
service, err := findService(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
subService, err := findPublicSubService(r, service)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if objs, err := apiServiceBlockSeriesHandlerCoreV2(r, subService); err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
} else {
|
||||||
|
returnJson(objs, w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
returnJson(uptimeData, w, r)
|
func apiServiceBlockSeriesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
service, err := findService(r)
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if objs, err := apiServiceBlockSeriesHandlerCoreV2(r, service); err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
} else {
|
||||||
|
returnJson(objs, w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceBlockSeriesHandlerCore(r *http.Request, service *services.Service) (*services.BlockSeries, error) {
|
||||||
|
|
||||||
|
groupHits, err := database.ParseQueries(r, service.AllHits())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groupFailures, err := database.ParseQueries(r, service.AllFailures())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
failuresData, err := database.ParseQueries(r, service.AllFailures())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
objs, err := failuresData.GraphData(database.ByCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockSeries services.BlockSeries = services.BlockSeries{}
|
||||||
|
|
||||||
|
if len(objs) == 0 {
|
||||||
|
return &blockSeries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var allFailures []*failures.Failure
|
||||||
|
var allHits []*hits.Hit
|
||||||
|
|
||||||
|
if err := groupHits.Find(&allHits); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := groupFailures.Find(&allFailures); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
uptimeData, err := service.UptimeData(allHits, allFailures)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockSeries.Start = uptimeData.Start
|
||||||
|
blockSeries.End = uptimeData.End
|
||||||
|
blockSeries.Downtime = uptimeData.Downtime
|
||||||
|
blockSeries.Uptime = uptimeData.Uptime
|
||||||
|
blockSeries.Series = &[]services.Block{}
|
||||||
|
|
||||||
|
var nextFrameTime time.Time
|
||||||
|
|
||||||
|
for c := 0; c < len(objs); c++ {
|
||||||
|
|
||||||
|
currentFrame := objs[c]
|
||||||
|
currentFrameTime, _ := time.Parse("2006-01-02T15:04:05Z", currentFrame.Timeframe)
|
||||||
|
if c+1 < len(objs) {
|
||||||
|
nextFrameTime, _ = time.Parse("2006-01-02T15:04:05Z", objs[c+1].Timeframe)
|
||||||
|
} else {
|
||||||
|
nextFrameTime = uptimeData.End
|
||||||
|
}
|
||||||
|
|
||||||
|
block := services.Block{
|
||||||
|
Timeframe: currentFrame.Timeframe,
|
||||||
|
Status: services.STATUS_UP,
|
||||||
|
Downtimes: &[]services.Downtime{}}
|
||||||
|
|
||||||
|
for _, data := range uptimeData.Series {
|
||||||
|
|
||||||
|
if !data.Online && currentFrameTime.Before(data.Start) && nextFrameTime.After(data.Start) {
|
||||||
|
|
||||||
|
*block.Downtimes = append(*block.Downtimes, services.Downtime{
|
||||||
|
Start: data.Start,
|
||||||
|
End: data.End,
|
||||||
|
Duration: data.Duration,
|
||||||
|
SubStatus: services.HandleEmptyStatus(data.SubStatus),
|
||||||
|
})
|
||||||
|
|
||||||
|
if block.Status != services.STATUS_DOWN {
|
||||||
|
block.Status = services.HandleEmptyStatus(data.SubStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*blockSeries.Series = append(*blockSeries.Series, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &blockSeries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceBlockSeriesHandlerCoreV2(r *http.Request, service *services.Service) (*services.BlockSeries, error) {
|
||||||
|
|
||||||
|
failuresData, err := database.ParseQueries(r, service.AllFailures())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
objs, err := failuresData.NoFailureGraphData(database.ByCount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockSeries services.BlockSeries = services.BlockSeries{}
|
||||||
|
|
||||||
|
if len(objs) == 0 {
|
||||||
|
return &blockSeries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
uptimeData, downtimesList, err := service.DowntimeData(failuresData.Start, failuresData.End)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockSeries.Start = uptimeData.Start
|
||||||
|
blockSeries.End = uptimeData.End
|
||||||
|
blockSeries.Downtime = uptimeData.Downtime
|
||||||
|
blockSeries.Uptime = uptimeData.Uptime
|
||||||
|
blockSeries.Series = &[]services.Block{}
|
||||||
|
|
||||||
|
var nextFrameTime time.Time
|
||||||
|
|
||||||
|
for c := 0; c < len(objs); c++ {
|
||||||
|
|
||||||
|
currentFrame := objs[c]
|
||||||
|
currentFrameTime, _ := time.ParseInLocation("2006-01-02T15:04:05Z", currentFrame.Timeframe, time.Local)
|
||||||
|
if c+1 < len(objs) {
|
||||||
|
nextFrameTime, _ = time.ParseInLocation("2006-01-02T15:04:05Z", objs[c+1].Timeframe, time.Local)
|
||||||
|
} else {
|
||||||
|
nextFrameTime = uptimeData.End
|
||||||
|
}
|
||||||
|
|
||||||
|
block := services.Block{
|
||||||
|
Timeframe: currentFrame.Timeframe,
|
||||||
|
Status: services.STATUS_UP,
|
||||||
|
Downtimes: &[]services.Downtime{}}
|
||||||
|
|
||||||
|
for _, data := range *downtimesList {
|
||||||
|
|
||||||
|
if (currentFrameTime.Before(data.Start) && nextFrameTime.After(data.Start)) ||
|
||||||
|
(currentFrameTime.Before(data.End) && nextFrameTime.After(data.End)) ||
|
||||||
|
(currentFrameTime.After(data.Start) && nextFrameTime.Before(data.End)) {
|
||||||
|
|
||||||
|
start := data.Start
|
||||||
|
end := data.End
|
||||||
|
|
||||||
|
if currentFrameTime.After(data.Start) {
|
||||||
|
start = currentFrameTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextFrameTime.Before(data.End) {
|
||||||
|
end = nextFrameTime
|
||||||
|
}
|
||||||
|
|
||||||
|
*block.Downtimes = append(*block.Downtimes, services.Downtime{
|
||||||
|
Start: start,
|
||||||
|
End: end,
|
||||||
|
Duration: end.Sub(start).Milliseconds(),
|
||||||
|
SubStatus: services.HandleEmptyStatus(data.SubStatus),
|
||||||
|
})
|
||||||
|
|
||||||
|
if block.Status != services.STATUS_DOWN {
|
||||||
|
block.Status = services.HandleEmptyStatus(data.SubStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*blockSeries.Series = append(*blockSeries.Series, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &blockSeries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiServiceHitsDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
func apiServiceHitsDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -277,6 +499,29 @@ func apiAllServicesHandler(r *http.Request) interface{} {
|
||||||
return srvs
|
return srvs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiAllSubServicesHandler(r *http.Request) interface{} {
|
||||||
|
var srvs []services.Service
|
||||||
|
service, err := findService(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if service.Type != "collection" {
|
||||||
|
return fmt.Errorf("Not a supported operation")
|
||||||
|
}
|
||||||
|
for id, config := range service.SubServicesDetails {
|
||||||
|
if config.Public {
|
||||||
|
if sub, err := services.FindOne(id); err == nil {
|
||||||
|
subClone := *sub
|
||||||
|
subClone.DisplayName = config.DisplayName
|
||||||
|
srvs = append(srvs, subClone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(services.ServiceOrder(srvs))
|
||||||
|
|
||||||
|
return srvs
|
||||||
|
}
|
||||||
|
|
||||||
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
service, err := findService(r)
|
service, err := findService(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -63,11 +63,6 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := configs.TriggerSamples(); err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = confgs.MigrateDatabase(); err != nil {
|
if err = confgs.MigrateDatabase(); err != nil {
|
||||||
|
@ -139,7 +134,6 @@ func registerNews(email, domain string) error {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("email", email)
|
v.Set("email", email)
|
||||||
v.Set("domain", domain)
|
v.Set("domain", domain)
|
||||||
v.Set("timezone", "UTC")
|
|
||||||
resp, err := http.PostForm("https://news.statping.com/new", v)
|
resp, err := http.PostForm("https://news.statping.com/new", v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env*
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"name": "razorpay-statping",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "react-scripts start",
|
||||||
|
"build": "rm -rf build && react-scripts build",
|
||||||
|
"test": "react-scripts test",
|
||||||
|
"eject": "react-scripts eject"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@chakra-ui/icons": "^1.0.15",
|
||||||
|
"@chakra-ui/react": "^1.6.7",
|
||||||
|
"@chakra-ui/skip-nav": "^1.1.11",
|
||||||
|
"@emotion/react": "^11",
|
||||||
|
"@emotion/styled": "^11",
|
||||||
|
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.1.15",
|
||||||
|
"apexcharts": "^3.28.1",
|
||||||
|
"axios": "^0.21.1",
|
||||||
|
"date-fns": "^2.23.0",
|
||||||
|
"framer-motion": "^4",
|
||||||
|
"node-sass": "^6.0.1",
|
||||||
|
"react": "^17.0.2",
|
||||||
|
"react-apexcharts": "^1.3.9",
|
||||||
|
"react-dom": "^17.0.2",
|
||||||
|
"react-router-dom": "^5.2.0",
|
||||||
|
"react-scripts": "4.0.3",
|
||||||
|
"react-tooltip": "^4.2.21"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,39 @@
|
||||||
|
<!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="Razorpay - Status Page" />
|
||||||
|
<!--
|
||||||
|
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>Razorpay - Status Page</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>
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"short_name": "Razorpay",
|
||||||
|
"name": "Razorpay - Status Page",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
# https://www.robotstxt.org/robotstxt.html
|
||||||
|
User-agent: *
|
||||||
|
Disallow:
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from "react";
|
||||||
|
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
|
||||||
|
import { ChakraProvider } from "@chakra-ui/react";
|
||||||
|
import theme from "../theme";
|
||||||
|
import ServicesPage from "./ServicesPage";
|
||||||
|
import Navigation from "./Navbar";
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
console.log(`Application running on ${process.env.NODE_ENV} mode.`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChakraProvider theme={theme}>
|
||||||
|
<BrowserRouter>
|
||||||
|
<div className="app-layout">
|
||||||
|
<Navigation />
|
||||||
|
<Switch>
|
||||||
|
<Route exact path="/" component={ServicesPage} />
|
||||||
|
<Redirect from="*" to="/" />
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
</BrowserRouter>
|
||||||
|
</ChakraProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Box } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
function Badge(props) {
|
||||||
|
const { children, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="span"
|
||||||
|
backgroundColor="green.200"
|
||||||
|
color="white.100"
|
||||||
|
fontSize="xs"
|
||||||
|
py="px"
|
||||||
|
px="1"
|
||||||
|
ml="px"
|
||||||
|
borderRadius="sm"
|
||||||
|
fontWeight="bold"
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Badge;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from './Badge';
|
||||||
|
export * from './Badge';
|
|
@ -0,0 +1,48 @@
|
||||||
|
import React from "react";
|
||||||
|
import { useStyleConfig, chakra, Box, forwardRef } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
const Button = forwardRef((props, ref) => {
|
||||||
|
const {
|
||||||
|
variant = "solid",
|
||||||
|
colorScheme = "blue",
|
||||||
|
size = "md",
|
||||||
|
leftIcon,
|
||||||
|
rightIcon,
|
||||||
|
children,
|
||||||
|
onClick,
|
||||||
|
...rest
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const styles = useStyleConfig("Button", {
|
||||||
|
variant,
|
||||||
|
colorScheme,
|
||||||
|
size,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
/* using sx instead of __css so the styles cannot be overriden
|
||||||
|
directly. To pass new styles, check the theme
|
||||||
|
*/
|
||||||
|
<chakra.button
|
||||||
|
display="inline-block"
|
||||||
|
sx={styles}
|
||||||
|
ref={ref}
|
||||||
|
{...rest}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{leftIcon && (
|
||||||
|
<Box as="span" marginRight={2} position="relative" top="-px">
|
||||||
|
{leftIcon}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
{rightIcon && (
|
||||||
|
<Box as="span" marginLeft={2} position="relative" top="-px">
|
||||||
|
{rightIcon}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</chakra.button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Button;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './Button';
|
||||||
|
export { default } from './Button';
|
|
@ -0,0 +1,13 @@
|
||||||
|
import React from "react";
|
||||||
|
import { core } from "../utils/data";
|
||||||
|
|
||||||
|
const ContentHeader = () => {
|
||||||
|
return (
|
||||||
|
<div className="header">
|
||||||
|
<h1 className="header-title mt-4 mb-3 font-24 fw-700">{core.name}</h1>
|
||||||
|
<h5 className="header-description font-12">{core.description}</h5>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContentHeader;
|
|
@ -0,0 +1,39 @@
|
||||||
|
import React from "react";
|
||||||
|
// import { groups } from "../utils/data";
|
||||||
|
import GroupItem from "./GroupItem";
|
||||||
|
import { isObject, isObjectEmpty } from "../utils/helper";
|
||||||
|
|
||||||
|
function showPlus(service) {
|
||||||
|
let show = false;
|
||||||
|
if (
|
||||||
|
isObject(service.sub_services_detail) &&
|
||||||
|
!isObjectEmpty(service.sub_services_detail)
|
||||||
|
) {
|
||||||
|
const arr = Object.values(service.sub_services_detail);
|
||||||
|
const isPublic = arr.some((a) => a.public === true);
|
||||||
|
show = service.type === "collection" && isPublic;
|
||||||
|
}
|
||||||
|
return show;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Group = ({ services }) => {
|
||||||
|
// const data = groups.sort((a, b) => a.order_id - b.order_id);
|
||||||
|
// if (!data.length > 0) return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="list-group">
|
||||||
|
{services?.map((service) => {
|
||||||
|
const showPlusButton = showPlus(service);
|
||||||
|
return (
|
||||||
|
<GroupItem
|
||||||
|
key={service.id}
|
||||||
|
service={service}
|
||||||
|
showPlusButton={showPlusButton}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Group;
|
|
@ -0,0 +1,186 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import ReactTooltip from "react-tooltip";
|
||||||
|
import API from "../config/API";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import GroupServiceFailures from "./GroupServiceFailures";
|
||||||
|
import SubServiceCard from "./SubServiceCard";
|
||||||
|
// import IncidentsBlock from "./IncidentsBlock";
|
||||||
|
// import ServiceLoader from "./ServiceLoader";
|
||||||
|
// import DateUtils from "../utils/DateUtils";
|
||||||
|
import infoIcon from "../static/info.svg";
|
||||||
|
|
||||||
|
const GroupItem = ({ service, showPlusButton }) => {
|
||||||
|
const [collapse, setCollapse] = useState(false);
|
||||||
|
const [subServices, setSubServices] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [hoverText, setHoverText] = useState("");
|
||||||
|
|
||||||
|
// const groupServices = services
|
||||||
|
// .filter((s) => s.group_id === service.id)
|
||||||
|
// .sort((a, b) => a.order_id - b.order_id);
|
||||||
|
|
||||||
|
const fetchSubServices = async () => {
|
||||||
|
const data = await API.fetchSubServices(service.id);
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
const sorted_data = data.sort((a, b) => a.order_id - b.order_id);
|
||||||
|
setSubServices(sorted_data);
|
||||||
|
}
|
||||||
|
setCollapse(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openCollapse = () => {
|
||||||
|
if (subServices.length === 0) {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
fetchSubServices();
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCollapse(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeCollapse = () => {
|
||||||
|
setCollapse(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOver = (service) => {
|
||||||
|
setHoverText(service.description || service.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOut = () => setHoverText("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="service-card service_item card-bg pb-0">
|
||||||
|
{/** TODO: change span to navlink */}
|
||||||
|
<div className="service_item--header mb-3">
|
||||||
|
<div className="service_item--right">
|
||||||
|
{!loading && showPlusButton && (
|
||||||
|
<>
|
||||||
|
{collapse ? (
|
||||||
|
<button className="square-minus" onClick={closeCollapse} />
|
||||||
|
) : (
|
||||||
|
<button className="square-plus" onClick={openCollapse} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loading && <FontAwesomeIcon icon={faCircleNotch} spin />}
|
||||||
|
|
||||||
|
<span
|
||||||
|
className="subtitle no-decoration font-14 mr-1"
|
||||||
|
// to="/service/1"
|
||||||
|
>
|
||||||
|
{service.name}
|
||||||
|
</span>
|
||||||
|
{service?.description && (
|
||||||
|
<>
|
||||||
|
<ReactTooltip
|
||||||
|
id={`tooltip-${service.name}`}
|
||||||
|
effect="solid"
|
||||||
|
place="right"
|
||||||
|
backgroundColor="#344A6C"
|
||||||
|
className="tooltip"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
onMouseOver={() => handleMouseOver(service)}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
|
src={infoIcon}
|
||||||
|
alt="info-icon"
|
||||||
|
data-for={`tooltip-${service.name}`}
|
||||||
|
data-tip={hoverText}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="service_item--left">
|
||||||
|
<span
|
||||||
|
className={`badge float-right font-12 ${
|
||||||
|
service.online ? "uptime" : "downtime"
|
||||||
|
}`}
|
||||||
|
style={{ display: collapse ? "none" : "block" }}
|
||||||
|
>
|
||||||
|
{service.online ? langs("online") : langs("offline")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<GroupServiceFailures service={service} collapse={collapse} />
|
||||||
|
{/*<IncidentsBlock service={service} /> */}
|
||||||
|
<div
|
||||||
|
className="list-group online_list"
|
||||||
|
style={{ display: collapse ? "block" : "none" }}
|
||||||
|
>
|
||||||
|
{subServices && subServices?.length > 0 ? (
|
||||||
|
subServices.map((sub_service, i) => {
|
||||||
|
return (
|
||||||
|
<SubServiceCard
|
||||||
|
key={i}
|
||||||
|
group={service}
|
||||||
|
service={sub_service}
|
||||||
|
collapse={collapse}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<div className="subtitle text-align-center">No Services</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default React.memo(GroupItem);
|
||||||
|
|
||||||
|
// import React from "react";
|
||||||
|
// import langs from "../config/langs";
|
||||||
|
// import { services } from "../data";
|
||||||
|
// import GroupServiceFailures from "./GroupServiceFailures";
|
||||||
|
// import IncidentsBlock from "./IncidentsBlock";
|
||||||
|
// // import DateUtils from "../utils/DateUtils";
|
||||||
|
|
||||||
|
// const GroupItem = ({ group }) => {
|
||||||
|
// const groupServices = services
|
||||||
|
// .filter((s) => s.group_id === group.id)
|
||||||
|
// .sort((a, b) => a.order_id - b.order_id);
|
||||||
|
|
||||||
|
// if (!groupServices.length > 0) return null;
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="col-12 full-col-12">
|
||||||
|
// {group.name !== "Empty Group" && (
|
||||||
|
// <h4 className="group_header mb-3 mt-4">{group.name}</h4>
|
||||||
|
// )}
|
||||||
|
// <div className="list-group online_list mb-4">
|
||||||
|
// {groupServices.map((service, i) => {
|
||||||
|
// return (
|
||||||
|
// <div key={i} className="service-card service-card-action">
|
||||||
|
// <span
|
||||||
|
// className="no-decoration font-3"
|
||||||
|
// // to={DateUtils.serviceLink(service)}
|
||||||
|
// >
|
||||||
|
// {service.name}
|
||||||
|
// </span>
|
||||||
|
|
||||||
|
// <span
|
||||||
|
// className={`badge text-uppercase float-right ${
|
||||||
|
// service.online ? "bg-success" : "bg-danger"
|
||||||
|
// }`}
|
||||||
|
// >
|
||||||
|
// {service.online ? langs("online") : langs("offline")}
|
||||||
|
// </span>
|
||||||
|
// <GroupServiceFailures service={service} />
|
||||||
|
// <IncidentsBlock service={service} />
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// })}
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default GroupItem;
|
|
@ -0,0 +1,185 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
// import useIntersectionObserver from "../hooks/useIntersectionObserver";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import API from "../config/API";
|
||||||
|
import ServiceLoader from "./ServiceLoader";
|
||||||
|
import ReactTooltip from "react-tooltip";
|
||||||
|
import { STATUS_CLASS } from "../utils/constants";
|
||||||
|
import { calcPer, isObjectEmpty } from "../utils/helper";
|
||||||
|
|
||||||
|
const STATUS_TEXT = {
|
||||||
|
up: "Uptime",
|
||||||
|
down: "Down",
|
||||||
|
degraded: "Degraded",
|
||||||
|
};
|
||||||
|
|
||||||
|
const groupByStatus = (arr = []) => {
|
||||||
|
const res = arr.reduce((acc, val) => {
|
||||||
|
const { duration, sub_status } = val;
|
||||||
|
if (acc.hasOwnProperty(sub_status)) {
|
||||||
|
acc[sub_status]["duration"] += duration;
|
||||||
|
acc[sub_status]["count"] += 1;
|
||||||
|
} else {
|
||||||
|
acc[sub_status] = {
|
||||||
|
duration,
|
||||||
|
sub_status,
|
||||||
|
count: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatStatusDuration(obj) {
|
||||||
|
const arrayStr = Object.values(obj).map((d) => {
|
||||||
|
let duration = DateUtils.humanize(d.duration);
|
||||||
|
return `${STATUS_TEXT[d.sub_status]} for ${duration}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
return arrayStr.join("<br/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchFailureSeries(url) {
|
||||||
|
const { now, beginningOf, endOf, nowSubtract, toUnix } = DateUtils;
|
||||||
|
const start = beginningOf("day", nowSubtract(86400 * 89));
|
||||||
|
const end = endOf("day", now());
|
||||||
|
const data = await API.service_failures_data(
|
||||||
|
url,
|
||||||
|
toUnix(start),
|
||||||
|
toUnix(end),
|
||||||
|
"24h",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
// console.log(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GroupServiceFailures = ({ group = null, service, collapse }) => {
|
||||||
|
// const [containerRef, isVisible] = useIntersectionObserver({
|
||||||
|
// root: null,
|
||||||
|
// rootMargin: "0px",
|
||||||
|
// threshold: 1.0,
|
||||||
|
// });
|
||||||
|
|
||||||
|
const [hoverText, setHoverText] = useState("");
|
||||||
|
const [loaded, setLoaded] = useState(true);
|
||||||
|
const [failureData, setFailureData] = useState([]);
|
||||||
|
const [uptime, setUptime] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchData() {
|
||||||
|
let url = "/services";
|
||||||
|
try {
|
||||||
|
if (group) {
|
||||||
|
url += `/${group.id}/sub_services/${service.id}/block_series`;
|
||||||
|
} else {
|
||||||
|
url += `/${service.id}/block_series`;
|
||||||
|
}
|
||||||
|
const { series, downtime, uptime } = await fetchFailureSeries(url);
|
||||||
|
const failureData = [];
|
||||||
|
series.forEach((d) => {
|
||||||
|
let date = DateUtils.parseISO(d.timeframe);
|
||||||
|
date = DateUtils.format(date, "dd MMMM yyyy");
|
||||||
|
failureData.push({
|
||||||
|
timeframe: date,
|
||||||
|
status: d.status,
|
||||||
|
downtimes: groupByStatus(d.downtimes),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const percentage = calcPer(uptime, downtime);
|
||||||
|
setFailureData(failureData);
|
||||||
|
setUptime(percentage);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message);
|
||||||
|
} finally {
|
||||||
|
setLoaded(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, [service]);
|
||||||
|
|
||||||
|
const handleTooltip = (d) => {
|
||||||
|
let txt = "";
|
||||||
|
if (d.status === "up") {
|
||||||
|
txt = `${d.timeframe} - No Downtime`;
|
||||||
|
} else if (d.status === "down" && !isObjectEmpty(d.downtimes)) {
|
||||||
|
txt = `<div style="text-align:center;">
|
||||||
|
<div>${d.timeframe}</div>
|
||||||
|
<div>${formatStatusDuration(d.downtimes)}</div>
|
||||||
|
</div>`;
|
||||||
|
} else if (d.status === "degraded") {
|
||||||
|
txt = `<div style="text-align:center;">
|
||||||
|
<div>${d.timeframe}</div>
|
||||||
|
<div>${formatStatusDuration(d.downtimes)}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
return txt;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOver = (d) => {
|
||||||
|
// let date = DateUtils.parseISO(d.timeframe);
|
||||||
|
// date = date.toLocaleDateString();
|
||||||
|
const tooltipText = handleTooltip(d);
|
||||||
|
setHoverText(tooltipText);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOut = () => setHoverText("");
|
||||||
|
|
||||||
|
if (loaded) return <ServiceLoader text="Loading series.." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
// transition div
|
||||||
|
<div name="fade" style={{ display: collapse ? "none" : "block" }}>
|
||||||
|
<div className="block-chart">
|
||||||
|
<ReactTooltip
|
||||||
|
effect="solid"
|
||||||
|
place="bottom"
|
||||||
|
backgroundColor="#344A6C"
|
||||||
|
html={true}
|
||||||
|
/>
|
||||||
|
{failureData?.length > 0 ? (
|
||||||
|
failureData.map((d, i) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex-fill service_day ${STATUS_CLASS[d.status]}`}
|
||||||
|
onMouseOver={() => handleMouseOver(d)}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
|
key={i}
|
||||||
|
data-tip={hoverText}
|
||||||
|
>
|
||||||
|
{d.status !== 0 && (
|
||||||
|
<span className="d-none d-md-block text-center small"></span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<span className="description font-10">
|
||||||
|
Service does not have any successful hits
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="timeline">
|
||||||
|
<div className="no-select">
|
||||||
|
<p className="divided justify-content-between">
|
||||||
|
<span className="timeline-text font-12">
|
||||||
|
90 {langs("days_ago")}
|
||||||
|
</span>
|
||||||
|
{/* <span className="timeline-divider"></span> */}
|
||||||
|
{/* <span className="timeline-text font-12">{service_txt()}</span> */}
|
||||||
|
{/* <span className="timeline-text font-12">{uptime}% uptime</span> */}
|
||||||
|
<span className="timeline-divider"></span>
|
||||||
|
<span className="timeline-text font-12">{langs("today")}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* <div className="daily-failures small text-right text-dim">
|
||||||
|
{hoverText}
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GroupServiceFailures;
|
|
@ -0,0 +1,8 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Image as ChakraImage } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
const Image = (props) => (
|
||||||
|
<ChakraImage loading="lazy" ignoreFallback={true} {...props} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Image;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from './Image';
|
||||||
|
export * from './Image';
|
|
@ -0,0 +1,119 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { service, up_time_data } from "../data";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import API from "../config/API";
|
||||||
|
|
||||||
|
const IncidentService = () => {
|
||||||
|
async function lastDaysFailures() {
|
||||||
|
const { beginningOf, endOf, nowSubtract, parseISO, toUnix } = DateUtils;
|
||||||
|
const failureData = [];
|
||||||
|
// const start = beginningOf("day", nowSubtract(86400 * 90));
|
||||||
|
// const end = endOf("tomorrow");
|
||||||
|
const data = await API.service_failures_data();
|
||||||
|
data.forEach((d) => {
|
||||||
|
let date = DateUtils.parseISO(d.timeframe);
|
||||||
|
failureData.push({
|
||||||
|
month: date.getMonth(),
|
||||||
|
day: date.getDate(),
|
||||||
|
date: date,
|
||||||
|
amount: d.amount,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return failureData;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [hoverText, setHoverText] = useState("");
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
|
useEffect(async () => {
|
||||||
|
const failureData = await lastDaysFailures();
|
||||||
|
setData(failureData);
|
||||||
|
setLoaded(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseOut = () => {
|
||||||
|
setHoverText("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOver = (d) => {
|
||||||
|
const start = DateUtils.parseISO(d.start);
|
||||||
|
const end = DateUtils.parseISO(d.end);
|
||||||
|
const duration = DateUtils.humanTime(d.duration);
|
||||||
|
|
||||||
|
setHoverText(
|
||||||
|
`${start.toLocaleDateString()} - ${end.toLocaleDateString()}
|
||||||
|
${duration} ${d.online ? "Online" : "Offline"}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const service_txt = () => {
|
||||||
|
return DateUtils.smallText(service);
|
||||||
|
};
|
||||||
|
|
||||||
|
const total_time_frame = up_time_data.uptime + up_time_data.downtime;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="list-group online_list mb-4">
|
||||||
|
<div className="service-card service-card-action">
|
||||||
|
{/** TODO: change span to navlink */}
|
||||||
|
<span className="no-decoration font-3" t0="/service/1">
|
||||||
|
Status Ping
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`badge float-right ${
|
||||||
|
service.online ? "bg-success" : "bg-danger"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{service.online ? "ONLINE" : "OFFLINE"}
|
||||||
|
</span>
|
||||||
|
<div className="d-flex mt-3">
|
||||||
|
{up_time_data.series.map((d, i) => {
|
||||||
|
const time = ((d.duration * 100) / total_time_frame).toFixed(2);
|
||||||
|
const width = time < 0.1 ? 0.15 : time;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`service_day ${d.online ? "uptime" : "downtime"}`}
|
||||||
|
style={{ width: width + "%" }}
|
||||||
|
onMouseOver={() => handleMouseOver(d)}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
{d.amount !== 0 && (
|
||||||
|
<span className="d-none d-md-block text-center small"></span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="row mt-2">
|
||||||
|
<div className="col-12 no-select">
|
||||||
|
<p className="divided">
|
||||||
|
<span className="font-2 text-muted">
|
||||||
|
{DateUtils.duration(up_time_data.uptime, up_time_data.downtime)}{" "}
|
||||||
|
{langs("days_ago")}
|
||||||
|
</span>
|
||||||
|
<span className="divider"></span>
|
||||||
|
<span
|
||||||
|
className={`text-center font-2 ${
|
||||||
|
service.online ? "text-muted" : "text-danger"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{service_txt()}
|
||||||
|
</span>
|
||||||
|
<span className="divider"></span>
|
||||||
|
<span className="font-2 text-muted">{langs("today")}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="daily-failures small text-right text-dim">
|
||||||
|
{hoverText}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IncidentService;
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from "react";
|
||||||
|
// import API from "../config/API";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
|
||||||
|
const IncidentUpdate = ({ update, admin }) => {
|
||||||
|
// async loadUpdates() {
|
||||||
|
// this.updates = await Api.incident_updates(this.incident)
|
||||||
|
// }
|
||||||
|
|
||||||
|
const deleteUpdate = (update) => {
|
||||||
|
alert("Delete Incident:", update.incident);
|
||||||
|
// const res = await API.incident_update_delete(update);
|
||||||
|
// if (res.status === "success") {
|
||||||
|
// this.onUpdate();
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="col-12 mb-3 pb-2 border-bottom" role="alert">
|
||||||
|
<span
|
||||||
|
className={`
|
||||||
|
font-weight-bold text-capitalize
|
||||||
|
${update.type.toLowerCase() === "resolved" ? "text-success" : ""}
|
||||||
|
${update.type.toLowerCase() === "investigating" ? "text-danger" : ""}
|
||||||
|
${update.type.toLowerCase() === "update" ? "text-warning" : ""}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{update.type}
|
||||||
|
</span>
|
||||||
|
<span className="text-muted">
|
||||||
|
- {update.message}
|
||||||
|
{admin && (
|
||||||
|
<button
|
||||||
|
onClick={deleteUpdate(update)}
|
||||||
|
type="button"
|
||||||
|
className="close"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className="d-block small">
|
||||||
|
{DateUtils.ago(update.created_at)} ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IncidentUpdate;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import API from "../config/API";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
import IncidentUpdate from "./IncidentUpdate";
|
||||||
|
|
||||||
|
const IncidentsBlock = ({ service }) => {
|
||||||
|
const [incidents, setIncidents] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchData() {
|
||||||
|
const data = await API.incidents_service(service.id);
|
||||||
|
setIncidents(data);
|
||||||
|
}
|
||||||
|
fetchData();
|
||||||
|
}, [service.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="row">
|
||||||
|
{incidents?.map((incident, i) => {
|
||||||
|
return (
|
||||||
|
<div className="col-12 mt-2" key={i}>
|
||||||
|
<span className="braker mt-1 mb-3"></span>
|
||||||
|
<h6>
|
||||||
|
{incident.title}
|
||||||
|
<span className="font-2 float-right">
|
||||||
|
{DateUtils.niceDate(incident.created_at)}
|
||||||
|
</span>
|
||||||
|
</h6>
|
||||||
|
<div className="font-2 mb-3" v-html="incident.description"></div>
|
||||||
|
{incident.updates.map((update, i) => {
|
||||||
|
return <IncidentUpdate key={i} update={update} admin={false} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IncidentsBlock;
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Link as ReactRouterLink } from "react-router-dom";
|
||||||
|
import { Link as ChakraLink } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
HOME: "/",
|
||||||
|
ABOUT: "/about/",
|
||||||
|
};
|
||||||
|
|
||||||
|
const newInfraPaths = Object.values(paths);
|
||||||
|
|
||||||
|
const Link = (props) => {
|
||||||
|
const isPathPresentInNewInfra = newInfraPaths.includes(props.to);
|
||||||
|
if (
|
||||||
|
!isPathPresentInNewInfra || // old infra paths can't be linked to with react-router
|
||||||
|
props.to?.startsWith("http://") ||
|
||||||
|
props.to?.startsWith("https://")
|
||||||
|
) {
|
||||||
|
// if outer link, use normal ChakraLink without React Router
|
||||||
|
const { to, ...rest } = props;
|
||||||
|
return <ChakraLink href={to} {...rest} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link from React Router
|
||||||
|
return <ChakraLink as={ReactRouterLink} {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Link;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default } from './Link';
|
||||||
|
export * from './Link';
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from "react";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
|
||||||
|
const MessageBlock = ({ message }) => {
|
||||||
|
return (
|
||||||
|
<div className="card shadow mb-4" role="alert">
|
||||||
|
<div className="card-body pb-2">
|
||||||
|
<h3 className="mb-3 font-weight-bold">{message.title}</h3>
|
||||||
|
<span className="mb-2">{message.description}</span>
|
||||||
|
<div className="col-12 mb-0">
|
||||||
|
<div className="dates">
|
||||||
|
<div className="start">
|
||||||
|
<strong>STARTS</strong> {DateUtils.niceDate(message.start_on)}
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div className="ends">
|
||||||
|
<strong>ENDS</strong> {DateUtils.niceDate(message.end_on)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessageBlock;
|
|
@ -0,0 +1,650 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Box, Divider, Flex, useBreakpointValue } from "@chakra-ui/react";
|
||||||
|
import {
|
||||||
|
ChevronLeftIcon,
|
||||||
|
ChevronRightIcon,
|
||||||
|
CloseIcon,
|
||||||
|
HamburgerIcon,
|
||||||
|
} from "@chakra-ui/icons";
|
||||||
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import { isDescendant } from "../../utils/general";
|
||||||
|
|
||||||
|
import Link from "../Link";
|
||||||
|
import Text from "../Text";
|
||||||
|
import Image from "../Image";
|
||||||
|
import Badge from "../Badge";
|
||||||
|
|
||||||
|
export const NavContext = React.createContext({
|
||||||
|
showContent: false,
|
||||||
|
expandNavContent: () => {},
|
||||||
|
collapseNavContent: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function useNavBreakpointValue({ mobile, desktop }) {
|
||||||
|
return (
|
||||||
|
useBreakpointValue({
|
||||||
|
xxs: mobile,
|
||||||
|
sm: mobile,
|
||||||
|
md: mobile,
|
||||||
|
lg: desktop,
|
||||||
|
xl: desktop,
|
||||||
|
"2xl": desktop,
|
||||||
|
"3xl": desktop,
|
||||||
|
base: desktop, // apply to rest of the breakpoints
|
||||||
|
}) ?? desktop
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const NewBadge = () => <Badge ml="1.5">NEW</Badge>;
|
||||||
|
|
||||||
|
export const NavMenu = React.forwardRef((props, ref) => {
|
||||||
|
const { children } = props;
|
||||||
|
|
||||||
|
const parentNavRef = React.useRef(null);
|
||||||
|
|
||||||
|
const [showContent, setShowContent] = React.useState(false);
|
||||||
|
const expandNavContent = () => {
|
||||||
|
props.collapseAllNavSections();
|
||||||
|
setShowContent(true);
|
||||||
|
props.setActiveTab(parentNavRef);
|
||||||
|
};
|
||||||
|
const collapseNavContent = () => {
|
||||||
|
setShowContent(false);
|
||||||
|
props.setActiveTab(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({
|
||||||
|
// add properties that you may want to call from outside of Provider.
|
||||||
|
collapseNavContent,
|
||||||
|
expandNavContent,
|
||||||
|
showContent,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const navContextValue = {
|
||||||
|
showContent,
|
||||||
|
expandNavContent,
|
||||||
|
collapseNavContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
const blurHandler = (event) => {
|
||||||
|
const child = event.relatedTarget;
|
||||||
|
if (parentNavRef.current) {
|
||||||
|
if (!isDescendant(parentNavRef.current, child)) {
|
||||||
|
navContextValue.collapseNavContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hoverListeners = useNavBreakpointValue({
|
||||||
|
mobile: {},
|
||||||
|
desktop: {
|
||||||
|
onMouseEnter: navContextValue.expandNavContent,
|
||||||
|
onMouseLeave: navContextValue.collapseNavContent,
|
||||||
|
onFocus: navContextValue.expandNavContent,
|
||||||
|
onBlur: blurHandler,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NavContext.Provider value={navContextValue}>
|
||||||
|
<Box {...hoverListeners} ref={parentNavRef} position="relative">
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
</NavContext.Provider>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NavTitle = (props) => {
|
||||||
|
const navContextValue = React.useContext(NavContext);
|
||||||
|
const ariaHasPopup = useNavBreakpointValue({ mobile: false, desktop: true });
|
||||||
|
const renderAs = useNavBreakpointValue({
|
||||||
|
mobile: "div",
|
||||||
|
desktop: "button",
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as={renderAs}
|
||||||
|
color={{
|
||||||
|
xxs: "gray.200",
|
||||||
|
lg: navContextValue.showContent ? "blue.400" : "gray.200",
|
||||||
|
}}
|
||||||
|
fontWeight={{ xxs: "bold", lg: "initial" }}
|
||||||
|
fontSize={{ xxs: "sm", lg: "md" }}
|
||||||
|
paddingTop={{ xxs: "5", lg: "8" }}
|
||||||
|
paddingBottom="4"
|
||||||
|
px={{ xxs: "5", lg: "3", xl: "4" }}
|
||||||
|
textTransform={{ xxs: "uppercase", lg: "none" }}
|
||||||
|
aria-haspopup={ariaHasPopup}
|
||||||
|
aria-expanded={navContextValue.showContent}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavTitleLink = (props) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
paddingTop={{ xxs: "5", lg: "8" }}
|
||||||
|
paddingBottom="4"
|
||||||
|
px={{ xxs: "5", lg: "4" }}
|
||||||
|
color="gray.200"
|
||||||
|
_hover={{
|
||||||
|
color: "blue.400",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MotionBox = motion(Box);
|
||||||
|
|
||||||
|
export const NavContent = (props) => {
|
||||||
|
const navContextValue = React.useContext(NavContext);
|
||||||
|
const { children, ...rest } = props;
|
||||||
|
|
||||||
|
const contentAnimationVariants = useNavBreakpointValue({
|
||||||
|
mobile: {
|
||||||
|
open: {
|
||||||
|
x: 0,
|
||||||
|
display: "block",
|
||||||
|
transition: {
|
||||||
|
type: "tween",
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closed: {
|
||||||
|
x: "100vw",
|
||||||
|
transition: {
|
||||||
|
type: "tween",
|
||||||
|
duration: 0.15,
|
||||||
|
},
|
||||||
|
transitionEnd: {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
desktop: {
|
||||||
|
open: {
|
||||||
|
opacity: 1,
|
||||||
|
display: "block",
|
||||||
|
transition: {
|
||||||
|
duration: 0.25,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closed: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.2,
|
||||||
|
},
|
||||||
|
transitionEnd: {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MotionBox
|
||||||
|
position={{ xxs: "fixed", lg: "absolute" }}
|
||||||
|
top={{ xxs: "0", lg: "revert" }}
|
||||||
|
height={{ xxs: "100%", lg: "revert" }}
|
||||||
|
backgroundColor={{ xxs: "white.100", lg: "revert" }}
|
||||||
|
overflowY="auto"
|
||||||
|
display="none"
|
||||||
|
variants={contentAnimationVariants}
|
||||||
|
animate={navContextValue.showContent ? "open" : "closed"}
|
||||||
|
zIndex="1"
|
||||||
|
aria-hidden={!navContextValue.showContent}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</MotionBox>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavLinkTitle = (props) => {
|
||||||
|
const { children, isNew, ...rest } = props;
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
className="nav-link-title"
|
||||||
|
fontSize={{ xxs: "sm", lg: "md" }}
|
||||||
|
color="blue.800"
|
||||||
|
lineHeight="10"
|
||||||
|
display="inline-block"
|
||||||
|
fontWeight="bold"
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
{isNew && <NewBadge />}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavLinkThinTitle = (props) => (
|
||||||
|
<NavLinkTitle color="revert" fontWeight="semibold" fontSize="sm" {...props} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NavLinkDescription = (props) => (
|
||||||
|
<Text
|
||||||
|
className="nav-link-description"
|
||||||
|
fontSize={{ xxs: "xs", lg: "sm" }}
|
||||||
|
lineHeight={{ xxs: "6", lg: "9" }}
|
||||||
|
color="blue.600"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NavLinkMobileDescription = (props) => (
|
||||||
|
<NavLinkDescription display={{ xxs: "revert", lg: "none" }} {...props} />
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NavLink = (props) => {
|
||||||
|
const { children, icon, iconVariant, isNew, ...rest } = props;
|
||||||
|
|
||||||
|
const commonNavLinkStyles = {
|
||||||
|
display: "block",
|
||||||
|
px: { xxs: "5", lg: "6" },
|
||||||
|
py: { xxs: "2.5", lg: "4" },
|
||||||
|
borderRadius: "md",
|
||||||
|
".chakra-image": {
|
||||||
|
background:
|
||||||
|
iconVariant === "purple"
|
||||||
|
? "linear-gradient(188.57deg, #2095FF -16.05%, #6416FF 116.32%);"
|
||||||
|
: "blue.400",
|
||||||
|
},
|
||||||
|
".chakra-icon": {
|
||||||
|
opacity: 0,
|
||||||
|
position: "relative",
|
||||||
|
left: "0",
|
||||||
|
transition: "left .1s",
|
||||||
|
},
|
||||||
|
"&:hover, &:focus": {
|
||||||
|
boxShadow: "sm",
|
||||||
|
".nav-tab-payments-lower &": {
|
||||||
|
boxShadow: "none",
|
||||||
|
},
|
||||||
|
".nav-link-title": {
|
||||||
|
color: "blue.400",
|
||||||
|
transition: "color .1s",
|
||||||
|
},
|
||||||
|
".chakra-image": {
|
||||||
|
background: "linear-gradient(30.55deg, #6AC7FF 11.05%, #148AFF 90.09%)",
|
||||||
|
transition: "all .1s",
|
||||||
|
},
|
||||||
|
".chakra-icon": {
|
||||||
|
color: "blue.400",
|
||||||
|
opacity: 1,
|
||||||
|
left: "2",
|
||||||
|
transition: "left .1s",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// if NavLink has icon property, render the nav link with icon and description
|
||||||
|
if (Array.isArray(children) && icon) {
|
||||||
|
// Render nav link with icon and description
|
||||||
|
const [heading, paragraph] = children;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link sx={commonNavLinkStyles} {...rest}>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Box>
|
||||||
|
<Image
|
||||||
|
height="8"
|
||||||
|
width="8"
|
||||||
|
htmlHeight="32px"
|
||||||
|
htmlWidth="32px"
|
||||||
|
rounded="full"
|
||||||
|
src={icon}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box flex="1" paddingLeft="3">
|
||||||
|
<Box>
|
||||||
|
{heading}
|
||||||
|
{isNew && <NewBadge />}
|
||||||
|
<ChevronRightIcon w="5" h="5" />
|
||||||
|
</Box>
|
||||||
|
{paragraph}
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
} else if (icon) {
|
||||||
|
return (
|
||||||
|
<Link sx={{ ...commonNavLinkStyles, px: "5", py: "2.5" }} {...rest}>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Box>
|
||||||
|
<Image
|
||||||
|
htmlHeight="24px"
|
||||||
|
htmlWidth="24px"
|
||||||
|
height="6"
|
||||||
|
width="6"
|
||||||
|
rounded="full"
|
||||||
|
src={icon}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box fontSize="sm" color="gray.300" paddingLeft="2">
|
||||||
|
{children}
|
||||||
|
{isNew && <NewBadge />}
|
||||||
|
<ChevronRightIcon w="5" h="5" />
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Render plain Nav Link without image
|
||||||
|
const plainLinkStyles = {
|
||||||
|
fontWeight: { xxs: "revert", lg: "normal" },
|
||||||
|
letterSpacing: "tighter",
|
||||||
|
position: "relative",
|
||||||
|
fontSize: { xxs: "sm", lg: "md" },
|
||||||
|
py: "1",
|
||||||
|
my: "1.5",
|
||||||
|
px: { xxs: "6", lg: "4" },
|
||||||
|
lineHeight: "0",
|
||||||
|
color: "blue.700",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
borderLeftWidth: "4px",
|
||||||
|
borderLeftColor: "transparent",
|
||||||
|
"&:before": {
|
||||||
|
content: '""',
|
||||||
|
height: "74px",
|
||||||
|
width: "px",
|
||||||
|
background:
|
||||||
|
"linear-gradient(to bottom,rgba(0,0,0,0),rgba(0,0,0,0.267),rgba(0,0,0,0))",
|
||||||
|
position: "absolute",
|
||||||
|
left: "-2.1px",
|
||||||
|
top: "-20px",
|
||||||
|
transition: "opacity .2s ease",
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
".chakra-icon": {
|
||||||
|
position: "relative",
|
||||||
|
top: "px",
|
||||||
|
opacity: 0,
|
||||||
|
left: "0",
|
||||||
|
transition: "left .1s",
|
||||||
|
},
|
||||||
|
"&:hover, &:focus": {
|
||||||
|
color: "blue.400",
|
||||||
|
".chakra-icon": {
|
||||||
|
opacity: 1,
|
||||||
|
left: "2",
|
||||||
|
transition: "left .1s",
|
||||||
|
},
|
||||||
|
borderLeftColor: "blue.400",
|
||||||
|
"&:before": {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link sx={plainLinkStyles} {...rest}>
|
||||||
|
<Box display="inline-block" as="span">
|
||||||
|
{children}
|
||||||
|
{isNew && <NewBadge />}
|
||||||
|
</Box>{" "}
|
||||||
|
<ChevronRightIcon w="5" h="5" />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavColumnHeading = (props) => (
|
||||||
|
<Text
|
||||||
|
px="6"
|
||||||
|
mt={{ xxs: "5", lg: "revert" }}
|
||||||
|
fontSize="xs"
|
||||||
|
fontWeight="normal"
|
||||||
|
letterSpacing="wider"
|
||||||
|
color="gray.200"
|
||||||
|
paddingBottom="1"
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const NavTab = (props) => {
|
||||||
|
const { variant, ...rest } = props;
|
||||||
|
if (variant === "payments-lower") {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className="nav-tab-payments-lower"
|
||||||
|
mx={{ xxs: "0", lg: "6" }}
|
||||||
|
py={{ xxs: "0", lg: "4" }}
|
||||||
|
px={{ xxs: "0", lg: "3" }}
|
||||||
|
bgColor={{ xxs: "white.100", lg: "white.200" }}
|
||||||
|
borderRadius="0 0 0.375rem 0.375rem"
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === "without-link-description") {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className="nav-tab-without-link-description"
|
||||||
|
px={{ xxs: "0", lg: "7" }}
|
||||||
|
py={{ xxs: "0", lg: "10" }}
|
||||||
|
borderRadius="md"
|
||||||
|
bgColor="white.100"
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className="nav-tab-default"
|
||||||
|
px={{ xxs: "0", lg: "8" }}
|
||||||
|
py={{ xxs: "0", lg: "10" }}
|
||||||
|
borderRadius="lg"
|
||||||
|
bgColor="white.100"
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Underline = ({ activeTab }) => {
|
||||||
|
if (!activeTab) return null;
|
||||||
|
const bounds = activeTab.current?.getBoundingClientRect();
|
||||||
|
if (!bounds) return null;
|
||||||
|
const left = bounds.left + 15; // 15px padding
|
||||||
|
const width = bounds.width - 30;
|
||||||
|
return (
|
||||||
|
<MotionBox
|
||||||
|
bgColor="blue.400"
|
||||||
|
display={{ xxs: "none", lg: "revert" }}
|
||||||
|
height="1"
|
||||||
|
top="68px"
|
||||||
|
position="absolute"
|
||||||
|
animate={{ left: `${left}px` }}
|
||||||
|
pointerEvents="none"
|
||||||
|
width={width}
|
||||||
|
initial={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavDivider = () => (
|
||||||
|
<Box display={{ xxs: "revert", lg: "none" }} px="5" py="2">
|
||||||
|
<Divider borderColor="white.300" borderWidth="1px" />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mobile Components
|
||||||
|
|
||||||
|
export const NavMobileBackButton = (props) => {
|
||||||
|
const navContextValue = React.useContext(NavContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
display={{ xxs: "block", lg: "none" }}
|
||||||
|
py="4"
|
||||||
|
px="3"
|
||||||
|
borderBottom="1px"
|
||||||
|
borderBottomColor="white.300"
|
||||||
|
bgColor="white.100"
|
||||||
|
width="100%"
|
||||||
|
textAlign="left"
|
||||||
|
fontSize="md"
|
||||||
|
color="blue.900"
|
||||||
|
fontWeight="bold"
|
||||||
|
onClick={navContextValue.collapseNavContent}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon h="5" w="5" marginRight="3" aria-label="Back Icon" />
|
||||||
|
{props.children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use \<NavMobileExploreButton\> inside \<NavMenu\> or pass
|
||||||
|
* `openMenuRef` with reference to NavMenu to open
|
||||||
|
*/
|
||||||
|
export const NavMobileExploreButton = (props) => {
|
||||||
|
const navContextValue = React.useContext(NavContext);
|
||||||
|
let { showContent, collapseNavContent, expandNavContent } = navContextValue;
|
||||||
|
|
||||||
|
const toggleNavMenu = () => {
|
||||||
|
if (props.openMenuRef?.current) {
|
||||||
|
showContent = props.openMenuRef.current.showContent;
|
||||||
|
collapseNavContent = props.openMenuRef.current.collapseNavContent;
|
||||||
|
expandNavContent = props.openMenuRef.current.expandNavContent;
|
||||||
|
}
|
||||||
|
if (showContent) {
|
||||||
|
collapseNavContent();
|
||||||
|
} else {
|
||||||
|
expandNavContent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
color={props.icon ? "gray.300" : "blue.400"}
|
||||||
|
px="5"
|
||||||
|
py={props.icon ? "2.5" : "4"}
|
||||||
|
display={{ xxs: "flex", lg: "none" }}
|
||||||
|
position="relative"
|
||||||
|
alignItems="center"
|
||||||
|
onClick={toggleNavMenu}
|
||||||
|
width="100%"
|
||||||
|
>
|
||||||
|
{props.icon ? (
|
||||||
|
<Image
|
||||||
|
htmlHeight="32px"
|
||||||
|
htmlWidth="32px"
|
||||||
|
height="8"
|
||||||
|
width="8"
|
||||||
|
src={props.icon}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
<Text
|
||||||
|
paddingLeft={props.icon ? "2" : "0"}
|
||||||
|
as="span"
|
||||||
|
fontWeight={props.icon ? "normal" : "bold"}
|
||||||
|
fontSize="sm"
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Text>
|
||||||
|
<ChevronRightIcon
|
||||||
|
marginLeft="auto"
|
||||||
|
w="5"
|
||||||
|
h="5"
|
||||||
|
opacity={props.icon ? "0.4" : "1"}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NavMobileIconLink = (props) => {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
display={{ xxs: "block", lg: "none" }}
|
||||||
|
px="5"
|
||||||
|
py="2.5"
|
||||||
|
fontSize="sm"
|
||||||
|
color="gray.300"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<Box>
|
||||||
|
<Image
|
||||||
|
htmlHeight="32px"
|
||||||
|
htmlWidth="32px"
|
||||||
|
height="8"
|
||||||
|
width="8"
|
||||||
|
rounded="full"
|
||||||
|
src={props.icon}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box paddingLeft="2">{props.children}</Box>
|
||||||
|
</Flex>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MotionCloseIcon = motion(CloseIcon);
|
||||||
|
const MotionHamburgerIcon = motion(HamburgerIcon);
|
||||||
|
|
||||||
|
export const NavHamMenuButton = ({ isMobileNavOpen, toggleMobileNavMenu }) => {
|
||||||
|
const navHamIconVariants = {
|
||||||
|
initial: {
|
||||||
|
rotate: 0,
|
||||||
|
},
|
||||||
|
rotated: {
|
||||||
|
rotate: -180,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
as="button"
|
||||||
|
position={isMobileNavOpen ? "fixed" : "absolute"}
|
||||||
|
top="0"
|
||||||
|
right="0"
|
||||||
|
zIndex="navbutton"
|
||||||
|
padding={isMobileNavOpen ? "4" : "6"}
|
||||||
|
onClick={toggleMobileNavMenu}
|
||||||
|
display={{ xxs: "inline-block", lg: "none" }}
|
||||||
|
aria-expanded={isMobileNavOpen}
|
||||||
|
aria-label={isMobileNavOpen ? "Close Nav Menu" : "Open Nav Menu"}
|
||||||
|
>
|
||||||
|
<AnimatePresence>
|
||||||
|
{isMobileNavOpen ? (
|
||||||
|
<MotionCloseIcon
|
||||||
|
color="gray.900"
|
||||||
|
fontSize="sm"
|
||||||
|
zIndex="1"
|
||||||
|
initial="initial"
|
||||||
|
top="6"
|
||||||
|
right="6"
|
||||||
|
animate="rotated"
|
||||||
|
exit="initial"
|
||||||
|
variants={navHamIconVariants}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MotionHamburgerIcon
|
||||||
|
color="gray.900"
|
||||||
|
fontSize="xl"
|
||||||
|
initial="initial"
|
||||||
|
animate="rotated"
|
||||||
|
exit="initial"
|
||||||
|
variants={navHamIconVariants}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,899 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Box, Grid, Tooltip } from "@chakra-ui/react";
|
||||||
|
// import { ArrowForwardIcon } from "@chakra-ui/icons";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { SkipNavLink } from "@chakra-ui/skip-nav";
|
||||||
|
import Link from "../Link";
|
||||||
|
import Image from "../Image";
|
||||||
|
import Button from "../Button";
|
||||||
|
|
||||||
|
import RzpLogo from "../../static/razorpay-logo.svg";
|
||||||
|
import * as productIcons from "../../static/product-icons-blue";
|
||||||
|
import indiaFlagSvg from "./images/india-flag.svg";
|
||||||
|
|
||||||
|
import {
|
||||||
|
NavMenu,
|
||||||
|
NavTitle,
|
||||||
|
NavContent,
|
||||||
|
NavTab,
|
||||||
|
NavColumnHeading,
|
||||||
|
NavLink,
|
||||||
|
NavLinkTitle,
|
||||||
|
NavLinkDescription,
|
||||||
|
NavMobileExploreButton,
|
||||||
|
NavMobileBackButton,
|
||||||
|
Underline,
|
||||||
|
useNavBreakpointValue,
|
||||||
|
NavTitleLink,
|
||||||
|
NavMobileIconLink,
|
||||||
|
NavLinkMobileDescription,
|
||||||
|
NavLinkThinTitle,
|
||||||
|
NavDivider,
|
||||||
|
// NavHamMenuButton,
|
||||||
|
} from "./NavHelpers";
|
||||||
|
|
||||||
|
const MotionBox = motion(Box);
|
||||||
|
|
||||||
|
const RazorpayLogoLink = () => (
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
py={{ xxs: "6", lg: "7" }}
|
||||||
|
paddingRight={{ xxs: "0", lg: "22" }}
|
||||||
|
paddingLeft={{ xxs: "2", lg: "0" }}
|
||||||
|
display="inline-block"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
width="125px"
|
||||||
|
htmlWidth="125px"
|
||||||
|
height="auto"
|
||||||
|
src={RzpLogo}
|
||||||
|
alt="Razorpay Logo"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Navbar = (props) => {
|
||||||
|
const [isMobileNavOpen, setIsMobileNavOpen] = React.useState(false);
|
||||||
|
const [activeTab, setActiveTab] = React.useState(false);
|
||||||
|
|
||||||
|
const paymentsMenuRef = React.createRef();
|
||||||
|
const bankingMenuRef = React.createRef();
|
||||||
|
const resourcesMenuRef = React.createRef();
|
||||||
|
const supportMenuRef = React.createRef();
|
||||||
|
|
||||||
|
const collapseAllNavSections = () => {
|
||||||
|
const navMenuRefs = [
|
||||||
|
paymentsMenuRef,
|
||||||
|
bankingMenuRef,
|
||||||
|
resourcesMenuRef,
|
||||||
|
supportMenuRef,
|
||||||
|
];
|
||||||
|
for (const navMenuRef of navMenuRefs) {
|
||||||
|
navMenuRef.current?.collapseNavContent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleMobileNavMenu = () => {
|
||||||
|
setIsMobileNavOpen(!isMobileNavOpen);
|
||||||
|
if (isMobileNavOpen) {
|
||||||
|
// when closing navbar, collapse all tabs
|
||||||
|
collapseAllNavSections();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const navStatusVariants = useNavBreakpointValue({
|
||||||
|
mobile: {
|
||||||
|
open: {
|
||||||
|
x: "-100vw",
|
||||||
|
display: "block",
|
||||||
|
transition: {
|
||||||
|
type: "tween",
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closed: {
|
||||||
|
x: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.15,
|
||||||
|
},
|
||||||
|
transitionEnd: {
|
||||||
|
display: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
desktop: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
// bg={props.backgroundColor}
|
||||||
|
position="relative"
|
||||||
|
as="nav"
|
||||||
|
px={{ xxs: "2", md: "8", xl: "48" }}
|
||||||
|
>
|
||||||
|
<Box display="flex" maxWidth="1080px" margin="auto">
|
||||||
|
<SkipNavLink>Skip to content</SkipNavLink>
|
||||||
|
<RazorpayLogoLink />
|
||||||
|
{/* Ham Menu Button */}
|
||||||
|
{/* <NavHamMenuButton
|
||||||
|
isMobileNavOpen={isMobileNavOpen}
|
||||||
|
toggleMobileNavMenu={toggleMobileNavMenu}
|
||||||
|
/> */}
|
||||||
|
<MotionBox
|
||||||
|
className="nav-container"
|
||||||
|
display={{ xxs: "inline-block", lg: "flex" }}
|
||||||
|
position={{ xxs: "fixed", lg: "revert" }}
|
||||||
|
zIndex="navbar"
|
||||||
|
right={{ xxs: "-100%", lg: "revert" }}
|
||||||
|
width={{ xxs: "300px", lg: "revert" }}
|
||||||
|
bg={{ xxs: "white.100", lg: "revert" }}
|
||||||
|
height={{ xxs: "100%", lg: "revert" }}
|
||||||
|
overflowY={{ xxs: activeTab ? "revert" : "auto", lg: "revert" }}
|
||||||
|
variants={navStatusVariants}
|
||||||
|
animate={isMobileNavOpen ? "open" : "closed"}
|
||||||
|
aria-hidden={!isMobileNavOpen}
|
||||||
|
>
|
||||||
|
<NavMenu
|
||||||
|
ref={paymentsMenuRef}
|
||||||
|
setActiveTab={setActiveTab}
|
||||||
|
collapseAllNavSections={collapseAllNavSections}
|
||||||
|
>
|
||||||
|
<NavTitle>Payments</NavTitle>
|
||||||
|
<Box display={{ xxs: "revert", lg: "none" }}>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-gateway"
|
||||||
|
icon={productIcons.paymentGatewaySvg}
|
||||||
|
>
|
||||||
|
Payment Gateway
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-links"
|
||||||
|
icon={productIcons.paymentLinksSvg}
|
||||||
|
>
|
||||||
|
Payment Links
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-buttons"
|
||||||
|
icon={productIcons.paymentButtonsSvg}
|
||||||
|
isNew
|
||||||
|
>
|
||||||
|
Payment Buttons
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<NavMobileExploreButton>
|
||||||
|
Explore Payment Suite
|
||||||
|
</NavMobileExploreButton>
|
||||||
|
<NavDivider />
|
||||||
|
<NavContent
|
||||||
|
left={{ xxs: "0", md: "-40", xl: "-56" }}
|
||||||
|
width={{ xxs: "100%", md: "1000px", xl: "1225px" }}
|
||||||
|
maxWidth={{ xxs: "100%", md: "1000px", xl: "1225px" }}
|
||||||
|
>
|
||||||
|
<NavMobileBackButton>Razorpay Payment Suite</NavMobileBackButton>
|
||||||
|
<NavTab>
|
||||||
|
<Grid
|
||||||
|
templateColumns={{
|
||||||
|
xxs: "repeat(1, 1fr)",
|
||||||
|
lg: "repeat(3, 1fr)",
|
||||||
|
}}
|
||||||
|
gap="0"
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>ACCEPT PAYMENTS</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-gateway"
|
||||||
|
icon={productIcons.paymentGatewaySvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Payment Gateway</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Payments on your Website & App
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-links"
|
||||||
|
icon={productIcons.paymentLinksSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Payment Links</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Create & send links to collect money
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-pages"
|
||||||
|
icon={productIcons.paymentPagesSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Payment Pages</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Get paid with personalized pages
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payment-buttons"
|
||||||
|
icon={productIcons.paymentButtonsSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle isNew>Payment Buttons</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Create, Copy and Collect in 5 mins
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
{/* Gap in column 2 */}
|
||||||
|
<Box
|
||||||
|
display={{ xxs: "none", lg: "block" }}
|
||||||
|
height="8"
|
||||||
|
marginBottom="0.5"
|
||||||
|
/>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/capital/instant-settlements/"
|
||||||
|
icon={productIcons.instantSettlementSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Instant Settlement</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Customer payments settled faster
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/invoices"
|
||||||
|
icon={productIcons.invoicesSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Invoices</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Create & send GST compliant invoices
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/smart-collect"
|
||||||
|
icon={productIcons.smartCollectSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Smart Collect</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Automate NEFT, RTGS, IMPS payments
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/invoices"
|
||||||
|
icon={productIcons.subscriptionsSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Subscriptions</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Collect recurring subscription payments
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>DISBURSE PAYMENTS</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/route"
|
||||||
|
icon={productIcons.routeSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Route</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Split & manage market payments
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<NavColumnHeading mt={{ xxs: "5", lg: "17px" }}>
|
||||||
|
RISK AND FRAUD
|
||||||
|
</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/thirdwatch"
|
||||||
|
icon={productIcons.thirdwatchSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Thirdwatch</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Fight fraud with Artificial Intelligence
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
|
||||||
|
<NavColumnHeading mt={{ xxs: "5", lg: "17px" }}>
|
||||||
|
PARTNER APPS
|
||||||
|
</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/app-store"
|
||||||
|
icon={productIcons.appStoreSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle isNew>App Store</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Find right app for your business
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</NavTab>
|
||||||
|
<NavTab variant="payments-lower">
|
||||||
|
<Grid
|
||||||
|
templateColumns={{
|
||||||
|
xxs: "repeat(1, 1fr)",
|
||||||
|
lg: "repeat(3, 1fr)",
|
||||||
|
}}
|
||||||
|
gap="0"
|
||||||
|
>
|
||||||
|
<NavColumnHeading display={{ xxs: "revert", lg: "none" }}>
|
||||||
|
MORE
|
||||||
|
</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payments-app"
|
||||||
|
icon={productIcons.paymentsMobileAppSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle isNew>Payments Mobile App</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Track and Accept payments
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/cred-pay"
|
||||||
|
icon={productIcons.credPaySvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle isNew>CRED Pay</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Make payments using CRED coins
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/upi-autopay"
|
||||||
|
icon={productIcons.upiAutopaySvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle isNew>UPI AutoPay</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Recurring payments using UPI App
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Grid>
|
||||||
|
</NavTab>
|
||||||
|
<MobileBottomLinks
|
||||||
|
paymentsMenuRef={paymentsMenuRef}
|
||||||
|
bankingMenuRef={bankingMenuRef}
|
||||||
|
resourcesMenuRef={resourcesMenuRef}
|
||||||
|
supportMenuRef={supportMenuRef}
|
||||||
|
hideTab="payments"
|
||||||
|
/>
|
||||||
|
</NavContent>
|
||||||
|
</NavMenu>
|
||||||
|
<NavMenu
|
||||||
|
ref={bankingMenuRef}
|
||||||
|
setActiveTab={setActiveTab}
|
||||||
|
collapseAllNavSections={collapseAllNavSections}
|
||||||
|
>
|
||||||
|
<NavTitle display={{ xxs: "none", lg: "revert" }}>Banking</NavTitle>
|
||||||
|
<NavTitle display={{ xxs: "revert", lg: "none" }}>
|
||||||
|
Razorpay X - Banking Suite
|
||||||
|
</NavTitle>
|
||||||
|
<Box display={{ xxs: "revert", lg: "none" }}>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/current-accounts"
|
||||||
|
icon={productIcons.currentAcountSvg}
|
||||||
|
>
|
||||||
|
Current Accounts
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/vendor-payments"
|
||||||
|
icon={productIcons.vendorPaymentsSvg}
|
||||||
|
>
|
||||||
|
Vendor Payments
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<NavMobileExploreButton>
|
||||||
|
Explore Banking Suite
|
||||||
|
</NavMobileExploreButton>
|
||||||
|
<NavDivider />
|
||||||
|
<NavContent
|
||||||
|
left={{ xxs: "0", md: "-64", xl: "-20.7rem" }}
|
||||||
|
width={{ xxs: "100%", md: "1000px", xl: "1225px" }}
|
||||||
|
maxWidth={{ xxs: "100%", md: "1000px", xl: "1225px" }}
|
||||||
|
>
|
||||||
|
<NavMobileBackButton>Razorpay Banking Suite</NavMobileBackButton>
|
||||||
|
<NavTab>
|
||||||
|
<Grid
|
||||||
|
templateColumns={{
|
||||||
|
xxs: "repeat(1, 1fr)",
|
||||||
|
lg: "repeat(3, 1fr)",
|
||||||
|
}}
|
||||||
|
gap="0"
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>BUSINESS BANKING</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/"
|
||||||
|
icon={productIcons.razorpayXSvg}
|
||||||
|
iconVariant="purple"
|
||||||
|
>
|
||||||
|
<NavLinkTitle>RazorpayX</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Business Banking built for disruptors
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/vendor-payments/"
|
||||||
|
icon={productIcons.vendorPaymentsSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Vendor Payments</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Automate vendor invoice and TDS payments
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/payout-links/"
|
||||||
|
icon={productIcons.payoutLinkSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Payout Links</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Send money without recipient account details
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/payouts/"
|
||||||
|
icon={productIcons.payoutsSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Payouts</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
24x7, Instant & Automated Payouts
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Box
|
||||||
|
display={{ xxs: "none", lg: "block" }}
|
||||||
|
height="8"
|
||||||
|
marginBottom="0.5"
|
||||||
|
/>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/current-accounts/"
|
||||||
|
icon={productIcons.currentAcountSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Current Account</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Current Accounts for fast growing businesses
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/tax-payments/"
|
||||||
|
icon={productIcons.taxPaymentsSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Tax Payments</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Pay your business taxes in under 30 seconds
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/payroll/"
|
||||||
|
icon={productIcons.payrollSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Payroll</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Automate and execute payroll
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>CREDIT</NavColumnHeading>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/capital/"
|
||||||
|
icon={productIcons.capitalSvg}
|
||||||
|
iconVariant="purple"
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Razorpay Capital</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Get money for your business needs
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/capital/cash-advance/"
|
||||||
|
icon={productIcons.cashAdvanceSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Cash Advance</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Instant additional cash for business use
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/x/corporate-cards/"
|
||||||
|
icon={productIcons.corporateCardsSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Corporate Cards</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Credit Card for growing businesses
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
isExternal
|
||||||
|
to="https://razorpay.com/capital/working-capital-loans/"
|
||||||
|
icon={productIcons.workingCapitalLoansSvg}
|
||||||
|
>
|
||||||
|
<NavLinkTitle>Working Capital Loans</NavLinkTitle>
|
||||||
|
<NavLinkDescription>
|
||||||
|
Avail collateral-free business loans
|
||||||
|
</NavLinkDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</NavTab>
|
||||||
|
<MobileBottomLinks
|
||||||
|
paymentsMenuRef={paymentsMenuRef}
|
||||||
|
bankingMenuRef={bankingMenuRef}
|
||||||
|
resourcesMenuRef={resourcesMenuRef}
|
||||||
|
supportMenuRef={supportMenuRef}
|
||||||
|
hideTab="banking"
|
||||||
|
/>
|
||||||
|
</NavContent>
|
||||||
|
</NavMenu>
|
||||||
|
<NavMenu
|
||||||
|
ref={resourcesMenuRef}
|
||||||
|
setActiveTab={setActiveTab}
|
||||||
|
collapseAllNavSections={collapseAllNavSections}
|
||||||
|
>
|
||||||
|
<NavTitle display={{ xxs: "none", lg: "revert" }}>
|
||||||
|
Resources
|
||||||
|
</NavTitle>
|
||||||
|
<NavContent
|
||||||
|
width={{ xxs: "100%", lg: "840px" }}
|
||||||
|
maxWidth={{ xxs: "100%", lg: "840px" }}
|
||||||
|
left={{ xxs: "0", lg: "-56" }}
|
||||||
|
>
|
||||||
|
<NavMobileBackButton>Resources</NavMobileBackButton>
|
||||||
|
<NavTab variant="without-link-description">
|
||||||
|
<Grid
|
||||||
|
templateColumns={{
|
||||||
|
xxs: "repeat(1, 1fr)",
|
||||||
|
lg: "repeat(4, 1fr)",
|
||||||
|
}}
|
||||||
|
gap="0"
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>AWARENESS</NavColumnHeading>
|
||||||
|
<NavLink to="https://razorpay.com/blog" isExternal>
|
||||||
|
<NavLinkThinTitle>Blog</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Know about the nitty gritty of Payments, Banking & more!
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/learn" isExternal>
|
||||||
|
<NavLinkThinTitle>Learn</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Learn about Business Management, Freelance & more!
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/events" isExternal>
|
||||||
|
<NavLinkThinTitle>Events</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Learn more about Startups, Products, Sales and Funding
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/white-papers">
|
||||||
|
<NavLinkThinTitle>White papers</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
From data-driven fintech insights to best practices
|
||||||
|
around handling payments
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/customer-stories">
|
||||||
|
<NavLinkThinTitle>Customer Stories</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
50,00,000+ businesses powering payments with Razorpay
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>DEVELOPERS</NavColumnHeading>
|
||||||
|
<NavLink to="https://razorpay.com/docs">
|
||||||
|
<NavLinkThinTitle>Developer Docs</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Get started with SDKs here
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/integrations">
|
||||||
|
<NavLinkThinTitle>Integrations</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
30+ platforms that Razorpay supports
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/docs/api" isExternal>
|
||||||
|
<NavLinkThinTitle>API Reference</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Official references for the Razorpay APIs
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<NavColumnHeading>SOLUTIONS</NavColumnHeading>
|
||||||
|
<NavLink to="https://razorpay.com/solutions/saas">
|
||||||
|
<NavLinkThinTitle>SaaS</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Multi-channel, Multi-mode Payments Experience
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/solutions/e-commerce">
|
||||||
|
<NavLinkThinTitle>E-commerce</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Seamlessly accept, manage and disburse money!
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/solutions/education">
|
||||||
|
<NavLinkThinTitle>Education</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Facilitate learning & growth for your students &
|
||||||
|
customers
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/solutions/bfsi">
|
||||||
|
<NavLinkThinTitle>BFSI</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Solve unique challenges across lending, wealth
|
||||||
|
management, and insurance sectors
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/freelancer-unregistered-business">
|
||||||
|
<NavLinkThinTitle>Freelance</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Accept both domestic & international payments from your
|
||||||
|
clients and cutomers!
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
<Box minWidth={{ base: "revert", lg: "250px" }}>
|
||||||
|
<NavColumnHeading>FREE TOOLS</NavColumnHeading>
|
||||||
|
<NavLink to="https://razorpay.com/gst-calculator">
|
||||||
|
<NavLinkThinTitle isNew>GST Calculator</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
The easiest way for businesses to calculate their GST
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/x/tds-online-payment/?ref=topnav">
|
||||||
|
<NavLinkThinTitle isNew>
|
||||||
|
Online TDS Payment
|
||||||
|
</NavLinkThinTitle>
|
||||||
|
<NavLinkMobileDescription>
|
||||||
|
Pay TDS for your business in 30 seconds
|
||||||
|
</NavLinkMobileDescription>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</NavTab>
|
||||||
|
<MobileBottomLinks
|
||||||
|
paymentsMenuRef={paymentsMenuRef}
|
||||||
|
bankingMenuRef={bankingMenuRef}
|
||||||
|
resourcesMenuRef={resourcesMenuRef}
|
||||||
|
supportMenuRef={supportMenuRef}
|
||||||
|
hideTab="resources"
|
||||||
|
/>
|
||||||
|
</NavContent>
|
||||||
|
</NavMenu>
|
||||||
|
<NavMenu
|
||||||
|
ref={supportMenuRef}
|
||||||
|
setActiveTab={setActiveTab}
|
||||||
|
collapseAllNavSections={collapseAllNavSections}
|
||||||
|
>
|
||||||
|
<NavTitle display={{ xxs: "none", lg: "revert" }}>Support</NavTitle>
|
||||||
|
<NavContent
|
||||||
|
width={{ xxs: "100%", lg: "272px" }}
|
||||||
|
maxWidth={{ xxs: "100%", lg: "272px" }}
|
||||||
|
left={{ xxs: "0", lg: "-20" }}
|
||||||
|
>
|
||||||
|
<NavMobileBackButton>Support</NavMobileBackButton>
|
||||||
|
<NavTab variant="without-link-description">
|
||||||
|
<Box py={{ xxs: "4", lg: "0" }}>
|
||||||
|
<NavColumnHeading>GET SUPPORT</NavColumnHeading>
|
||||||
|
<NavLink to="https://razorpay.com/support#request" isExternal>
|
||||||
|
<NavLinkThinTitle>Raise a request</NavLinkThinTitle>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/knowledgebase" isExternal>
|
||||||
|
<NavLinkThinTitle>Knowledgebase</NavLinkThinTitle>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/chargeback" isExternal>
|
||||||
|
<NavLinkThinTitle>Chargeback Guides</NavLinkThinTitle>
|
||||||
|
</NavLink>
|
||||||
|
<NavLink to="https://razorpay.com/settlement" isExternal>
|
||||||
|
<NavLinkThinTitle>Settlement Guides</NavLinkThinTitle>
|
||||||
|
</NavLink>
|
||||||
|
</Box>
|
||||||
|
</NavTab>
|
||||||
|
<MobileBottomLinks
|
||||||
|
paymentsMenuRef={paymentsMenuRef}
|
||||||
|
bankingMenuRef={bankingMenuRef}
|
||||||
|
resourcesMenuRef={resourcesMenuRef}
|
||||||
|
supportMenuRef={supportMenuRef}
|
||||||
|
hideTab="support"
|
||||||
|
/>
|
||||||
|
</NavContent>
|
||||||
|
</NavMenu>
|
||||||
|
|
||||||
|
{/* Desktop Links */}
|
||||||
|
<NavTitleLink
|
||||||
|
display={{ xxs: "none", lg: "inline-block" }}
|
||||||
|
to="https://razorpay.com/partners"
|
||||||
|
>
|
||||||
|
Partners
|
||||||
|
</NavTitleLink>
|
||||||
|
<NavTitleLink
|
||||||
|
display={{ xxs: "none", lg: "inline-block" }}
|
||||||
|
to="https://razorpay.com/pricing"
|
||||||
|
>
|
||||||
|
Pricing
|
||||||
|
</NavTitleLink>
|
||||||
|
{/* Mobile Links */}
|
||||||
|
<MobileBottomLinks
|
||||||
|
paymentsMenuRef={paymentsMenuRef}
|
||||||
|
bankingMenuRef={bankingMenuRef}
|
||||||
|
resourcesMenuRef={resourcesMenuRef}
|
||||||
|
supportMenuRef={supportMenuRef}
|
||||||
|
backgroundColor="white.100"
|
||||||
|
hideTab={["payments", "banking"]}
|
||||||
|
mt="0"
|
||||||
|
pt="2"
|
||||||
|
/>
|
||||||
|
<Underline activeTab={activeTab} />
|
||||||
|
</MotionBox>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
marginLeft="auto"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
my="auto"
|
||||||
|
paddingRight={{ xxs: "16", lg: "0" }}
|
||||||
|
py={{ xxs: "4", lg: "6" }}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
width="56"
|
||||||
|
textAlign="center"
|
||||||
|
label="Razorpay is currently available only for Indian businesses"
|
||||||
|
hasArrow
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
role="group"
|
||||||
|
as="span"
|
||||||
|
marginRight="4"
|
||||||
|
aria-label="Razorpay is currently available only for Indian businesses"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
display={{ base: "none", xxs: "none", lg: "inline-block" }}
|
||||||
|
src={indiaFlagSvg}
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
size="sm"
|
||||||
|
colorScheme="white"
|
||||||
|
to="https://dashboard.razorpay.com/#/access/signin"
|
||||||
|
>
|
||||||
|
Log In
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
to="https://dashboard.razorpay.com/signup"
|
||||||
|
marginLeft={{ base: "1", xs: "4" }}
|
||||||
|
size="sm"
|
||||||
|
colorScheme="white"
|
||||||
|
display={{ base: "none", xxs: "none", lg: "inline-block" }}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MobileBottomLinks = (props) => {
|
||||||
|
const {
|
||||||
|
paymentsMenuRef,
|
||||||
|
bankingMenuRef,
|
||||||
|
resourcesMenuRef,
|
||||||
|
supportMenuRef,
|
||||||
|
hideTab,
|
||||||
|
...rest
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
display={{ xxs: "revert", lg: "none" }}
|
||||||
|
backgroundColor="gray.100"
|
||||||
|
py="6"
|
||||||
|
mt="8"
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{!hideTab?.includes("payments") && (
|
||||||
|
<NavMobileExploreButton
|
||||||
|
openMenuRef={paymentsMenuRef}
|
||||||
|
icon={productIcons.explorePaymentsSvg}
|
||||||
|
>
|
||||||
|
Explore Payments Suite
|
||||||
|
</NavMobileExploreButton>
|
||||||
|
)}
|
||||||
|
{!hideTab?.includes("banking") && (
|
||||||
|
<NavMobileExploreButton
|
||||||
|
openMenuRef={bankingMenuRef}
|
||||||
|
icon={productIcons.exploreBankingSvg}
|
||||||
|
>
|
||||||
|
Explore Banking Suite
|
||||||
|
</NavMobileExploreButton>
|
||||||
|
)}
|
||||||
|
<NavMobileIconLink
|
||||||
|
to="https://razorpay.com/pricing"
|
||||||
|
isExternal
|
||||||
|
icon={productIcons.pricingSvg}
|
||||||
|
color="yellow.200"
|
||||||
|
>
|
||||||
|
Pricing
|
||||||
|
</NavMobileIconLink>
|
||||||
|
{!hideTab?.includes("resources") && (
|
||||||
|
<NavMobileExploreButton
|
||||||
|
openMenuRef={resourcesMenuRef}
|
||||||
|
icon={productIcons.resourcesSvg}
|
||||||
|
>
|
||||||
|
Resources
|
||||||
|
</NavMobileExploreButton>
|
||||||
|
)}
|
||||||
|
<NavMobileIconLink
|
||||||
|
to="https://razorpay.com/partners"
|
||||||
|
isExternal
|
||||||
|
icon={productIcons.partnersSvg}
|
||||||
|
>
|
||||||
|
Partners (Refer & Earn)
|
||||||
|
</NavMobileIconLink>
|
||||||
|
{!hideTab?.includes("support") && (
|
||||||
|
<NavMobileExploreButton
|
||||||
|
openMenuRef={supportMenuRef}
|
||||||
|
icon={productIcons.supportSvg}
|
||||||
|
>
|
||||||
|
Support
|
||||||
|
</NavMobileExploreButton>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
display={{ xxs: "revert", lg: "none" }}
|
||||||
|
px="4"
|
||||||
|
py="2"
|
||||||
|
position="sticky"
|
||||||
|
bottom="0px"
|
||||||
|
width="100%"
|
||||||
|
backgroundColor="white.100"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
display="block"
|
||||||
|
textAlign="center"
|
||||||
|
size="sm"
|
||||||
|
to="https://dashboard.razorpay.com/#/access/signin"
|
||||||
|
>
|
||||||
|
Log In
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navbar;
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="20" viewBox="0 0 28 20"><defs><filter id="a" width="129.2%" height="143.8%" x="-14.6%" y="-15.6%" filterUnits="objectBoundingBox"><feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1"/><feColorMatrix in="shadowBlurOuter1" result="shadowMatrixOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g fill="none" filter="url(#a)" transform="translate(2 1)"><path fill="#FAB446" d="M23.95 5.34H0V.44C0 .2.18 0 .41 0h23.13c.23 0 .41.2.41.42v4.9z"/><path fill="#73AF00" d="M23.54 15.99H.41a.42.42 0 0 1-.41-.42v-4.9h23.95v4.9c0 .23-.18.42-.41.42z"/><path fill="#F5F5F5" d="M0 5.34h23.95v5.32H0z"/><g fill="#41479B" transform="translate(9.73 5.71)"><path d="M2.25 4.55A2.24 2.24 0 0 1 .03 2.29c0-1.25 1-2.26 2.22-2.26a2.24 2.24 0 0 1 2.22 2.26c0 1.24-1 2.26-2.22 2.26zm0-4.1c-1 0-1.81.82-1.81 1.84a1.81 1.81 0 1 0 3.62.01c0-1.02-.8-1.84-1.8-1.84z"/><ellipse cx="2.25" cy="2.29" rx="1" ry="1"/><path d="M2.22 2.31l-.5-.41L.8.86.85.81l1.02.94.4.5zm0 0l.4.51 1.02.94.06-.05-.93-1.04-.5-.41z"/><path d="M2.22 2.26l.4-.5L3.65.8l.06.05-.93 1.04-.5.41zm0 0l-.5.41L.8 3.71l.05.05 1.02-.94.4-.5z"/><path d="M2.26 2.32l-.57.31-1.29.48-.03-.08 1.25-.6.61-.18z"/><path d="M2.26 2.32l.62-.19 1.24-.6-.03-.06-1.3.47-.56.31z"/><path d="M2.21 2.3l-.3-.58L1.44.41l.07-.03.59 1.26.18.63z"/><path d="M2.21 2.3l.19.63.58 1.26.07-.03-.47-1.31-.3-.58z"/><path d="M2.23 2.32l-.61-.19-1.25-.6.03-.06 1.3.47.56.31z"/><path d="M2.23 2.32l.57.31 1.3.48.02-.08-1.24-.6-.62-.18z"/><path d="M2.21 2.27l.19-.63.58-1.26.07.03-.47 1.31-.3.58zm0 0l-.3.58-.47 1.31.07.03.59-1.26.18-.63z"/></g><ellipse cx="11.98" cy="8" fill="#F5F5F5" rx="1" ry="1"/></g></svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,92 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Box, Tooltip } from "@chakra-ui/react";
|
||||||
|
import Link from "../Link";
|
||||||
|
import Image from "../Image";
|
||||||
|
import Button from "../Button";
|
||||||
|
|
||||||
|
import RzpLogo from "../../static/razorpay-logo-white.svg";
|
||||||
|
import indiaFlagSvg from "./images/india-flag.svg";
|
||||||
|
|
||||||
|
const RazorpayLogoLink = () => (
|
||||||
|
<Link
|
||||||
|
to="https://razorpay.com/"
|
||||||
|
isExternal
|
||||||
|
py={{ xxs: "6", lg: "7" }}
|
||||||
|
paddingRight={{ xxs: "0", lg: "22" }}
|
||||||
|
paddingLeft={{ xxs: "2", lg: "0" }}
|
||||||
|
display="inline-block"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
width="125px"
|
||||||
|
htmlWidth="125px"
|
||||||
|
height="auto"
|
||||||
|
src={RzpLogo}
|
||||||
|
alt="Razorpay Logo"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Navigation = () => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
// bg={props.backgroundColor}
|
||||||
|
position="relative"
|
||||||
|
as="nav"
|
||||||
|
px={{ xxs: "2", md: "8", xl: "48" }}
|
||||||
|
>
|
||||||
|
<Box display="flex" maxWidth="1080px" margin="auto">
|
||||||
|
<RazorpayLogoLink />
|
||||||
|
<Box
|
||||||
|
marginLeft="auto"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
my="auto"
|
||||||
|
paddingRight={{ xxs: "16", lg: "0" }}
|
||||||
|
py={{ xxs: "4", lg: "6" }}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
width="56"
|
||||||
|
textAlign="center"
|
||||||
|
label="Razorpay is currently available only for Indian businesses"
|
||||||
|
hasArrow
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
role="group"
|
||||||
|
as="span"
|
||||||
|
marginRight="4"
|
||||||
|
aria-label="Razorpay is currently available only for Indian businesses"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
display={{ base: "none", xxs: "none", lg: "inline-block" }}
|
||||||
|
src={indiaFlagSvg}
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
size="sm"
|
||||||
|
colorScheme="link"
|
||||||
|
to="https://dashboard.razorpay.com/#/access/signin"
|
||||||
|
>
|
||||||
|
Log In
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
as={Link}
|
||||||
|
to="https://dashboard.razorpay.com/signup"
|
||||||
|
marginLeft={{ base: "1", xs: "4" }}
|
||||||
|
size="sm"
|
||||||
|
colorScheme="white"
|
||||||
|
display={{ base: "none", xxs: "none", lg: "inline-block" }}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navigation;
|
|
@ -0,0 +1,252 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
import ServiceChart from "./ServiceChart";
|
||||||
|
import ServiceTopStats from "./ServiceTopStats";
|
||||||
|
|
||||||
|
const timeset = (seconds) => {
|
||||||
|
return DateUtils.toUnix(DateUtils.nowSubtract(seconds));
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeframes = [
|
||||||
|
{ value: timeset(1800), text: "30 Minutes", set: 1 },
|
||||||
|
{ value: timeset(3600), text: "1 Hour", set: 2 },
|
||||||
|
{ value: timeset(21600), text: "6 Hours", set: 3 },
|
||||||
|
{ value: timeset(43200), text: "12 Hours", set: 4 },
|
||||||
|
{ value: timeset(86400), text: "1 Day", set: 5 },
|
||||||
|
{ value: timeset(259200), text: "3 Days", set: 6 },
|
||||||
|
{ value: timeset(604800), text: "7 Days", set: 7 },
|
||||||
|
{ value: timeset(1209600), text: "14 Days", set: 8 },
|
||||||
|
{ value: timeset(2592000), text: "1 Month", set: 9 },
|
||||||
|
{ value: timeset(7776000), text: "3 Months", set: 10 },
|
||||||
|
{ value: 0, text: "All Records" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const intervals = [
|
||||||
|
{ value: "1m", text: "1/min", set: 1 },
|
||||||
|
{ value: "5m", text: "5/min", set: 2 },
|
||||||
|
{ value: "15m", text: "15/min", set: 3 },
|
||||||
|
{ value: "30m", text: "30/min", set: 4 },
|
||||||
|
{ value: "60m", text: "1/hr", set: 5 },
|
||||||
|
{ value: "180m", text: "3/hr", set: 6 },
|
||||||
|
{ value: "360m", text: "6/hr", set: 7 },
|
||||||
|
{ value: "720m", text: "12/hr", set: 8 },
|
||||||
|
{ value: "1440m", text: "1/day", set: 9 },
|
||||||
|
{ value: "4320m", text: "3/day", set: 10 },
|
||||||
|
{ value: "10080m", text: "7/day", set: 11 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
total_failures: {
|
||||||
|
title: "Total Failures",
|
||||||
|
subtitle: "Last 7 Days",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
high_latency: {
|
||||||
|
title: "Highest Latency",
|
||||||
|
subtitle: "Last 7 Days",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
lowest_latency: {
|
||||||
|
title: "Lowest Latency",
|
||||||
|
subtitle: "Last 7 Days",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
high_ping: {
|
||||||
|
title: "Highest Ping",
|
||||||
|
subtitle: "Last 7 Days",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
low_ping: {
|
||||||
|
title: "Lowest Ping",
|
||||||
|
subtitle: "Last 7 Days",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ServiceBlock = ({ service }) => {
|
||||||
|
const history = useHistory();
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [dropDownMenu, setDropDownMenu] = useState(false);
|
||||||
|
const [intervalMenu, setIntervalMenu] = useState(false);
|
||||||
|
const [intervalVal, setIntervalVal] = useState("60m");
|
||||||
|
const [timeframeVal, setTimeframeVal] = useState(timeset(259200));
|
||||||
|
// const [service, setService] = useState(null);
|
||||||
|
|
||||||
|
const timeframepick = timeframes.find((s) => s.value === timeframeVal);
|
||||||
|
|
||||||
|
const intervalpick = intervals.find((s) => s.value === intervalVal);
|
||||||
|
|
||||||
|
const chartTimeframe = {
|
||||||
|
start_time: timeframeVal,
|
||||||
|
interval: intervalVal,
|
||||||
|
};
|
||||||
|
|
||||||
|
const disabled_interval = (interval) => {
|
||||||
|
let min = timeframepick.set - interval.set - 1;
|
||||||
|
return min >= interval.set;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openMenu = (tm) => {
|
||||||
|
if (tm === "interval") {
|
||||||
|
setIntervalMenu(!intervalMenu);
|
||||||
|
setDropDownMenu(false);
|
||||||
|
} else if (tm === "timeframe") {
|
||||||
|
setDropDownMenu(!dropDownMenu);
|
||||||
|
setIntervalMenu(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeInterval = (interval) => {
|
||||||
|
setIntervalVal(interval.value);
|
||||||
|
setIntervalMenu(false);
|
||||||
|
setDropDownMenu(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeTimeframe = (timeframe) => {
|
||||||
|
setTimeframeVal(timeframe.value);
|
||||||
|
setIntervalMenu(false);
|
||||||
|
setDropDownMenu(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlesetService = () => {
|
||||||
|
// setService(service);
|
||||||
|
history.push("/service/" + service.id, {
|
||||||
|
props: { service: service },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const visibleChart = (isVisible, entry) => {
|
||||||
|
if (isVisible && !visible) {
|
||||||
|
setVisible(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-md-4 mb-4">
|
||||||
|
<div className={`card index-chart ${expanded ? "expanded-service" : ""}`}>
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="col-12">
|
||||||
|
<h4 className="mt-2">
|
||||||
|
<span
|
||||||
|
className="d-inline-block text-truncate font-4"
|
||||||
|
style={{ maxWidth: "65vw" }}
|
||||||
|
// to={serviceLink(service)}
|
||||||
|
// in_service={service}
|
||||||
|
>
|
||||||
|
{service.name}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className={`badge float-right ${
|
||||||
|
service.online ? "bg-success" : "bg-danger"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{service.online ? "ONLINE" : "OFFLINE"}
|
||||||
|
</span>
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<ServiceTopStats service={service} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <div
|
||||||
|
// v-observe-visibility="{ callback: visibleChart, throttle: 200 }"
|
||||||
|
className="chart-container"
|
||||||
|
>
|
||||||
|
<ServiceChart
|
||||||
|
service={service}
|
||||||
|
visible={visible}
|
||||||
|
chartTimeframe={chartTimeframe}
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`row lower_canvas full-col-12 text-white ${
|
||||||
|
service.online ? "bg-success" : "bg-danger"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="col-md-10 col-6">
|
||||||
|
<div className={`dropup ${dropDownMenu ? "show" : ""}`}>
|
||||||
|
<button
|
||||||
|
style={{ fontSize: "10pt" }}
|
||||||
|
onClick={() => openMenu("timeframe")}
|
||||||
|
type="button"
|
||||||
|
className="col-4 float-left btn btn-sm float-right btn-block text-white dropdown-toggle service_scale pr-2"
|
||||||
|
>
|
||||||
|
{timeframepick.text}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className={`service-tm-menu ${!dropDownMenu ? "d-none" : ""}`}
|
||||||
|
>
|
||||||
|
{timeframes.map((timeframe, i) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={() => changeTimeframe(timeframe)}
|
||||||
|
className={`dropdown-item ${
|
||||||
|
timeframepick === timeframe ? "active" : ""
|
||||||
|
}`}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
{timeframe.text}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`dropup ${intervalMenu ? "show" : ""}`}>
|
||||||
|
<button
|
||||||
|
style={{ fontSize: "10pt" }}
|
||||||
|
onClick={() => openMenu("interval")}
|
||||||
|
type="button"
|
||||||
|
className="col-4 float-left btn btn-sm float-right btn-block text-white dropdown-toggle service_scale pr-2"
|
||||||
|
>
|
||||||
|
{intervalpick.text}
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
className={`service-tm-menu ${!intervalMenu ? "d-none" : ""}`}
|
||||||
|
>
|
||||||
|
{intervals.map((interval, i) => {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={() => changeInterval(interval)}
|
||||||
|
className={`dropdown-item ${
|
||||||
|
intervalpick === interval ? "active" : ""
|
||||||
|
}`}
|
||||||
|
disabled={disabled_interval(interval)}
|
||||||
|
key={i}
|
||||||
|
>
|
||||||
|
{interval.text}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="d-none float-left d-md-inline">
|
||||||
|
{DateUtils.smallText(service)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <div className="col-md-2 col-6 float-right">
|
||||||
|
<button
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
// onClick={handlesetService}
|
||||||
|
className={`btn btn-sm float-right dyn-dark text-white ${
|
||||||
|
service.online ? "bg-success" : "bg-danger"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{langs("view")}
|
||||||
|
</button>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceBlock;
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React from "react";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import GroupServiceFailures from "./GroupServiceFailures";
|
||||||
|
import IncidentsBlock from "./IncidentsBlock";
|
||||||
|
|
||||||
|
const ServiceCard = ({ service }) => {
|
||||||
|
return (
|
||||||
|
<div className="service-card service_item card-bg">
|
||||||
|
{/** TODO: change span to navlink */}
|
||||||
|
<div className="service_item--header">
|
||||||
|
<div className="service_item--right">
|
||||||
|
{service.type === "collection" && (
|
||||||
|
<span className="square-plus"></span>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className="subtitle no-decoration font-14"
|
||||||
|
// to="/service/1"
|
||||||
|
>
|
||||||
|
{service.name}
|
||||||
|
</span>
|
||||||
|
{/* <span className="info">i</span> */}
|
||||||
|
</div>
|
||||||
|
<div className="service_item--left">
|
||||||
|
<span
|
||||||
|
className={`badge float-right font-12 ${
|
||||||
|
service.online ? "status-green" : "status-red"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{service.online ? langs("online") : langs("offline")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<GroupServiceFailures service={service} />
|
||||||
|
{/* <IncidentsBlock service={service} /> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceCard;
|
|
@ -0,0 +1,273 @@
|
||||||
|
import React from "react";
|
||||||
|
import Chart from "react-apexcharts";
|
||||||
|
|
||||||
|
const ServiceChart = () => {
|
||||||
|
const state = {
|
||||||
|
options: {
|
||||||
|
chart: {
|
||||||
|
type: "area",
|
||||||
|
height: 350,
|
||||||
|
},
|
||||||
|
stroke: { curve: "straight" },
|
||||||
|
xaxis: {
|
||||||
|
type: "datetime",
|
||||||
|
categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "series-1",
|
||||||
|
data: [30, 40, 45, 50, 49, 60, 70, 91],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="app">
|
||||||
|
<div className="row">
|
||||||
|
<div className="mixed-chart">
|
||||||
|
<Chart options={state.options} series={state.series} width="500" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceChart;
|
||||||
|
|
||||||
|
// import React, { useState, useEffect } from "react";
|
||||||
|
// import ReactApexChart from "react-apexcharts";
|
||||||
|
// import API from "../config/API";
|
||||||
|
// import DateUtils from "../utils/DateUtils";
|
||||||
|
|
||||||
|
// const timeoptions = {
|
||||||
|
// weekday: "long",
|
||||||
|
// year: "numeric",
|
||||||
|
// month: "long",
|
||||||
|
// day: "numeric",
|
||||||
|
// hour: "numeric",
|
||||||
|
// minute: "numeric",
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const axisOptions = {
|
||||||
|
// labels: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// crosshairs: {
|
||||||
|
// show: true,
|
||||||
|
// },
|
||||||
|
// lines: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// tooltip: {
|
||||||
|
// enabled: true,
|
||||||
|
// },
|
||||||
|
// axisTicks: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// grid: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const ServiceChart = ({ service, visible, chartTimeframe }) => {
|
||||||
|
// const [ready, setReady] = useState(false);
|
||||||
|
// const [showing, setShowing] = useState(null);
|
||||||
|
// const [data, setData] = useState(null);
|
||||||
|
// const [pingData, setPingData] = useState(null);
|
||||||
|
// const [series, setSeries] = useState(null);
|
||||||
|
|
||||||
|
// const state = {
|
||||||
|
// options: {
|
||||||
|
// noData: {
|
||||||
|
// text: "Loading...",
|
||||||
|
// },
|
||||||
|
// chart: {
|
||||||
|
// id: "ping-chart",
|
||||||
|
// height: "100%",
|
||||||
|
// width: "100%",
|
||||||
|
// type: "area",
|
||||||
|
// animations: {
|
||||||
|
// enabled: true,
|
||||||
|
// easing: "easeinout",
|
||||||
|
// speed: 800,
|
||||||
|
// animateGradually: {
|
||||||
|
// enabled: false,
|
||||||
|
// delay: 400,
|
||||||
|
// },
|
||||||
|
// dynamicAnimation: {
|
||||||
|
// enabled: true,
|
||||||
|
// speed: 500,
|
||||||
|
// },
|
||||||
|
// hover: {
|
||||||
|
// animationDuration: 0, // duration of animations when hovering an item
|
||||||
|
// },
|
||||||
|
// responsiveAnimationDuration: 0,
|
||||||
|
// },
|
||||||
|
// selection: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// zoom: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// toolbar: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// grid: {
|
||||||
|
// show: false,
|
||||||
|
// padding: {
|
||||||
|
// top: 0,
|
||||||
|
// right: 0,
|
||||||
|
// bottom: 0,
|
||||||
|
// left: -10,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// dropShadow: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// xaxis: {
|
||||||
|
// type: "datetime",
|
||||||
|
// labels: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// tooltip: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// yaxis: {
|
||||||
|
// labels: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// markers: {
|
||||||
|
// size: 0,
|
||||||
|
// strokeWidth: 0,
|
||||||
|
// hover: {
|
||||||
|
// size: undefined,
|
||||||
|
// sizeOffset: 0,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// tooltip: {
|
||||||
|
// theme: false,
|
||||||
|
// enabled: true,
|
||||||
|
// custom: ({ series, seriesIndex, dataPointIndex, w }) => {
|
||||||
|
// let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||||
|
// const dt = new Date(ts).toLocaleDateString("en-us", timeoptions);
|
||||||
|
// let val = series[0][dataPointIndex];
|
||||||
|
// let pingVal = series[1][dataPointIndex];
|
||||||
|
// return `<div class="chartmarker">
|
||||||
|
// <span>Average Response Time: ${DateUtils.humanTime(val)}/${
|
||||||
|
// chartTimeframe.interval
|
||||||
|
// }</span>
|
||||||
|
// <span>Average Ping: ${DateUtils.humanTime(pingVal)}/${
|
||||||
|
// chartTimeframe.interval
|
||||||
|
// }</span>
|
||||||
|
// <span>${dt}</span>
|
||||||
|
// </div>`;
|
||||||
|
// },
|
||||||
|
// fixed: {
|
||||||
|
// enabled: true,
|
||||||
|
// position: "topRight",
|
||||||
|
// offsetX: -30,
|
||||||
|
// offsetY: 0,
|
||||||
|
// },
|
||||||
|
// x: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// y: {
|
||||||
|
// formatter: (value) => {
|
||||||
|
// return value + " %";
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// legend: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// dataLabels: {
|
||||||
|
// enabled: false,
|
||||||
|
// },
|
||||||
|
// floating: true,
|
||||||
|
// axisTicks: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// axisBorder: {
|
||||||
|
// show: false,
|
||||||
|
// },
|
||||||
|
// fill: {
|
||||||
|
// colors: service.online
|
||||||
|
// ? ["#3dc82f", "#48d338"]
|
||||||
|
// : ["#c60f20", "#dd3545"],
|
||||||
|
// opacity: 1,
|
||||||
|
// type: "solid",
|
||||||
|
// },
|
||||||
|
// stroke: {
|
||||||
|
// show: false,
|
||||||
|
// curve: "smooth",
|
||||||
|
// lineCap: "butt",
|
||||||
|
// colors: service.online
|
||||||
|
// ? ["#38bc2a", "#48d338"]
|
||||||
|
// : ["#c60f20", "#dd3545"],
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const chartHits = async (val) => {
|
||||||
|
// setReady(false);
|
||||||
|
// const end = DateUtils.endOf("hour", DateUtils.now());
|
||||||
|
// const start = DateUtils.beginningOf(
|
||||||
|
// "hour",
|
||||||
|
// DateUtils.fromUnix(val.start_time)
|
||||||
|
// );
|
||||||
|
// const hits_data = await API.service_hits(
|
||||||
|
// service.id,
|
||||||
|
// DateUtils.toUnix(start),
|
||||||
|
// DateUtils.toUnix(end),
|
||||||
|
// val.interval,
|
||||||
|
// false
|
||||||
|
// );
|
||||||
|
// const ping_data = await API.service_ping(
|
||||||
|
// service.id,
|
||||||
|
// DateUtils.toUnix(start),
|
||||||
|
// DateUtils.toUnix(end),
|
||||||
|
// val.interval,
|
||||||
|
// false
|
||||||
|
// );
|
||||||
|
|
||||||
|
// const series = [
|
||||||
|
// { name: "Latency", ...DateUtils.convertToChartData(hits_data) },
|
||||||
|
// { name: "Ping", ...DateUtils.convertToChartData(ping_data) },
|
||||||
|
// ];
|
||||||
|
// setSeries(series);
|
||||||
|
// setReady(true);
|
||||||
|
// };
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// chartHits();
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <div className="app">
|
||||||
|
// <div className="row">
|
||||||
|
// <div className="mixed-chart">
|
||||||
|
// {ready ? (
|
||||||
|
// <ReactApexChart
|
||||||
|
// options={state.options}
|
||||||
|
// series={series}
|
||||||
|
// type="bar"
|
||||||
|
// width="500"
|
||||||
|
// class="service-chart"
|
||||||
|
// width="100%"
|
||||||
|
// height="100%"
|
||||||
|
// type="area"
|
||||||
|
// />
|
||||||
|
// ) : (
|
||||||
|
// <span>Loading Chart...</span>
|
||||||
|
// )}
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default ServiceChart;
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from "react";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
const ServiceLoader = ({ text = "Loading..." }) => {
|
||||||
|
return (
|
||||||
|
<div className="row mt-5 mb-5">
|
||||||
|
<div className="col-12 mt-5 mb-2 text-center">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faCircleNotch}
|
||||||
|
className="text-dim"
|
||||||
|
size="2x"
|
||||||
|
spin
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 text-center mt-3 mb-3">
|
||||||
|
<span className="text-dim">{text}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceLoader;
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from "react";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
|
||||||
|
const ServiceTopStats = ({ service }) => {
|
||||||
|
return (
|
||||||
|
<div className="row stats_area mt-5 mb-4">
|
||||||
|
<div className="col-4">
|
||||||
|
<span className="font-5 d-block font-weight-bold">
|
||||||
|
{DateUtils.humanTime(service.avg_response)}
|
||||||
|
</span>
|
||||||
|
<span className="font-1 subtitle">{langs("average_response")}</span>
|
||||||
|
</div>
|
||||||
|
<div className="col-4">
|
||||||
|
<span className="font-5 d-block font-weight-bold">
|
||||||
|
{service.online_24_hours} %
|
||||||
|
</span>
|
||||||
|
<span className="font-1 subtitle">
|
||||||
|
{langs("last_uptime")}
|
||||||
|
{/* {langs("last_uptime", [24, $tc("hour", 24)])} */}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="col-4">
|
||||||
|
<span className="font-5 d-block font-weight-bold">
|
||||||
|
{service.online_7_days} %
|
||||||
|
</span>
|
||||||
|
<span className="font-1 subtitle">
|
||||||
|
{langs("last_uptime")}
|
||||||
|
{/* {langs("last_uptime", [7, $tc("day", 7)])} */}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceTopStats;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { services } from "../utils/data";
|
||||||
|
import ServiceCard from "./ServiceCard";
|
||||||
|
|
||||||
|
const ServicesList = ({ services }) => {
|
||||||
|
// const [state, setstate] = useState([]);
|
||||||
|
|
||||||
|
// useEffect(() => {
|
||||||
|
// const data = services
|
||||||
|
// .filter((g) => g.group_id === 0)
|
||||||
|
// .sort((a, b) => a.order_id - b.order_id);
|
||||||
|
// setstate(data);
|
||||||
|
// }, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="list-group online_list">
|
||||||
|
{services?.map((service) => {
|
||||||
|
return <ServiceCard key={service.id} service={service} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicesList;
|
|
@ -0,0 +1,116 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
// import { NavLink } from "react-router-dom";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import {
|
||||||
|
faCheckCircle,
|
||||||
|
faExclamationCircle,
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import DateUtils from "../utils/DateUtils";
|
||||||
|
import Group from "./Group";
|
||||||
|
import ContentHeader from "./ContentHeader";
|
||||||
|
import ServiceLoader from "./ServiceLoader";
|
||||||
|
// import IncidentService from "./IncidentService";
|
||||||
|
// import MessageBlock from "./MessageBlock";
|
||||||
|
// import ServiceBlock from "./ServiceBlock";
|
||||||
|
// import ServicesList from "./ServicesList";
|
||||||
|
import API from "../config/API";
|
||||||
|
import { STATUS_COLOR, STATUS_TEXT } from "../utils/constants";
|
||||||
|
import { findStatus } from "../utils/helper";
|
||||||
|
|
||||||
|
const ServicesPage = () => {
|
||||||
|
// const data = messages.filter((m) => inRange(m) && m.service === 0);
|
||||||
|
const [services, setServices] = useState([]);
|
||||||
|
const [status, setStatus] = useState(true);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [poll, setPolling] = useState(1);
|
||||||
|
const today = DateUtils.format(new Date(), "d MMMM yyyy, hh:mm aaa");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
setPolling((prev) => (prev += 1));
|
||||||
|
}, 120000);
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, [poll]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchServices = async () => {
|
||||||
|
try {
|
||||||
|
const data = await API.fetchServices();
|
||||||
|
const status = findStatus(data);
|
||||||
|
const sorted_data = data.sort((a, b) => a.order_id - b.order_id);
|
||||||
|
setServices(sorted_data);
|
||||||
|
setStatus(status);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchServices();
|
||||||
|
}, [poll]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container col-md-7 col-sm-12 sm-container">
|
||||||
|
<ContentHeader />
|
||||||
|
<div className="app-content">
|
||||||
|
<div className="service">
|
||||||
|
<h2 className="title font-20 fw-700">Razorpay Payments</h2>
|
||||||
|
<div className="d-flex align-items-center subtitle font-12 mt-2">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={status === "up" ? faCheckCircle : faExclamationCircle}
|
||||||
|
style={{
|
||||||
|
fontSize: "16px",
|
||||||
|
color: STATUS_COLOR[status],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="mx-1">{STATUS_TEXT[status]}</span>
|
||||||
|
<span className="date">{today}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading && <ServiceLoader text="Loading Services" />}
|
||||||
|
|
||||||
|
{/* <ServicesList loading={loading} services={services} /> */}
|
||||||
|
|
||||||
|
{/* TODO --> Grouped Services to Accordian*/}
|
||||||
|
{services && services.length > 0 ? (
|
||||||
|
<Group services={services} />
|
||||||
|
) : (
|
||||||
|
<div className="description text-align-center">No Services</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* <div>
|
||||||
|
{data.map((message) => {
|
||||||
|
return <MessageBlock key={message.id} message={message} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{services.map((service) => {
|
||||||
|
return <ServiceBlock key={service.id} service={service} />;
|
||||||
|
})}
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
<div className="app-footer">
|
||||||
|
<div className="service-status">
|
||||||
|
<span className="service-status-badge uptime"></span>
|
||||||
|
<span className="description font-12">100% Uptime</span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div className="service-status">
|
||||||
|
<span className="service-status-badge degraded"></span>
|
||||||
|
<span className="description font-12">Partial degradation</span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
<div className="service-status">
|
||||||
|
<span className="service-status-badge downtime"></span>
|
||||||
|
<span className="description font-12">Downtime</span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicesPage;
|
|
@ -0,0 +1,66 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import ReactTooltip from "react-tooltip";
|
||||||
|
import langs from "../config/langs";
|
||||||
|
import GroupServiceFailures from "./GroupServiceFailures";
|
||||||
|
// import IncidentsBlock from "./IncidentsBlock";
|
||||||
|
import infoIcon from "../static/info.svg";
|
||||||
|
|
||||||
|
const SubServiceCard = ({ group, service }) => {
|
||||||
|
const [hoverText, setHoverText] = useState("");
|
||||||
|
|
||||||
|
const handleMouseOver = (service) => {
|
||||||
|
setHoverText(service.description || service.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOut = () => setHoverText("");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="service-card service_item border-radius-0 border-right-0 border-left-0">
|
||||||
|
{/** TODO: change span to navlink */}
|
||||||
|
|
||||||
|
<div className="service_item--header">
|
||||||
|
<div className="service_item--right">
|
||||||
|
<span
|
||||||
|
className="subtitle no-decoration font-14 mr-1"
|
||||||
|
// to="/service/1"
|
||||||
|
>
|
||||||
|
{service.name}
|
||||||
|
</span>
|
||||||
|
{service?.description && (
|
||||||
|
<>
|
||||||
|
<ReactTooltip
|
||||||
|
id={`tooltip-${service.name}`}
|
||||||
|
effect="solid"
|
||||||
|
place="right"
|
||||||
|
backgroundColor="#344A6C"
|
||||||
|
className="tooltip"
|
||||||
|
/>
|
||||||
|
<img
|
||||||
|
onMouseOver={() => handleMouseOver(service)}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
|
src={infoIcon}
|
||||||
|
alt="info-icon"
|
||||||
|
data-for={`tooltip-${service.name}`}
|
||||||
|
data-tip={hoverText}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="service_item--left">
|
||||||
|
<span
|
||||||
|
className={`badge float-right font-12 ${
|
||||||
|
service.online ? "status-green" : "status-red"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{service.online ? langs("online") : langs("offline")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<GroupServiceFailures group={group} service={service} />
|
||||||
|
{/* <IncidentsBlock service={service} /> */}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SubServiceCard;
|
|
@ -0,0 +1,8 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Text as ChakraText } from "@chakra-ui/react";
|
||||||
|
|
||||||
|
const Text = (props) => {
|
||||||
|
return <ChakraText {...props} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Text;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './Text';
|
||||||
|
export { default } from './Text';
|
|
@ -0,0 +1,398 @@
|
||||||
|
import axios from "axios";
|
||||||
|
import { hits_data, services, incidents, ping_data } from "../utils/data";
|
||||||
|
|
||||||
|
const qs = require("querystring");
|
||||||
|
|
||||||
|
// const LOCAL_API = "http://localhost:8080";
|
||||||
|
const STAGE_API = "https://statping.concierge.stage.razorpay.in";
|
||||||
|
const TOKEN_KEY = "statping_auth";
|
||||||
|
|
||||||
|
axios.defaults.baseURL =
|
||||||
|
process.env.NODE_ENV === "development" ? `${STAGE_API}/api` : "/api";
|
||||||
|
class Api {
|
||||||
|
constructor() {
|
||||||
|
this.version = "0.90.74";
|
||||||
|
this.commit = "df8e1f73d9f7fdf218bc5c26130d7d8a6af6719a";
|
||||||
|
}
|
||||||
|
|
||||||
|
async oauth() {
|
||||||
|
const oauth = axios.get("api/oauth").then((response) => response.data);
|
||||||
|
return oauth;
|
||||||
|
}
|
||||||
|
|
||||||
|
async core() {
|
||||||
|
const core = axios.get("api").then((response) => response.data);
|
||||||
|
// if (core.allow_reports) {
|
||||||
|
// await this.sentry_init();
|
||||||
|
// }
|
||||||
|
return core;
|
||||||
|
}
|
||||||
|
|
||||||
|
async core_save(obj) {
|
||||||
|
return axios.post("api/core", obj).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async oauth_save(obj) {
|
||||||
|
return axios.post("api/oauth", obj).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup_save(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/setup", qs.stringify(data))
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async services() {
|
||||||
|
// return axios.get("api/services").then((response) => response.data);
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
async service(id) {
|
||||||
|
return axios.get("api/services/" + id).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_create(data) {
|
||||||
|
return axios.post("api/services", data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_update(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/services/" + data.id, data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_hits(id, start, end, group, fill = true) {
|
||||||
|
return hits_data;
|
||||||
|
// return axios
|
||||||
|
// .get(
|
||||||
|
// "api/services/" +
|
||||||
|
// id +
|
||||||
|
// "/hits_data?start=" +
|
||||||
|
// start +
|
||||||
|
// "&end=" +
|
||||||
|
// end +
|
||||||
|
// "&group=" +
|
||||||
|
// group +
|
||||||
|
// "&fill=" +
|
||||||
|
// fill
|
||||||
|
// )
|
||||||
|
// .then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_ping(id, start, end, group, fill = true) {
|
||||||
|
return ping_data;
|
||||||
|
// return axios
|
||||||
|
// .get(
|
||||||
|
// "api/services/" +
|
||||||
|
// id +
|
||||||
|
// "/ping_data?start=" +
|
||||||
|
// start +
|
||||||
|
// "&end=" +
|
||||||
|
// end +
|
||||||
|
// "&group=" +
|
||||||
|
// group +
|
||||||
|
// "&fill=" +
|
||||||
|
// fill
|
||||||
|
// )
|
||||||
|
// .then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_failures_data(url, start, end, group, fill = true) {
|
||||||
|
return axios
|
||||||
|
.get(`${url}?start=${start}&end=${end}&group=${group}&fill=${fill}`)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_uptime(id, start, end) {
|
||||||
|
return axios
|
||||||
|
.get("api/services/" + id + "/uptime_data?start=" + start + "&end=" + end)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_heatmap(id, start, end, group) {
|
||||||
|
return axios
|
||||||
|
.get("api/services/" + id + "/heatmap")
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_failures(id, start, end, limit = 999, offset = 0) {
|
||||||
|
return axios
|
||||||
|
.get(
|
||||||
|
"api/services/" +
|
||||||
|
id +
|
||||||
|
"/failures?start=" +
|
||||||
|
start +
|
||||||
|
"&end=" +
|
||||||
|
end +
|
||||||
|
"&limit=" +
|
||||||
|
limit +
|
||||||
|
"&offset=" +
|
||||||
|
offset
|
||||||
|
)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_failures_delete(service) {
|
||||||
|
return axios
|
||||||
|
.delete("api/services/" + service.id + "/failures")
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_delete(id) {
|
||||||
|
return axios.delete("api/services/" + id).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async services_reorder(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/reorder/services", data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkins() {
|
||||||
|
return axios.get("api/checkins").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async groups() {
|
||||||
|
return axios.get("api/groups").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async groups_reorder(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/reorder/groups", data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async group_delete(id) {
|
||||||
|
return axios.delete("api/groups/" + id).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async group_create(data) {
|
||||||
|
return axios.post("api/groups", data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async group_update(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/groups/" + data.id, data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async users() {
|
||||||
|
return axios.get("api/users").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async user_create(data) {
|
||||||
|
return axios.post("api/users", data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async user_update(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/users/" + data.id, data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async user_delete(id) {
|
||||||
|
return axios.delete("api/users/" + id).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incident_updates(incident) {
|
||||||
|
return axios
|
||||||
|
.get("api/incidents/" + incident.id + "/updates")
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incident_update_create(update) {
|
||||||
|
return axios
|
||||||
|
.post("api/incidents/" + update.incident + "/updates", update)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incident_update_delete(update) {
|
||||||
|
return axios
|
||||||
|
.delete("api/incidents/" + update.incident + "/updates/" + update.id)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incidents_service(id) {
|
||||||
|
return incidents[id];
|
||||||
|
// return axios
|
||||||
|
// .get("api/services/" + id + "/incidents")
|
||||||
|
// .then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incident_create(service_id, data) {
|
||||||
|
return axios
|
||||||
|
.post("api/services/" + service_id + "/incidents", data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incident_delete(incident) {
|
||||||
|
return axios
|
||||||
|
.delete("api/incidents/" + incident.id)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkin(api) {
|
||||||
|
return axios.get("api/checkins/" + api).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkin_create(data) {
|
||||||
|
return axios.post("api/checkins", data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkin_delete(checkin) {
|
||||||
|
return axios
|
||||||
|
.delete("api/checkins/" + checkin.api_key)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async messages() {
|
||||||
|
return axios.get("api/messages").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async message_create(data) {
|
||||||
|
return axios.post("api/messages", data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async message_update(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/messages/" + data.id, data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async message_delete(id) {
|
||||||
|
return axios.delete("api/messages/" + id).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async group(id) {
|
||||||
|
return axios.get("api/groups/" + id).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async notifiers() {
|
||||||
|
return axios.get("api/notifiers").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async notifier_save(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/notifier/" + data.method, data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async notifier_test(data, notifier) {
|
||||||
|
return axios
|
||||||
|
.post("api/notifier/" + notifier + "/test", data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async renewApiKeys() {
|
||||||
|
return axios.get("api/renew").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async logs() {
|
||||||
|
return axios.get("api/logs").then((response) => response.data) || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async logs_last() {
|
||||||
|
return axios.get("api/logs/last").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async theme() {
|
||||||
|
return axios.get("api/theme").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async theme_generate(create = true) {
|
||||||
|
if (create) {
|
||||||
|
return axios.get("api/theme/create").then((response) => response.data);
|
||||||
|
} else {
|
||||||
|
return axios.delete("api/theme").then((response) => response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async theme_save(data) {
|
||||||
|
return axios.post("api/theme", data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async import(data) {
|
||||||
|
return axios
|
||||||
|
.post("api/settings/import", data)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async check_token(token) {
|
||||||
|
const f = { token: token };
|
||||||
|
return axios
|
||||||
|
.post("api/users/token", qs.stringify(f))
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(username, password) {
|
||||||
|
const f = { username: username, password: password };
|
||||||
|
return axios
|
||||||
|
.post("api/login", qs.stringify(f))
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async logout() {
|
||||||
|
return axios.get("api/logout").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async scss_base() {
|
||||||
|
return await axios({
|
||||||
|
url: "/scss/base.scss",
|
||||||
|
method: "GET",
|
||||||
|
responseType: "blob",
|
||||||
|
}).then((response) => {
|
||||||
|
const reader = new window.FileReader();
|
||||||
|
return reader.readAsText(response.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async configs() {
|
||||||
|
return (
|
||||||
|
axios.get("api/settings/configs").then((response) => response.data) || []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async configs_save(data) {
|
||||||
|
return (
|
||||||
|
axios
|
||||||
|
.post("api/settings/configs", data)
|
||||||
|
.then((response) => response.data) || []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
token() {
|
||||||
|
return localStorage.get(TOKEN_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken() {
|
||||||
|
const tk = localStorage.get(TOKEN_KEY);
|
||||||
|
if (tk) {
|
||||||
|
return { Authorization: "Bearer " + tk };
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async github_release() {
|
||||||
|
return fetch(
|
||||||
|
"https://api.github.com/repos/statping/statping/releases/latest"
|
||||||
|
).then((response) => response.json());
|
||||||
|
}
|
||||||
|
|
||||||
|
async allActions(...all) {
|
||||||
|
await axios.all([all]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchServices() {
|
||||||
|
return axios.get("/services").then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchSubServices(service_id) {
|
||||||
|
return await axios
|
||||||
|
.get(`/services/${service_id}/sub_services`)
|
||||||
|
.then((response) => response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const API = new Api();
|
||||||
|
export default API;
|
|
@ -0,0 +1,147 @@
|
||||||
|
const english = {
|
||||||
|
settings: "Settings",
|
||||||
|
dashboard: "Dashboard",
|
||||||
|
services: "Services",
|
||||||
|
service: "Service",
|
||||||
|
failures: "Failures",
|
||||||
|
users: "Users",
|
||||||
|
login: "Login",
|
||||||
|
logout: "Logout",
|
||||||
|
online: "Online",
|
||||||
|
offline: "Offline",
|
||||||
|
configs: "Configuration",
|
||||||
|
username: "Username",
|
||||||
|
password: "Password",
|
||||||
|
email: "Email",
|
||||||
|
confirm_password: "Confirm Password",
|
||||||
|
uptime: "Uptime",
|
||||||
|
name: "Name",
|
||||||
|
copy: "Copy",
|
||||||
|
close: "Close",
|
||||||
|
secret: "Secret",
|
||||||
|
second: "Seconds",
|
||||||
|
regen_api: "Regenerate API Keys",
|
||||||
|
regen_desc:
|
||||||
|
"API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.",
|
||||||
|
visibility: "Visibility",
|
||||||
|
group: "Group",
|
||||||
|
group_create: "Create Group",
|
||||||
|
group_update: "Update Group",
|
||||||
|
group_public_desc: "Show group services to the public",
|
||||||
|
groups: "Groups",
|
||||||
|
no_group: "No Group",
|
||||||
|
public: "Public",
|
||||||
|
private: "Private",
|
||||||
|
announcements: "Announcements",
|
||||||
|
notifiers: "Notifiers",
|
||||||
|
logs: "Logs",
|
||||||
|
help: "Help",
|
||||||
|
type: "Type",
|
||||||
|
edit: "Edit",
|
||||||
|
update: "Update",
|
||||||
|
create: "Create",
|
||||||
|
view: "View",
|
||||||
|
save: "Save",
|
||||||
|
title: "Title",
|
||||||
|
status: "Status",
|
||||||
|
begins: "Begins",
|
||||||
|
total_services: "Total Services",
|
||||||
|
online_services: "Online Services",
|
||||||
|
request_timeout: "Request Timeout",
|
||||||
|
service_never_online: "Service has never been online",
|
||||||
|
service_online_check: "Online checked",
|
||||||
|
service_offline_time: "Service has been offline for",
|
||||||
|
days_ago: "Days ago",
|
||||||
|
today: "Today",
|
||||||
|
week: "Week",
|
||||||
|
month: "Month",
|
||||||
|
day: "Day",
|
||||||
|
hour: "Hour",
|
||||||
|
minute: "Minute",
|
||||||
|
failures_24_hours: "Failures last 24 hours",
|
||||||
|
no_services: "You currently don't have any services!",
|
||||||
|
theme: "Theme",
|
||||||
|
cache: "Cache",
|
||||||
|
authentication: "Authentication",
|
||||||
|
import: "Import",
|
||||||
|
main_settings: "Main Settings",
|
||||||
|
variables: "Variables",
|
||||||
|
docs: "Documentation",
|
||||||
|
links: "Links",
|
||||||
|
changelog: "Change Log",
|
||||||
|
repo: "Repository",
|
||||||
|
language: "Language",
|
||||||
|
db_connection: "Database Connection",
|
||||||
|
db_host: "Database Host",
|
||||||
|
db_port: "Database Port",
|
||||||
|
db_username: "Database Username",
|
||||||
|
db_password: "Database Password",
|
||||||
|
db_database: "Database Name",
|
||||||
|
send_reports: "Send Error Reports",
|
||||||
|
send_reports_desc: "Send errors to Statping for debugging",
|
||||||
|
project_name: "Status Page Name",
|
||||||
|
description: "Description",
|
||||||
|
domain: "Domain",
|
||||||
|
enable_cdn: "Enable CDN",
|
||||||
|
newsletter: "Newsletter",
|
||||||
|
newsletter_note: "We will only send you an email for major changes",
|
||||||
|
loading: "Loading",
|
||||||
|
save_settings: "Save Settings",
|
||||||
|
average_response: "Average Response",
|
||||||
|
last_uptime: "Uptime last",
|
||||||
|
sign_in: "Sign In",
|
||||||
|
last_login: "Last Login",
|
||||||
|
admin: "Admin",
|
||||||
|
user: "User",
|
||||||
|
failed: "Failed",
|
||||||
|
wrong_login: "Incorrect username or password",
|
||||||
|
theme_editor: "Theme Editor",
|
||||||
|
enable_assets: "Enable Local Assets",
|
||||||
|
assets_desc:
|
||||||
|
"Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.",
|
||||||
|
assets_btn: "Enable Local Assets",
|
||||||
|
assets_loading: "Creating Assets",
|
||||||
|
assets_dir: "Assets Directory",
|
||||||
|
footer: "Footer",
|
||||||
|
footer_notes: "You can use HTML tags in footer",
|
||||||
|
global_announcement: "Global Announcement",
|
||||||
|
announcement_date: "Announcement Date Range",
|
||||||
|
notify_users: "Notify Users",
|
||||||
|
notify_desc: "Notify Users Before Scheduled Time",
|
||||||
|
notify_method: "Notification Method",
|
||||||
|
notify_before: "Notify Before",
|
||||||
|
message_create: "Create Announcement",
|
||||||
|
message_edit: "Edit Announcement",
|
||||||
|
minutes: "Minutes",
|
||||||
|
hours: "Hours",
|
||||||
|
days: "Days",
|
||||||
|
user_create: "Create User",
|
||||||
|
user_update: "Update User",
|
||||||
|
administrator: "Administrator",
|
||||||
|
checkins: "Checkins",
|
||||||
|
incidents: "Incidents",
|
||||||
|
service_info: "Service Info",
|
||||||
|
service_name: "Service Name",
|
||||||
|
service_type: "Service Type",
|
||||||
|
permalink: "Permalink URL",
|
||||||
|
service_public: "Public Service",
|
||||||
|
check_interval: "Check Interval",
|
||||||
|
service_endpoint: "Service Endpoint",
|
||||||
|
service_check: "Service Check Type",
|
||||||
|
service_timeout: "Request Timeout",
|
||||||
|
expected_resp: "Expected Response",
|
||||||
|
expected_code: "Expected Status Code",
|
||||||
|
follow_redir: "Follow Redirects",
|
||||||
|
verify_ssl: "Verify SSL",
|
||||||
|
tls_cert: "Use TLS Cert",
|
||||||
|
notification_opts: "Notification Options",
|
||||||
|
notifications_enable: "Enable Notifications",
|
||||||
|
notify_after: "Notify After Failures",
|
||||||
|
notify_all: "Notify All Changes",
|
||||||
|
service_update: "Update Service",
|
||||||
|
service_create: "Create Service",
|
||||||
|
};
|
||||||
|
|
||||||
|
const langs = (v) => english[v];
|
||||||
|
|
||||||
|
export default langs;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useRef, useState, useEffect } from "react";
|
||||||
|
|
||||||
|
const useIntersectionObserver = (options) => {
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
|
||||||
|
const callbackFunction = (entries) => {
|
||||||
|
const [entry] = entries;
|
||||||
|
setIsVisible(entry.isIntersecting);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(callbackFunction, options);
|
||||||
|
if (containerRef.current) observer.observe(containerRef.current);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (containerRef.current) observer.unobserve(containerRef.current);
|
||||||
|
};
|
||||||
|
}, [containerRef, options]);
|
||||||
|
|
||||||
|
return [containerRef, isVisible];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useIntersectionObserver;
|
|
@ -0,0 +1,12 @@
|
||||||
|
import React from "react";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
import App from "./components/App";
|
||||||
|
import "./styles/css/bootstrap.min.css";
|
||||||
|
import "./styles/scss/index.scss";
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>,
|
||||||
|
document.getElementById("root")
|
||||||
|
);
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1665px" height="665px" viewBox="0 0 1665 665" preserveAspectRatio="none"><defs><linearGradient x1="-9.76013358%" y1="100%" x2="93.4135454%" y2="47.0457337%" id="linearGradient-1"><stop stop-color="#023EA4" offset="0%"></stop><stop stop-color="#2377C9" offset="100%"></stop></linearGradient></defs><polygon fill="#F4F8FF" transform="translate(832.500000, 274.000000) scale(1, -1) translate(-832.500000, -274.000000) " points="0 3 1665 3 1665 346.491263 117.611298 545 0 475.825616"></polygon><polygon fill="url(#linearGradient-1)" points="0 124.293036 1665 0 1665 561.957836 1550.97584 665 0 601.957836"></polygon></svg>
|
After Width: | Height: | Size: 666 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.00001 5.5C6.27615 5.5 6.50001 5.72386 6.50001 6V8C6.50001 8.27614 6.27615 8.5 6.00001 8.5C5.72387 8.5 5.50001 8.27614 5.50001 8V6C5.50001 5.72386 5.72387 5.5 6.00001 5.5Z" fill="#162F56" fill-opacity="0.54"/>
|
||||||
|
<path d="M6.00001 3.5C5.72387 3.5 5.50001 3.72386 5.50001 4C5.50001 4.27614 5.72387 4.5 6.00001 4.5H6.00501C6.28115 4.5 6.50501 4.27614 6.50501 4C6.50501 3.72386 6.28115 3.5 6.00501 3.5H6.00001Z" fill="#162F56" fill-opacity="0.54"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 6C0.5 2.96243 2.96243 0.5 6 0.5C9.03757 0.5 11.5 2.96243 11.5 6C11.5 9.03757 9.03757 11.5 6 11.5C2.96243 11.5 0.5 9.03757 0.5 6ZM6 1.5C3.51472 1.5 1.5 3.51472 1.5 6C1.5 8.48528 3.51472 10.5 6 10.5C8.48528 10.5 10.5 8.48528 10.5 6C10.5 3.51472 8.48528 1.5 6 1.5Z" fill="#162F56" fill-opacity="0.54"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 911 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.49243 22.1483V14.4493C8.49243 12.2546 11.8037 10.5999 16.1942 10.5999C20.5848 10.5999 23.896 12.2546 23.896 14.4493V22.1483C23.896 24.3431 20.5848 25.9978 16.1942 25.9978C11.8037 25.9978 8.49243 24.3431 8.49243 22.1483ZM22.6124 14.5663C22.6124 13.3546 19.8678 12 16.1943 12C12.5208 12 9.77612 13.3546 9.77612 14.5663C9.77612 15.7781 12.5208 17.1327 16.1943 17.1327C19.8678 17.1327 22.6124 15.7781 22.6124 14.5663ZM16.1943 20.7674C12.5208 20.7674 9.77612 19.4128 9.77612 18.2011V16.6348C11.6953 17.8316 13.9351 18.4123 16.1943 18.2988C18.4535 18.4123 20.6932 17.8316 22.6124 16.6348V18.2011C22.6124 19.4128 19.8678 20.7674 16.1943 20.7674ZM16.1943 24.7146C12.5208 24.7146 9.77612 23.36 9.77612 22.1483V20.4843C11.6953 21.6811 13.9351 22.2618 16.1943 22.1483C18.4535 22.2618 20.6932 21.6811 22.6124 20.4843V22.1483C22.6124 23.36 19.8678 24.7146 16.1943 24.7146Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0241 7.87249C13.2701 8.11851 13.669 8.11851 13.915 7.87249L16.2436 5.54387L18.5723 7.87249C18.8183 8.11851 19.2171 8.11851 19.4632 7.87249C19.7092 7.62648 19.7092 7.2276 19.4632 6.98159L16.6891 4.20751C16.4431 3.9615 16.0442 3.9615 15.7982 4.20751L13.0241 6.98159C12.7781 7.2276 12.7781 7.62648 13.0241 7.87249Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.194 13.9952C16.5694 13.9952 16.8736 13.7593 16.8736 13.4683V5.34752C16.8736 5.05652 16.5694 4.82061 16.194 4.82061C15.8187 4.82061 15.5144 5.05652 15.5144 5.34752V13.4683C15.5144 13.7593 15.8187 13.9952 16.194 13.9952Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.1616 0 0 7.1616 0 16s7.1616 16 16 16 16-7.1616 16-16S24.8384 0 16 0z" fill="url(#paint0_linear)"/><path d="M19 8c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zM7 16c0-2.39 1.4-4.46 3.43-5.42.34-.16.57-.47.57-.84v-.19c0-.68-.71-1.11-1.32-.82C6.92 9.99 5 12.77 5 16c0 3.23 1.92 6.01 4.68 7.27.61.28 1.32-.14 1.32-.82v-.18c0-.37-.23-.69-.57-.85C8.4 20.46 7 18.39 7 16z" fill="#fff"/></svg>
|
After Width: | Height: | Size: 583 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.8279 15.7846C16.9757 15.8902 16.9757 16.1098 16.8279 16.2154L9.8885 21.1727C9.71329 21.2979 9.46991 21.1727 9.46991 20.9573L9.46991 11.0427C9.46991 10.8274 9.71329 10.7021 9.8885 10.8273L16.8279 15.7846Z" stroke="white" stroke-width="1.5883"/>
|
||||||
|
<path d="M25.3591 15.7846C25.5069 15.8902 25.5069 16.1098 25.3591 16.2154L18.4197 21.1727C18.2445 21.2979 18.0012 21.1727 18.0012 20.9573L18.0012 11.0427C18.0012 10.8274 18.2445 10.7021 18.4198 10.8273L25.3591 15.7846Z" stroke="white" stroke-width="1.5883"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 619 B |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="7.19385" y="12.6141" width="18.5301" height="2.4553" fill="white"/>
|
||||||
|
<rect x="7.45581" y="10.4058" width="17.5177" height="11.8124" rx="1.04899" stroke="white" stroke-width="1.5"/>
|
||||||
|
<ellipse cx="19.8233" cy="18.4709" rx="1.30446" ry="1.31895" fill="white"/>
|
||||||
|
<ellipse cx="21.5628" cy="18.4709" rx="1.30446" ry="1.31895" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 444 B |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.0876 16.7956C11.0876 16.6143 11.1597 16.4404 11.2879 16.3122C11.4161 16.184 11.59 16.1119 11.7713 16.1119C11.9526 16.1119 12.1265 16.184 12.2547 16.3122C12.3829 16.4404 12.4549 16.6143 12.4549 16.7956V20.8973C12.4549 21.0786 12.3829 21.2525 12.2547 21.3807C12.1265 21.5089 11.9526 21.581 11.7713 21.581C11.59 21.581 11.4161 21.5089 11.2879 21.3807C11.1597 21.2525 11.0876 21.0786 11.0876 20.8973V16.7956Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<path d="M15.1897 16.7956C15.1897 16.6143 15.2617 16.4404 15.3899 16.3122C15.5181 16.184 15.692 16.1119 15.8733 16.1119C16.0546 16.1119 16.2285 16.184 16.3567 16.3122C16.4849 16.4404 16.557 16.6143 16.557 16.7956V20.8973C16.557 21.0786 16.4849 21.2525 16.3567 21.3807C16.2285 21.5089 16.0546 21.581 15.8733 21.581C15.692 21.581 15.5181 21.5089 15.3899 21.3807C15.2617 21.2525 15.1897 21.0786 15.1897 20.8973V16.7956Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<path d="M19.2913 16.7956C19.2913 16.6143 19.3633 16.4404 19.4915 16.3122C19.6197 16.184 19.7936 16.1119 19.9749 16.1119C20.1562 16.1119 20.3301 16.184 20.4583 16.3122C20.5865 16.4404 20.6585 16.6143 20.6585 16.7956V20.8973C20.6585 21.0786 20.5865 21.2525 20.4583 21.3807C20.3301 21.5089 20.1562 21.581 19.9749 21.581C19.7936 21.581 19.6197 21.5089 19.4915 21.3807C19.3633 21.2525 19.2913 21.0786 19.2913 20.8973V16.7956Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<path d="M9.03685 23.9982C8.92912 23.9982 8.82244 23.977 8.72291 23.9358C8.62338 23.8945 8.53295 23.8341 8.45677 23.7579C8.38059 23.6818 8.32017 23.5913 8.27894 23.4918C8.23771 23.3923 8.21649 23.2856 8.21649 23.1779C8.21649 23.0701 8.23771 22.9635 8.27894 22.8639C8.32017 22.7644 8.38059 22.674 8.45677 22.5978C8.53295 22.5216 8.62338 22.4612 8.72291 22.42C8.82244 22.3787 8.92912 22.3575 9.03685 22.3575H23.393C23.5007 22.3575 23.6074 22.3787 23.7069 22.42C23.8065 22.4612 23.8969 22.5216 23.9731 22.5978C24.0493 22.674 24.1097 22.7644 24.1509 22.8639C24.1921 22.9635 24.2134 23.0701 24.2134 23.1779C24.2134 23.2856 24.1921 23.3923 24.1509 23.4918C24.1097 23.5913 24.0493 23.6818 23.9731 23.7579C23.8969 23.8341 23.8065 23.8945 23.7069 23.9358C23.6074 23.977 23.5007 23.9982 23.393 23.9982H9.03685ZM9.03685 15.1111C8.25136 15.1111 7.91501 14.1143 8.53985 13.6385L15.7179 8.16952C15.8608 8.06074 16.0354 8.00183 16.2149 8.00183C16.3945 8.00183 16.5691 8.06074 16.7119 8.16952L23.89 13.6385C24.5148 14.1143 24.1778 15.1111 23.393 15.1111H9.03685ZM20.9627 13.4704L16.2149 9.85329L11.4665 13.4704H20.962H20.9627Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.0209 10.238C22.0199 10.2383 22.018 10.2387 22.0153 10.2397L8.49322 15.1227C8.48959 15.124 8.48735 15.1251 8.48619 15.1257C8.4856 15.1271 8.48381 15.1308 8.4837 15.1359C8.48359 15.141 8.48454 15.1443 8.48507 15.1457C8.4862 15.1464 8.48906 15.1481 8.49263 15.1495L14.4962 17.6216C14.7537 17.7276 14.96 17.9295 15.0716 18.1846L17.5085 23.7545C17.51 23.7581 17.5113 23.7602 17.512 23.7614C17.5135 23.7619 17.5172 23.7634 17.5223 23.7631C17.5274 23.7629 17.5306 23.7617 17.532 23.7611C17.5327 23.7599 17.5341 23.757 17.5353 23.7533L22.0338 10.2578C22.0347 10.2551 22.0352 10.2532 22.0353 10.2521C22.0355 10.2513 22.0355 10.2507 22.0356 10.2505C22.0353 10.2497 22.034 10.2466 22.0302 10.2429C22.0264 10.2392 22.0233 10.238 22.0225 10.2378C22.0224 10.2378 22.0227 10.2378 22.0225 10.2378C22.0222 10.2378 22.0214 10.2379 22.0209 10.238ZM21.6551 9.24238C22.4997 8.93739 23.3237 9.74124 23.0398 10.5931L18.5413 24.0886C18.23 25.0224 16.9315 25.0813 16.537 24.1795L14.1002 18.6096C14.0987 18.6062 14.0959 18.6035 14.0925 18.6021L8.0889 16.13C7.1802 15.7559 7.20877 14.4591 8.13308 14.1253L21.6551 9.24238Z" fill="white"/>
|
||||||
|
<path d="M22.0225 10.2378C22.0233 10.238 22.0264 10.2392 22.0302 10.2429C22.034 10.2466 22.0353 10.2497 22.0356 10.2505C22.0355 10.2507 22.0355 10.2513 22.0353 10.2521C22.0352 10.2532 22.0347 10.2551 22.0338 10.2578L17.5353 23.7533C17.5341 23.757 17.5327 23.7599 17.532 23.7611C17.5306 23.7617 17.5274 23.7629 17.5223 23.7631C17.5172 23.7634 17.5135 23.7619 17.512 23.7614C17.5113 23.7602 17.51 23.7581 17.5085 23.7545L15.0716 18.1846C14.96 17.9295 14.7537 17.7276 14.4962 17.6216L8.49263 15.1495C8.48906 15.1481 8.4862 15.1464 8.48507 15.1457C8.48454 15.1443 8.48359 15.141 8.4837 15.1359C8.48381 15.1308 8.4856 15.1271 8.48619 15.1257C8.48735 15.1251 8.48959 15.124 8.49322 15.1227L22.0153 10.2397C22.018 10.2387 22.0199 10.2383 22.0209 10.238C22.0214 10.2379 22.0222 10.2378 22.0225 10.2378ZM22.0225 10.2378C22.0227 10.2378 22.0224 10.2378 22.0225 10.2378ZM23.0398 10.5931C23.3237 9.74124 22.4997 8.93739 21.6551 9.24238L8.13308 14.1253C7.20877 14.4591 7.1802 15.7559 8.0889 16.13L14.0925 18.6021C14.0959 18.6035 14.0987 18.6062 14.1002 18.6096L16.537 24.1795C16.9315 25.0813 18.23 25.0224 18.5413 24.0886L23.0398 10.5931Z" stroke="white" stroke-width="0.5"/>
|
||||||
|
<rect x="16.9666" y="18.9755" width="4.35981" height="1.06035" rx="0.530175" transform="rotate(-69.9739 16.9666 18.9755)" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.387 11.4708C14.387 12.021 14.8329 12.4669 15.3831 12.4669L20.5902 12.4669L20.5902 17.6741C20.5902 18.2242 21.0362 18.6702 21.5863 18.6702C22.1365 18.6702 22.5824 18.2242 22.5824 17.6741L22.5824 11.4708C22.5824 10.9207 22.1365 10.4747 21.5863 10.4747L15.3831 10.4747C14.8329 10.4747 14.387 10.9207 14.387 11.4708Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.0855 21.8609C11.5052 22.2806 12.1092 22.357 12.4345 22.0316L21.5142 12.9519C21.8396 12.6266 21.7632 12.0226 21.3435 11.603C20.9238 11.1833 20.3199 11.1069 19.9945 11.4322L10.9148 20.5119C10.5894 20.8373 10.6659 21.4412 11.0855 21.8609Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 790 B |
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0)">
|
||||||
|
<path d="M23.028 13.2709L22.9684 13.3782L23.028 13.2709L21.1192 12.2105V10.4328C21.1192 9.97578 20.9377 9.53748 20.6145 9.21432C20.2914 8.89116 19.8531 8.70961 19.396 8.70961H13.0335C12.5765 8.70961 12.1382 8.89116 11.815 9.21432C11.4918 9.53748 11.3103 9.97578 11.3103 10.4328V12.2105L9.40158 13.2709L9.40157 13.2709C9.25693 13.3513 9.13641 13.4689 9.0525 13.6115C8.96859 13.7541 8.92434 13.9166 8.92433 14.082V20.772C8.92433 21.4399 9.18967 22.0805 9.66199 22.5528C10.1343 23.0251 10.7749 23.2905 11.4428 23.2905H20.9867C21.6546 23.2905 22.2952 23.0251 22.7675 22.5528C23.2398 22.0805 23.5052 21.4399 23.5052 20.772V14.082C23.5052 13.9166 23.4609 13.7541 23.377 13.6115C23.2931 13.4689 23.1726 13.3513 23.028 13.2709ZM9.98476 14.16L11.3103 13.4236V15.1427L11.2968 15.1354L11.2967 15.1353C11.1728 15.0688 11.0276 15.0541 10.893 15.0945C10.7583 15.1349 10.6452 15.2272 10.5785 15.3509C10.5118 15.4747 10.497 15.6199 10.5373 15.7546C10.5776 15.8893 10.6698 16.0025 10.7935 16.0693L10.7936 16.0694L15.9632 18.853L15.9634 18.8531C16.0407 18.8945 16.1271 18.9162 16.2148 18.9162C16.3025 18.9162 16.3888 18.8945 16.4661 18.8531L16.4663 18.853L21.6359 16.0694L21.636 16.0693C21.7598 16.0025 21.8519 15.8893 21.8922 15.7546C21.9325 15.6199 21.9177 15.4747 21.851 15.3509C21.7843 15.2272 21.6712 15.1349 21.5365 15.0945C21.4019 15.0541 21.2567 15.0688 21.1328 15.1353L21.1328 15.1354L21.1192 15.1427V13.4236L22.4448 14.16V20.7718C22.4443 21.1584 22.2905 21.5291 22.0172 21.8024C21.7438 22.0758 21.3731 22.2296 20.9865 22.23H11.443C11.0564 22.2296 10.6857 22.0758 10.4124 21.8024C10.139 21.5291 9.9852 21.1584 9.98476 20.7718L9.98476 14.16ZM13.0336 9.77004H19.3959C19.3959 9.77004 19.3959 9.77004 19.3959 9.77004C19.5717 9.77025 19.7401 9.84016 19.8644 9.96442C19.9887 10.0887 20.0586 10.2572 20.0588 10.433V15.7137L16.2148 17.7838L12.3707 15.7137V10.433C12.3709 10.2572 12.4408 10.0887 12.5651 9.96442C12.6894 9.84015 12.8579 9.77024 13.0336 9.77004Z" fill="white" stroke="white" stroke-width="0.265106"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0">
|
||||||
|
<rect width="32" height="32" fill="white" transform="translate(0.214844)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.2105 10.0989C14.8063 10.0989 13.7305 11.353 13.9457 12.7443L14.9945 19.5755L13.8587 19.7499L12.81 12.9199C12.8099 12.9198 12.81 12.92 12.81 12.9199C12.4875 10.8332 14.1009 8.94971 16.2105 8.94971C18.3201 8.94971 19.9335 10.8328 19.611 12.9195C19.611 12.9196 19.6111 12.9194 19.611 12.9195L18.5623 19.7499L17.4264 19.5755L18.4752 12.7449C18.6903 11.3536 17.6147 10.0989 16.2105 10.0989Z" fill="white"/>
|
||||||
|
<path d="M12.81 12.9199L13.8587 19.7499L14.9945 19.5755L13.9457 12.7443C13.7305 11.353 14.8063 10.0989 16.2105 10.0989C17.6147 10.0989 18.6903 11.3536 18.4752 12.7449L17.4264 19.5755L18.5623 19.7499L19.611 12.9195M12.81 12.9199C12.81 12.92 12.8099 12.9198 12.81 12.9199ZM12.81 12.9199C12.4875 10.8332 14.1009 8.94971 16.2105 8.94971C18.3201 8.94971 19.9335 10.8328 19.611 12.9195M19.611 12.9195C19.6111 12.9194 19.611 12.9196 19.611 12.9195Z" stroke="white" stroke-width="0.25" stroke-miterlimit="10"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4673 19.8672C11.4673 19.5498 11.7245 19.2926 12.0419 19.2926C12.3592 19.2926 12.6165 19.5498 12.6165 19.8672V20.5252C12.6165 21.2059 13.1683 21.7578 13.8491 21.7578H18.5803C19.261 21.7578 19.8129 21.2059 19.8129 20.5252V19.8672C19.8129 19.5498 20.0701 19.2926 20.3875 19.2926C20.7048 19.2926 20.9621 19.5498 20.9621 19.8672V21.0998C20.9621 22.0979 20.153 22.907 19.1549 22.907H13.2745C12.2764 22.907 11.4673 22.0979 11.4673 21.0998V19.8672Z" fill="white" stroke="white" stroke-width="0.25" stroke-miterlimit="10"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.2651 19.2218C23.2651 19.5392 23.0079 19.7964 22.6905 19.7964H9.73915C9.42181 19.7964 9.16455 19.5392 9.16455 19.2218C9.16455 18.9045 9.4218 18.6472 9.73915 18.6472H22.6905C23.0079 18.6472 23.2651 18.9045 23.2651 19.2218Z" fill="white" stroke="white" stroke-width="0.25" stroke-miterlimit="10"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4268 10.0183C10.4268 9.26616 11.0365 8.65643 11.7886 8.65643H20.6409C21.3931 8.65643 22.0028 9.26616 22.0028 10.0183V23.3436L18.744 21.3211L16.2148 22.8908L13.6856 21.3211L10.4268 23.3436V10.0183ZM11.8859 10.1156V20.7207L13.6856 19.6038L16.2148 21.1735L18.744 19.6038L20.5437 20.7207V10.1156H11.8859Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2869 12.835C13.2869 12.4321 13.6135 12.1055 14.0164 12.1055H17.9885C18.3914 12.1055 18.7181 12.4321 18.7181 12.835C18.7181 13.238 18.3914 13.5646 17.9885 13.5646H14.0164C13.6135 13.5646 13.2869 13.238 13.2869 12.835Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.2869 15.8637C13.2869 15.4608 13.6135 15.1342 14.0164 15.1342H17.9885C18.3914 15.1342 18.7181 15.4608 18.7181 15.8637C18.7181 16.2666 18.3914 16.5933 17.9885 16.5933H14.0164C13.6135 16.5933 13.2869 16.2666 13.2869 15.8637Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.7852 0C7.94676 0 0.785156 7.1616 0.785156 16C0.785156 24.8384 7.94676 32 16.7852 32C25.6236 32 32.7852 24.8384 32.7852 16C32.7852 7.1616 25.6236 0 16.7852 0Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.5974 10.5081L15.3241 14.0161L13.595 16.0268L9.28516 10.5081H12.5974Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.66 7L9.28516 21.5334H12.2998L23.4635 7H20.66Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5186 17.3207L20.4491 25.0414H23.9724L15.5502 14.2667L14.5186 17.3207Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 736 B |
|
@ -0,0 +1,13 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.1616 0 0 7.1616 0 16C0 24.8384 7.1616 32 16 32C24.8384 32 32 24.8384 32 16C32 7.1616 24.8384 0 16 0Z" fill="#DBEBFF"/>
|
||||||
|
<g clip-path="url(#clip0)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.65 11.6064L14.8314 14.4129L13.4481 16.0214L10.0002 11.6064H12.65Z" fill="#3987F0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.0998 8.79999L10 20.4267H12.4117L21.3426 8.79999H19.0998Z" fill="#3987F0"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.1865 17.0566L18.931 23.2331H21.7496L15.0118 14.6133L14.1865 17.0566Z" fill="#3987F0"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0">
|
||||||
|
<rect width="16" height="16" fill="white" transform="translate(8 8)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 836 B |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.1616 0 0 7.1616 0 16C0 24.8384 7.1616 32 16 32C24.8384 32 32 24.8384 32 16C32 7.1616 24.8384 0 16 0Z" fill="#DBEBFF"/>
|
||||||
|
<path d="M21.3333 10.6667H10.6666C9.92659 10.6667 9.33992 11.26 9.33992 12L9.33325 20C9.33325 20.74 9.92659 21.3333 10.6666 21.3333H21.3333C22.0733 21.3333 22.6666 20.74 22.6666 20V12C22.6666 11.26 22.0733 10.6667 21.3333 10.6667ZM20.6666 20H11.3333C10.9666 20 10.6666 19.7 10.6666 19.3333V20.4H21.3333V19.3333C21.3333 19.7 21.0333 20 20.6666 20ZM21.3333 14.3333H10.6666V12.6667C10.6666 12.3 10.9666 12 11.3333 12H20.6666C21.0333 12 21.3333 12.3 21.3333 12.6667V14.3333Z" fill="#3987F0"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 767 B |
|
@ -0,0 +1,32 @@
|
||||||
|
export { default as paymentGatewaySvg } from './payments/payment-gateway.svg';
|
||||||
|
export { default as paymentLinksSvg } from './payments/payment-links.svg';
|
||||||
|
export { default as paymentButtonsSvg } from './payments/payment-buttons.svg';
|
||||||
|
export { default as paymentPagesSvg } from './payments/payment-pages.svg';
|
||||||
|
export { default as instantSettlementSvg } from './payments/instant-settlements.svg';
|
||||||
|
export { default as invoicesSvg } from './payments/invoices.svg';
|
||||||
|
export { default as smartCollectSvg } from './payments/smart-collect.svg';
|
||||||
|
export { default as subscriptionsSvg } from './payments/subscriptions.svg';
|
||||||
|
export { default as routeSvg } from './payments/route.svg';
|
||||||
|
export { default as thirdwatchSvg } from './payments/thirdwatch.svg';
|
||||||
|
export { default as appStoreSvg } from './payments/app-store.svg';
|
||||||
|
export { default as paymentsMobileAppSvg } from './payments/payments-mobile-app.svg';
|
||||||
|
export { default as credPaySvg } from './payments/cred-pay.svg';
|
||||||
|
export { default as upiAutopaySvg } from './payments/upi-autopay.svg';
|
||||||
|
export { default as razorpayXSvg } from './banking/x.svg';
|
||||||
|
export { default as vendorPaymentsSvg } from './banking/vendor-payments.svg';
|
||||||
|
export { default as payoutLinkSvg } from './banking/payout-links.svg';
|
||||||
|
export { default as payoutsSvg } from './banking/payouts.svg';
|
||||||
|
export { default as currentAcountSvg } from './banking/current-account.svg';
|
||||||
|
export { default as taxPaymentsSvg } from './banking/tax-payments.svg';
|
||||||
|
export { default as payrollSvg } from './banking/payroll.svg';
|
||||||
|
export { default as capitalSvg } from './banking/capital.svg';
|
||||||
|
export { default as cashAdvanceSvg } from './banking/cash-advance.svg';
|
||||||
|
export { default as corporateCardsSvg } from './banking/corporate-cards.svg';
|
||||||
|
export { default as workingCapitalLoansSvg } from './banking/capital-loans.svg';
|
||||||
|
export { default as pricingSvg } from './pricing.svg';
|
||||||
|
export { default as resourcesSvg } from './resources.svg';
|
||||||
|
export { default as partnersSvg } from './partners.svg';
|
||||||
|
export { default as supportSvg } from './support.svg';
|
||||||
|
export { default as explorePaymentsSvg } from './explore-payments.svg';
|
||||||
|
export { default as exploreBankingSvg } from './explore-banking.svg';
|
||||||
|
export { default as unregisteredBusinessSvg } from './unregistered-business.svg';
|
|
@ -0,0 +1 @@
|
||||||
|
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M16 32c8.837 0 16-7.163 16-16S24.837 0 16 0 0 7.163 0 16s7.163 16 16 16z" fill="#E7F1FD"/><path d="M7.639 22.13c.45 0 .815-.388.815-.867 0-2.243 1.713-4.067 3.819-4.067s3.82 1.824 3.82 4.066c0 .48.364.868.814.868.45 0 .815-.388.815-.867 0-2.258-1.22-4.214-2.993-5.172.607-.661.985-1.563.985-2.558 0-2.02-1.544-3.663-3.441-3.663-1.897 0-3.44 1.643-3.44 3.663 0 .995.377 1.897.984 2.558-1.773.958-2.993 2.914-2.993 5.171 0 .48.365.868.815.868zm4.634-10.525c.999 0 1.811.865 1.811 1.928 0 1.064-.812 1.928-1.811 1.928-.999 0-1.811-.864-1.811-1.928 0-1.063.812-1.928 1.81-1.928z" fill="#3987F0"/><path d="M17.056 22.13c.363 0 .657-.313.657-.699 0-1.806 1.38-3.275 3.075-3.275 1.696 0 3.076 1.47 3.076 3.275 0 .386.294.699.657.699.362 0 .656-.313.656-.699 0-1.818-.983-3.393-2.41-4.165.489-.532.793-1.258.793-2.06 0-1.626-1.244-2.95-2.772-2.95-1.527 0-2.77 1.324-2.77 2.95 0 .802.304 1.528.793 2.06-1.428.772-2.41 2.347-2.41 4.165 0 .386.293.699.655.699zm3.732-8.477c.805 0 1.46.697 1.46 1.553 0 .857-.655 1.553-1.46 1.553-.804 0-1.458-.696-1.458-1.553 0-.856.654-1.553 1.458-1.553z" fill="#3987F0"/></svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19.8833 9.76666L22.2417 12.125L19.8833 14.4833L17.525 12.125L19.8833 9.76666ZM13.5 10.1667V13.5H10.1667V10.1667H13.5ZM21.8333 18.5V21.8333H18.5V18.5H21.8333ZM13.5 18.5V21.8333H10.1667V18.5H13.5ZM20.5898 8.11356C20.1994 7.72387 19.5672 7.72387 19.1769 8.11356L15.875 11.4096C15.4839 11.8 15.4836 12.4336 15.8744 12.8244L19.1762 16.1262C19.5668 16.5167 20.1999 16.5167 20.5904 16.1262L23.8923 12.8244C24.283 12.4336 24.2828 11.8 23.8916 11.4096L20.5898 8.11356ZM15.1667 9.49999C15.1667 8.94771 14.719 8.49999 14.1667 8.49999H9.5C8.94771 8.49999 8.5 8.94771 8.5 9.49999V14.1667C8.5 14.7189 8.94772 15.1667 9.5 15.1667H14.1667C14.719 15.1667 15.1667 14.7189 15.1667 14.1667V9.49999ZM23.5 17.8333C23.5 17.281 23.0523 16.8333 22.5 16.8333H17.8333C17.281 16.8333 16.8333 17.281 16.8333 17.8333V22.5C16.8333 23.0523 17.281 23.5 17.8333 23.5H22.5C23.0523 23.5 23.5 23.0523 23.5 22.5V17.8333ZM15.1667 17.8333C15.1667 17.281 14.719 16.8333 14.1667 16.8333H9.5C8.94771 16.8333 8.5 17.281 8.5 17.8333V22.5C8.5 23.0523 8.94772 23.5 9.5 23.5H14.1667C14.719 23.5 15.1667 23.0523 15.1667 22.5V17.8333Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.31982 8.74649C8.31982 8.51085 8.51085 8.31982 8.74649 8.31982H23.2532C23.4888 8.31982 23.6798 8.51085 23.6798 8.74649V21.1198C23.6798 21.2761 23.5944 21.4199 23.4571 21.4946L16.2037 25.4413C16.0766 25.5105 15.923 25.5105 15.7959 25.4413L8.54257 21.4946C8.40528 21.4199 8.31982 21.2761 8.31982 21.1198V8.74649ZM9.17316 9.17316V20.8662L15.9998 24.5808L22.8265 20.8662V9.17316H9.17316Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5867 11.3067H12.6934V10.4534H21.0134C21.249 10.4534 21.44 10.6444 21.44 10.88V14.6134H20.5867V11.3067Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5601 13.12C10.5601 12.8844 10.7511 12.6934 10.9867 12.6934H18.2401V13.5467H11.4134V19.5837L16.0001 22.0234L20.5867 19.5837V16.96H21.4401V19.84C21.4401 19.9978 21.353 20.1426 21.2138 20.2167L16.2004 22.8834C16.0752 22.95 15.925 22.95 15.7997 22.8834L10.7864 20.2167C10.6471 20.1426 10.5601 19.9978 10.5601 19.84V13.12Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.7998 18.4533V15.7866H13.6531V18.2069L16.007 19.5648L18.5709 18.1843L18.9754 18.9356L16.2021 20.429C16.0719 20.4991 15.9147 20.4968 15.7866 20.4229L13.0133 18.8229C12.8812 18.7467 12.7998 18.6058 12.7998 18.4533Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.006 17.5756L16.5643 7.87978C16.6741 7.68823 16.9675 7.77968 16.949 7.9997L16.4569 13.8519H21.2468C21.4049 13.8519 21.5043 14.0225 21.4263 14.16L15.7821 24.118C15.6733 24.3101 15.3795 24.2201 15.3969 24L15.8808 17.8846H11.185C11.0264 17.8846 10.9271 17.7131 11.006 17.5756Z" stroke="white" stroke-width="1.35"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 427 B |
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.3401 8.90898H11.0896C10.8705 8.90898 10.6929 9.0866 10.6929 9.3057V24.0944C10.6929 24.3135 10.8705 24.4911 11.0896 24.4911H21.3401C21.5592 24.4911 21.7369 24.3135 21.7369 24.0944V9.3057C21.7369 9.0866 21.5592 8.90898 21.3401 8.90898ZM11.0896 7.85107C10.2863 7.85107 9.63501 8.50233 9.63501 9.3057V24.0944C9.63501 24.8978 10.2863 25.549 11.0896 25.549H21.3401C22.1435 25.549 22.7948 24.8978 22.7948 24.0944V9.3057C22.7948 8.50233 22.1435 7.85107 21.3401 7.85107H11.0896Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<path d="M12.2148 14.5C12.2148 14.2239 12.4685 14 12.7814 14H19.6483C19.9612 14 20.2148 14.2239 20.2148 14.5C20.2148 14.7761 19.9612 15 19.6483 15H12.7814C12.4685 15 12.2148 14.7761 12.2148 14.5Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<path d="M12.2148 17.5C12.2148 17.2239 12.4685 17 12.7814 17H19.6483C19.9612 17 20.2148 17.2239 20.2148 17.5C20.2148 17.7761 19.9612 18 19.6483 18H12.7814C12.4685 18 12.2148 17.7761 12.2148 17.5Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<path d="M12.2148 20.5C12.2148 20.2239 12.4685 20 12.7814 20H19.6483C19.9612 20 20.2148 20.2239 20.2148 20.5C20.2148 20.7761 19.9612 21 19.6483 21H12.7814C12.4685 21 12.2148 20.7761 12.2148 20.5Z" fill="white" stroke="white" stroke-width="0.5"/>
|
||||||
|
<circle cx="16.2149" cy="8.43342" r="1.98249" fill="#3987F0" stroke="white" stroke-width="1.5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.0933 17.875C14.0933 14.2679 17.0175 11.3438 20.6246 11.3438H30.2496C33.8567 11.3438 36.7808 14.2679 36.7808 17.875C36.7808 21.4821 33.8567 24.4062 30.2496 24.4062H20.6246C17.0175 24.4062 14.0933 21.4821 14.0933 17.875ZM20.6246 13.4062C18.1566 13.4062 16.1558 15.407 16.1558 17.875C16.1558 20.343 18.1566 22.3438 20.6246 22.3438H30.2496C32.7176 22.3438 34.7183 20.343 34.7183 17.875C34.7183 15.407 32.7176 13.4062 30.2496 13.4062H20.6246Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.7001 18.2188H15.1246C11.5175 18.2188 8.59332 21.1429 8.59332 24.75C8.59332 28.3571 11.5175 31.2812 15.1246 31.2812H24.7496C28.3567 31.2812 31.2808 28.3571 31.2808 24.75C31.2808 24.2892 31.2331 23.8395 31.1423 23.4056C30.8924 23.4529 30.6345 23.4776 30.3708 23.4776H29.0346C29.1541 23.8809 29.2183 24.3079 29.2183 24.75C29.2183 27.218 27.2176 29.2188 24.7496 29.2188H15.1246C12.6566 29.2188 10.6558 27.218 10.6558 24.75C10.6558 22.282 12.6566 20.2812 15.1246 20.2812H15.805C15.7364 19.9828 15.7001 19.6719 15.7001 19.3526V18.2188Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.9149 9.37312L22.4137 9.37312L22.4137 18.0711L11.9149 18.0711L11.9149 9.37312ZM10.2841 9.37312C10.2841 8.47241 11.0142 7.74225 11.9149 7.74225H22.4137C23.3144 7.74225 24.0445 8.47241 24.0445 9.37312V18.0711C24.0445 18.9718 23.3144 19.702 22.4137 19.702H18.4384V22.4201C18.4384 23.3208 17.7083 24.051 16.8076 24.051H9.36671C8.46601 24.051 7.73584 23.3208 7.73584 22.4201V16.9839C7.73584 16.0832 8.46601 15.353 9.36671 15.353H10.2841V9.37312ZM10.2841 16.9839H9.36671L9.36671 22.4201H16.8076L16.8076 19.702H11.9149C11.0142 19.702 10.2841 18.9718 10.2841 18.0711V16.9839Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6626 16.6642C19.4377 16.8418 19.1547 16.9476 18.8472 16.9476C18.1148 16.9476 17.521 16.3472 17.521 15.6067C17.521 14.8661 18.1148 14.2657 18.8472 14.2657C19.1547 14.2657 19.4377 14.3715 19.6626 14.5491C19.8875 14.3715 20.1706 14.2657 20.4781 14.2657C21.2105 14.2657 21.8042 14.8661 21.8042 15.6067C21.8042 16.3472 21.2105 16.9476 20.4781 16.9476C20.1706 16.9476 19.8875 16.8418 19.6626 16.6642Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2099 12.5259H10.0695V18.3478L18.2099 18.3478L18.2099 12.5259ZM10.0695 10.7997C9.11612 10.7997 8.34326 11.5725 8.34326 12.5259V18.3478C8.34326 19.3011 9.11612 20.074 10.0695 20.074H18.2099C19.1633 20.074 19.9362 19.3011 19.9362 18.3478V12.5259C19.9362 11.5725 19.1633 10.7997 18.2099 10.7997H10.0695Z" fill="white"/>
|
||||||
|
<path d="M15.8226 16.5962C15.8226 17.2364 15.3161 17.7555 14.6913 17.7555C14.0665 17.7555 13.5601 17.2364 13.5601 16.5962C13.5601 15.9559 14.0665 15.4369 14.6913 15.4369C15.3161 15.4369 15.8226 15.9559 15.8226 16.5962Z" fill="white"/>
|
||||||
|
<path d="M17.2137 16.5964C17.2137 17.2366 16.7072 17.7557 16.0824 17.7557C15.4577 17.7557 14.9512 17.2366 14.9512 16.5964C14.9512 15.9561 15.4577 15.4371 16.0824 15.4371C16.7072 15.4371 17.2137 15.9561 17.2137 16.5964Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.6658 12.4201C19.6702 12.4698 19.6725 12.5202 19.6725 12.571V13.9906H20.3825C20.9679 13.9906 21.4425 13.6109 21.4425 13.1426C21.4425 12.6742 20.9679 12.2946 20.3825 12.2946H19.6499C19.6567 12.3359 19.6621 12.3778 19.6658 12.4201Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.3152 19.3813L21.4338 19.3809C21.423 19.3809 21.4122 19.381 21.4014 19.3813C21.4122 19.381 21.423 19.3809 21.4338 19.3809L21.4338 19.3813L23.3152 19.3813ZM10.4074 22.0945V19.9328H10.66V22.0945C10.66 22.6277 11.1048 23.0716 11.6622 23.0716H19.9245L19.9245 23.0689V20.6093C19.9245 19.7831 20.6055 19.1283 21.4338 19.1283H21.6864L23.3152 19.1287C23.395 19.1287 23.472 19.1427 23.5435 19.1683V8.97699C23.5435 8.44405 23.1002 8.00232 22.5404 8.00232H10.66V10.8874H10.4074L10.4074 7.74978V11.0141H10.0269C9.86889 11.0141 9.71593 11.0358 9.5709 11.0766L9.5709 10.9564C9.48355 10.9823 9.39916 11.015 9.31836 11.0541V6.67749H22.5404C23.8299 6.67749 24.8851 7.70146 24.8851 8.97699V21.2341L24.6956 21.4269L21.976 24.1949L21.7781 24.396H11.6622C10.3731 24.396 9.31836 23.3683 9.31836 22.0941V19.7662C9.39916 19.8052 9.48355 19.8379 9.5709 19.8638L9.5709 19.9969C9.71593 20.0376 9.86889 20.0594 10.0269 20.0594H10.4074L10.4074 22.0945ZM23.801 21.9771L24.6237 21.1398L24.5154 21.25L23.801 21.9771ZM22.5404 6.93003H9.5709V6.93004H9.98917L22.5404 6.93003ZM23.3152 20.4532C23.395 20.4532 23.472 20.4392 23.5435 20.4136V20.6968L21.2665 23.0146V20.6093C21.2665 20.528 21.3334 20.4532 21.4338 20.4532H23.3152Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1 @@
|
||||||
|
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><mask id="a" mask-type="alpha" maskUnits="userSpaceOnUse" x="1" y="1" width="30" height="30"><rect x="1.076" y="1.076" width="29.867" height="29.867" rx="14.933" fill="#111B33"/></mask><g mask="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.836 9.618a1.93 1.93 0 0 1 1.93-1.929h13.633a1.93 1.93 0 0 1 1.93 1.93v21.314a1.93 1.93 0 0 1-1.93 1.93H8.765a1.93 1.93 0 0 1-1.93-1.93V9.618zm1.93-.649a.65.65 0 0 0-.65.65v21.314c0 .358.29.65.65.65h13.633a.65.65 0 0 0 .65-.65V9.618a.65.65 0 0 0-.65-.649H8.765z" fill="#fff" stroke="#fff" stroke-width=".5" stroke-miterlimit="10"/></g><rect x="10.889" y="18.104" width="1.396" height="5.585" rx=".698" fill="#fff"/><rect x="13.395" y="16.707" width="1.396" height="6.982" rx=".698" fill="#fff"/><rect x="9.983" y="12.626" width="1.396" height="6.982" rx=".698" transform="rotate(-90 9.983 12.626)" fill="#fff"/><rect x="9.949" y="28.235" width="1.345" height="2.896" rx=".672" transform="rotate(-90 9.95 28.235)" fill="#fff"/><rect x="14.293" y="28.235" width="1.345" height="2.896" rx=".672" transform="rotate(-90 14.293 28.235)" fill="#fff"/><rect x="18.348" y="28.235" width="1.345" height="2.896" rx=".672" transform="rotate(-90 18.348 28.235)" fill="#fff"/><rect x="15.9" y="19.5" width="1.396" height="4.189" rx=".698" fill="#fff"/><rect x="18.406" y="16.009" width="1.396" height="7.68" rx=".698" fill="#fff"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.1605 13.4783C21.7061 13.4783 21.3925 13.8495 21.3413 14.2975C21.0213 16.9855 19.5877 17.7215 18.2181 18.1439C17.9685 18.2207 17.5845 18.3935 17.5845 18.5279V15.7567C17.5845 15.7247 17.5845 15.6927 17.5845 15.6543V8.67832C17.5845 8.22392 17.0981 7.85912 16.6437 7.85912C16.1893 7.85912 15.7029 8.22392 15.7029 8.67832V13.3951C15.7029 13.2607 15.2741 13.1391 15.0373 13.0111C13.4629 12.1855 11.8565 11.4431 11.8565 8.67192C11.8565 8.21752 11.4981 7.85272 11.0501 7.85272C10.5957 7.85272 10.2373 8.21752 10.2373 8.67192C10.2373 12.4415 12.6501 13.7279 14.2757 14.5791C15.1653 15.0463 15.7029 15.3535 15.7029 15.7311V24.1471C15.7029 24.6015 16.1893 24.9663 16.6437 24.9663C17.0981 24.9663 17.5845 24.6015 17.5845 24.1471V21.3823C17.5845 20.9919 18.0645 20.6783 18.9797 20.2047C20.6053 19.3535 23.0245 18.0671 23.0245 14.2975C23.0181 13.8495 22.6149 13.4783 22.1605 13.4783Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.619 17.891L14.211 16.483C13.9166 16.1886 13.9166 15.7022 14.211 15.4078C14.5054 15.1134 14.9918 15.1134 15.2862 15.4078L16.0734 16.195L18.0574 14.211C18.3518 13.9166 18.8382 13.9166 19.1326 14.211C19.427 14.5054 19.427 14.9918 19.1326 15.2862L16.5278 17.891C16.2718 18.1406 15.8686 18.1406 15.619 17.891Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.4938 24.7408C16.3978 24.7408 16.3018 24.7216 16.2058 24.6896C8.82019 22.1296 8.93539 16.1008 9.01859 11.6976C9.02499 11.2368 9.03779 10.7952 9.03779 10.3728C9.03779 9.8928 9.42819 9.5024 9.90819 9.5024C12.4426 9.5024 14.2858 8.8112 15.8858 7.2496C16.225 6.9168 16.7626 6.9168 17.1018 7.2496C18.7018 8.8048 20.545 9.5024 23.0794 9.5024C23.5594 9.5024 23.9498 9.8928 23.9498 10.3728C23.9498 10.7952 23.9562 11.2304 23.969 11.6976C24.0522 16.1008 24.1674 22.1296 16.7818 24.6896C16.6858 24.728 16.5898 24.7408 16.4938 24.7408ZM10.7722 11.2304L10.7658 11.7424C10.6826 15.9728 10.593 20.7536 16.4938 22.9488C22.3946 20.7536 22.305 15.9664 22.2218 11.7424L22.2154 11.2304C19.9434 11.0896 18.0938 10.392 16.4938 9.0608C14.8938 10.3856 13.0442 11.0896 10.7722 11.2304Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M24.6789 11.2693L23.7377 12.0221C24.2826 13.0625 25.0301 13.9607 25.0301 16.8686C24.3839 22.2868 20.406 25.269 16.3065 25.269V23.0074L12.7524 25.5921L16.3064 28.5V26.4537C21.7807 26.4537 26.2146 22.0197 26.2146 16.5454C26.2146 14.6009 25.6449 12.7927 24.6789 11.2693Z" fill="white" stroke="white" stroke-width="0.4"/>
|
||||||
|
<path d="M8.56171 22.3802L9.46633 21.5838C8.8729 20.5703 8.08393 19.7083 7.94674 16.8037C7.81064 11.6348 12.1693 8.19442 16.2643 8.00101L16.371 10.2602L19.7991 7.5106L16.112 4.77365L16.2085 6.81767C10.7403 7.07594 6.52046 11.7141 6.77873 17.1823C6.87046 19.1247 7.52486 20.904 8.56171 22.3802Z" fill="white" stroke="white" stroke-width="0.4"/>
|
||||||
|
<path d="M18.0123 13.488C18.1784 13.7561 18.2923 14.0868 18.3538 14.48H19.2308V15.5525H18.4C18.3569 16.196 18.1938 16.7277 17.9107 17.1478C17.6338 17.5678 17.2676 17.8404 16.8122 17.9655C16.9968 18.0281 17.163 18.1398 17.3107 18.3007C17.4584 18.4616 17.5969 18.6895 17.7261 18.9844L19.1015 21.9605H17.6799L16.2953 18.9174C16.1722 18.6314 16.0399 18.4303 15.8983 18.3141C15.7629 18.189 15.5814 18.1264 15.3537 18.1264H14.0244V16.7724H15.9906C16.3476 16.7724 16.6307 16.6696 16.8399 16.4641C17.0491 16.2585 17.1784 15.9547 17.2276 15.5525H14.0244V14.48H17.1907C17.123 14.1493 16.9876 13.9036 16.7845 13.7427C16.5814 13.5729 16.3168 13.488 15.9906 13.488H14.0244V12.4155H19.2308V13.488H18.0123Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.1008 21C17.1008 21.5797 16.6516 22 16.1578 22C15.6641 22 15.2148 21.5797 15.2148 21C15.2148 20.4203 15.6641 20 16.1578 20C16.6516 20 17.1008 20.4203 17.1008 21Z" stroke="white" stroke-width="2"/>
|
||||||
|
<path d="M23.0712 8.23901V21.287C20.4641 14.9658 11.6243 15.2722 9.05298 21.2362L9.053 8.23901H23.0712Z" stroke="white" stroke-width="1.5" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 477 B |
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.9277 10.2389L25.8023 10.02L24.3471 11.0803L24.426 11.249C24.5042 11.4161 24.5846 11.5766 24.6653 11.7374C24.8858 12.177 25.1077 12.6194 25.2872 13.2075C25.5288 13.9994 25.6896 15.053 25.6181 16.6667C24.6435 22.5313 20.2372 25.6039 15.8039 25.5118L15.9286 22.7437L11.1702 25.8871L15.6077 29.8643L15.7232 27.3013C21.9291 27.4605 27.1422 22.6253 27.4228 16.3959C27.5237 14.1559 26.9611 12.0433 25.9277 10.2389Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.78071 22.1964L6.91626 22.4091L8.31984 21.2814L8.23304 21.1166C8.1471 20.9534 8.05914 20.7969 7.97098 20.6401C7.72998 20.2113 7.48748 19.7799 7.28045 19.2009C7.00106 18.4195 6.79039 17.3717 6.78665 15.7501C6.89266 10.149 11.7024 6.58909 16.1726 6.46307L16.1787 9.23379L20.7835 5.86945L16.1633 2.10595L16.1689 4.67154C9.96236 4.80539 4.98315 9.88115 4.99676 16.1168C5.00165 18.3591 5.66329 20.4428 6.78071 22.1964Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.6633 10.8764L19.2947 16.0807L17.1966 17.3872L17.1166 18.8607L21.4035 16.1913L17.4308 13.0734L17.3506 14.5508L16.3812 13.7901L16.5282 11.0823L23.1596 16.2866L16.0038 20.7425L16.1506 18.0386L12.1389 20.5366L12.6633 10.8764ZM13.5659 12.8675L17.5386 15.9854L13.2517 18.6548L13.5659 12.8675Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="16" cy="16" r="16" fill="#CB7100" fill-opacity="0.09"/>
|
||||||
|
<path d="M22.5397 16.189C22.481 16.0689 22.481 15.9312 22.5397 15.8111L23.0839 14.6978C23.3869 14.0779 23.1469 13.3391 22.5374 13.0157L21.4428 12.4349C21.3247 12.3723 21.2437 12.2608 21.2206 12.1291L21.0065 10.9086C20.8873 10.229 20.2587 9.77235 19.5757 9.86899L18.3487 10.0425C18.2163 10.0612 18.0853 10.0187 17.9893 9.92573L17.0987 9.06416C16.6028 8.58442 15.8259 8.58439 15.33 9.06416L14.4394 9.92582C14.3433 10.0188 14.2123 10.0613 14.0799 10.0426L12.853 9.86908C12.1697 9.77238 11.5413 10.2291 11.4221 10.9087L11.2081 12.1292C11.1849 12.2608 11.104 12.3723 10.9859 12.435L9.89128 13.0158C9.28181 13.3391 9.04174 14.078 9.34473 14.6979L9.88891 15.8111C9.94762 15.9312 9.94762 16.069 9.88891 16.1891L9.3447 17.3024C9.04171 17.9222 9.28178 18.6611 9.89125 18.9844L10.9859 19.5652C11.104 19.6279 11.1849 19.7394 11.2081 19.871L11.4221 21.0916C11.5307 21.7102 12.0611 22.1441 12.6708 22.144C12.7308 22.144 12.7918 22.1398 12.853 22.1311L14.08 21.9576C14.2123 21.9388 14.3433 21.9815 14.4394 22.0744L15.33 22.936C15.578 23.1759 15.8961 23.2958 16.2143 23.2957C16.5325 23.2957 16.8508 23.1758 17.0986 22.936L17.9893 22.0744C18.0854 21.9815 18.2163 21.939 18.3487 21.9576L19.5757 22.1311C20.259 22.2278 20.8873 21.7711 21.0065 21.0915L21.2206 19.871C21.2437 19.7394 21.3247 19.6279 21.4428 19.5652L22.5374 18.9844C23.1469 18.6611 23.3869 17.9222 23.0839 17.3023L22.5397 16.189ZM14.5312 12.213C15.3819 12.213 16.0741 12.9052 16.0741 13.7559C16.0741 14.6066 15.3819 15.2988 14.5312 15.2988C13.6804 15.2988 12.9883 14.6066 12.9883 13.7559C12.9883 12.9052 13.6804 12.213 14.5312 12.213ZM13.7348 19.0746C13.6527 19.1568 13.545 19.1979 13.4373 19.1979C13.3296 19.1979 13.2219 19.1568 13.1398 19.0746C12.9754 18.9103 12.9754 18.6439 13.1398 18.4795L18.6938 12.9255C18.8581 12.7612 19.1246 12.7612 19.2889 12.9255C19.4532 13.0898 19.4532 13.3562 19.2889 13.5206L13.7348 19.0746ZM17.8974 19.7871C17.0467 19.7871 16.3545 19.095 16.3545 18.2442C16.3545 17.3935 17.0467 16.7014 17.8974 16.7014C18.7482 16.7014 19.4403 17.3935 19.4403 18.2442C19.4403 19.095 18.7482 19.7871 17.8974 19.7871Z" fill="#DF8700"/>
|
||||||
|
<path d="M18.0605 17.6918C17.6365 17.6918 17.2915 18.0368 17.2915 18.4608C17.2915 18.8849 17.6365 19.2298 18.0605 19.2298C18.4846 19.2298 18.8295 18.8849 18.8295 18.4608C18.8295 18.0368 18.4846 17.6918 18.0605 17.6918Z" fill="#DF8700"/>
|
||||||
|
<path d="M14.3691 12.7701C13.9451 12.7701 13.6001 13.1151 13.6001 13.5391C13.6001 13.9632 13.9451 14.3082 14.3691 14.3082C14.7931 14.3082 15.1381 13.9632 15.1381 13.5391C15.1381 13.1151 14.7931 12.7701 14.3691 12.7701Z" fill="#DF8700"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
|
@ -0,0 +1,7 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.1616 0 0 7.1616 0 16C0 24.8384 7.1616 32 16 32C24.8384 32 32 24.8384 32 16C32 7.1616 24.8384 0 16 0Z" fill="#DBEBFF"/>
|
||||||
|
<path d="M20.125 10.375C18.6625 10.375 17.0875 10.675 16 11.5C14.9125 10.675 13.3375 10.375 11.875 10.375C10.7875 10.375 9.6325 10.54 8.665 10.9675C8.1175 11.215 7.75 11.7475 7.75 12.355V20.815C7.75 21.79 8.665 22.51 9.61 22.27C10.345 22.0825 11.125 22 11.875 22C13.045 22 14.29 22.195 15.295 22.69C15.745 22.915 16.255 22.915 16.6975 22.69C17.7025 22.1875 18.9475 22 20.1175 22C20.8675 22 21.6475 22.0825 22.3825 22.27C23.3275 22.5175 24.2425 21.7975 24.2425 20.815V12.355C24.2425 11.7475 23.875 11.215 23.3275 10.9675C22.3675 10.54 21.2125 10.375 20.125 10.375ZM22.75 19.9225C22.75 20.395 22.315 20.74 21.85 20.6575C21.2875 20.5525 20.7025 20.5075 20.125 20.5075C18.85 20.5075 17.0125 20.995 16 21.6325V13C17.0125 12.3625 18.85 11.875 20.125 11.875C20.815 11.875 21.4975 11.9425 22.15 12.085C22.495 12.16 22.75 12.4675 22.75 12.82V19.9225Z" fill="#2B83EA"/>
|
||||||
|
<path d="M17.4852 15.2575C17.2452 15.2575 17.0277 15.1075 16.9527 14.8675C16.8552 14.575 17.0202 14.2525 17.3127 14.1625C18.4677 13.7875 19.9602 13.6675 21.3327 13.825C21.6402 13.8625 21.8652 14.14 21.8277 14.4475C21.7902 14.755 21.5127 14.98 21.2052 14.9425C19.9902 14.8 18.6627 14.9125 17.6577 15.235C17.5977 15.2425 17.5377 15.2575 17.4852 15.2575Z" fill="#2B83EA"/>
|
||||||
|
<path d="M17.4852 17.2525C17.2452 17.2525 17.0277 17.1025 16.9527 16.8625C16.8552 16.57 17.0202 16.2475 17.3127 16.1575C18.4602 15.7825 19.9602 15.6625 21.3327 15.82C21.6402 15.8575 21.8652 16.135 21.8277 16.4425C21.7902 16.75 21.5127 16.975 21.2052 16.9375C19.9902 16.795 18.6627 16.9075 17.6577 17.23C17.5977 17.245 17.5377 17.2525 17.4852 17.2525Z" fill="#2B83EA"/>
|
||||||
|
<path d="M17.4852 19.2474C17.2452 19.2474 17.0277 19.0974 16.9527 18.8574C16.8552 18.5649 17.0202 18.2424 17.3127 18.1524C18.4602 17.7774 19.9602 17.6574 21.3327 17.8149C21.6402 17.8524 21.8652 18.1299 21.8277 18.4374C21.7902 18.7449 21.5127 18.9624 21.2052 18.9324C19.9902 18.7899 18.6627 18.9024 17.6577 19.2249C17.5977 19.2399 17.5377 19.2474 17.4852 19.2474Z" fill="#2B83EA"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.1616 0 0 7.1616 0 16C0 24.8384 7.1616 32 16 32C24.8384 32 32 24.8384 32 16C32 7.1616 24.8384 0 16 0Z" fill="#DBEBFF"/>
|
||||||
|
<path d="M15.5333 8.01418C11.8156 8.23964 9 11.287 9 14.7707V19.6363C9 20.8436 10.0422 21.8181 11.3333 21.8181H12.1111C12.9667 21.8181 13.6667 21.1636 13.6667 20.3636V17.4544C13.6667 16.6544 12.9667 15.9998 12.1111 15.9998H10.5556V14.7562C10.5556 11.9634 12.8578 9.53422 15.8367 9.45422C18.9244 9.37421 21.4444 11.6797 21.4444 14.5452V15.9998H19.8889C19.0333 15.9998 18.3333 16.6544 18.3333 17.4544V20.3636C18.3333 21.1636 19.0333 21.8181 19.8889 21.8181H21.4444V22.5454H16.7778C16.35 22.5454 16 22.8727 16 23.2727C16 23.6727 16.35 24 16.7778 24H20.6667C21.9578 24 23 23.0254 23 21.8181V14.5452C23 10.7852 19.6089 7.76691 15.5333 8.01418Z" fill="#3987F0"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 936 B |
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19.1178 28.4638C18.5213 30.2531 17.0999 31.5677 15.0069 32.2653C14.0681 32.5758 13.089 32.7472 12.1006 32.7744C11.8685 32.7744 11.6459 32.6822 11.4818 32.5181C11.3177 32.354 11.2256 32.1314 11.2256 31.8994C11.2527 30.9109 11.4242 29.9318 11.7346 28.993C12.4322 26.9 13.7468 25.4785 15.5361 24.8821C15.6451 24.8457 15.7603 24.8312 15.8749 24.8394C15.9895 24.8475 16.1014 24.8781 16.2042 24.9295C16.3069 24.9809 16.3986 25.052 16.4739 25.1389C16.5492 25.2257 16.6066 25.3264 16.643 25.4355C16.6793 25.5445 16.6938 25.6596 16.6857 25.7742C16.6775 25.8888 16.6469 26.0007 16.5955 26.1035C16.5441 26.2062 16.473 26.2979 16.3862 26.3732C16.2994 26.4485 16.1986 26.5059 16.0896 26.5423C13.9396 27.2589 13.2736 29.5927 13.0677 30.9324C14.4066 30.7264 16.7409 30.0605 17.4576 27.9103C17.494 27.8013 17.5514 27.7005 17.6267 27.6137C17.702 27.5269 17.7937 27.4558 17.8965 27.4044C17.9992 27.353 18.1111 27.3223 18.2258 27.3142C18.3404 27.306 18.4555 27.3206 18.5645 27.3569C18.6735 27.3932 18.7743 27.4507 18.8611 27.526C18.9479 27.6013 19.0191 27.6929 19.0705 27.7957C19.1219 27.8985 19.1525 28.0104 19.1607 28.125C19.1688 28.2396 19.1543 28.3548 19.118 28.4638H19.1178ZM30.0434 20.1436L29.0624 21.1248V27.8246C29.0631 28.0545 29.0181 28.2822 28.9301 28.4946C28.8421 28.707 28.7129 28.8998 28.5499 29.0619L25.0123 32.5994C24.7883 32.8234 24.5077 32.9824 24.2005 33.0594C23.8932 33.1365 23.5708 33.1287 23.2676 33.0369C22.9645 32.9451 22.6919 32.7728 22.479 32.5382C22.2661 32.3037 22.1208 32.0157 22.0587 31.7051L21.194 27.3811L16.6188 22.8059L12.2948 21.9411C11.9842 21.879 11.6963 21.7337 11.4618 21.5208C11.2273 21.308 11.0549 21.0354 10.9631 20.7323C10.8713 20.4291 10.8635 20.1068 10.9405 19.7995C11.0175 19.4923 11.1765 19.2117 11.4005 18.9877L14.9377 15.4502C15.0999 15.2872 15.2928 15.158 15.5052 15.0701C15.7176 14.9821 15.9453 14.9371 16.1752 14.9377H22.8751L23.8562 13.9565C26.9831 10.8299 30.1915 10.9206 31.426 11.1048C31.7966 11.1585 32.1398 11.3306 32.4046 11.5954C32.6693 11.8601 32.8414 12.2033 32.8953 12.5739C33.0796 13.8083 33.1703 17.0168 30.0435 20.1437L30.0434 20.1436ZM12.6379 20.225L16.7627 21.0499L21.1251 16.6877H16.1753L12.6379 20.225ZM18.2879 21.9998L22.0001 25.7121L28.806 18.9063C29.6602 18.052 31.5998 15.7503 31.1644 12.8323C28.2485 12.4 25.9478 14.3398 25.0937 15.194L18.2879 21.9998ZM27.3124 22.8748L22.95 27.2372L23.775 31.362L27.3123 27.8246L27.3124 22.8748Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |