Browse Source

Init admin

webui
Ruben Cid 6 years ago
parent
commit
268b401430
  1. 6
      .dockerignore
  2. 12
      .gitignore
  3. 59
      Dockerfile
  4. 42
      Makefile
  5. 25
      configure/liveconfig.go
  6. 2
      go.mod
  7. 11
      go.sum
  8. 4
      protocol/dashboard/dashboard.go
  9. 15
      protocol/httpopera/http_opera.go
  10. 5
      webui/.dockerignore
  11. 2
      webui/.gitignore
  12. 13
      webui/Dockerfile
  13. 41
      webui/package.json
  14. 1
      webui/public/_redirects
  15. BIN
      webui/public/favicon.ico
  16. BIN
      webui/public/images/auth.jpg
  17. BIN
      webui/public/images/avatars/avatar_1.png
  18. BIN
      webui/public/images/avatars/avatar_10.png
  19. BIN
      webui/public/images/avatars/avatar_11.png
  20. BIN
      webui/public/images/avatars/avatar_2.png
  21. BIN
      webui/public/images/avatars/avatar_3.png
  22. BIN
      webui/public/images/avatars/avatar_4.png
  23. BIN
      webui/public/images/avatars/avatar_5.png
  24. BIN
      webui/public/images/avatars/avatar_6.png
  25. BIN
      webui/public/images/avatars/avatar_7.png
  26. BIN
      webui/public/images/avatars/avatar_8.png
  27. BIN
      webui/public/images/avatars/avatar_9.png
  28. 10
      webui/public/images/logos/logo--white.svg
  29. BIN
      webui/public/images/not_found.png
  30. BIN
      webui/public/images/products/product_1.png
  31. BIN
      webui/public/images/products/product_2.png
  32. BIN
      webui/public/images/products/product_3.png
  33. BIN
      webui/public/images/products/product_4.png
  34. BIN
      webui/public/images/products/product_5.png
  35. BIN
      webui/public/images/products/product_6.png
  36. 1
      webui/public/images/undraw_page_not_found_su7k.svg
  37. 1
      webui/public/images/undraw_resume_folder_2_arse.svg
  38. 41
      webui/public/index.html
  39. BIN
      webui/public/logo192.png
  40. BIN
      webui/public/logo512.png
  41. 10
      webui/public/manifest.json
  42. 3
      webui/public/robots.txt
  43. 54
      webui/readme.md
  44. 38
      webui/src/App.css
  45. 58
      webui/src/App.js
  46. 9
      webui/src/App.test.js
  47. 93
      webui/src/Routes.js
  48. 24
      webui/src/assets/scss/index.scss
  49. 9
      webui/src/common/validators.js
  50. 26
      webui/src/components/RouteWithLayout/RouteWithLayout.js
  51. 1
      webui/src/components/RouteWithLayout/index.js
  52. 56
      webui/src/components/SearchInput/SearchInput.js
  53. 1
      webui/src/components/SearchInput/index.js
  54. 83
      webui/src/components/StatusBullet/StatusBullet.js
  55. 1
      webui/src/components/StatusBullet/index.js
  56. 3
      webui/src/components/index.js
  57. 192
      webui/src/helpers/chartjs.js
  58. 7
      webui/src/helpers/getInitials.js
  59. 2
      webui/src/helpers/index.js
  60. 12
      webui/src/icons/Facebook/index.js
  61. 12
      webui/src/icons/Google/index.js
  62. 2
      webui/src/icons/index.js
  63. 13
      webui/src/index.css
  64. 14
      webui/src/index.js
  65. 71
      webui/src/layouts/Main/Main.js
  66. 46
      webui/src/layouts/Main/components/Footer/Footer.js
  67. 1
      webui/src/layouts/Main/components/Footer/index.js
  68. 119
      webui/src/layouts/Main/components/Sidebar/Sidebar.js
  69. 62
      webui/src/layouts/Main/components/Sidebar/components/Profile/Profile.js
  70. 1
      webui/src/layouts/Main/components/Sidebar/components/Profile/index.js
  71. 88
      webui/src/layouts/Main/components/Sidebar/components/SidebarNav/SidebarNav.js
  72. 1
      webui/src/layouts/Main/components/Sidebar/components/SidebarNav/index.js
  73. 79
      webui/src/layouts/Main/components/Sidebar/components/UpgradePlan/UpgradePlan.js
  74. 1
      webui/src/layouts/Main/components/Sidebar/components/UpgradePlan/index.js
  75. 3
      webui/src/layouts/Main/components/Sidebar/components/index.js
  76. 1
      webui/src/layouts/Main/components/Sidebar/index.js
  77. 78
      webui/src/layouts/Main/components/Topbar/Topbar.js
  78. 1
      webui/src/layouts/Main/components/Topbar/index.js
  79. 3
      webui/src/layouts/Main/components/index.js
  80. 1
      webui/src/layouts/Main/index.js
  81. 35
      webui/src/layouts/Minimal/Minimal.js
  82. 42
      webui/src/layouts/Minimal/components/Topbar/Topbar.js
  83. 1
      webui/src/layouts/Minimal/components/Topbar/index.js
  84. 1
      webui/src/layouts/Minimal/components/index.js
  85. 1
      webui/src/layouts/Minimal/index.js
  86. 2
      webui/src/layouts/index.js
  87. 7
      webui/src/logo.svg
  88. 22
      webui/src/serviceWorker.js
  89. 5
      webui/src/setupTests.js
  90. 17
      webui/src/theme/index.js
  91. 7
      webui/src/theme/overrides/MuiButton.js
  92. 10
      webui/src/theme/overrides/MuiIconButton.js
  93. 5
      webui/src/theme/overrides/MuiPaper.js
  94. 9
      webui/src/theme/overrides/MuiTableCell.js
  95. 7
      webui/src/theme/overrides/MuiTableHead.js
  96. 14
      webui/src/theme/overrides/MuiTableRow.js
  97. 5
      webui/src/theme/overrides/MuiTypography.js
  98. 15
      webui/src/theme/overrides/index.js
  99. 56
      webui/src/theme/palette.js
  100. 89
      webui/src/theme/typography.js
  101. Some files were not shown because too many files have changed in this diff Show More

6
.dockerignore

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
dist/
site/
vendor/
.idea/
.vscode/
room_keys.json

12
.gitignore vendored

@ -3,7 +3,11 @@ @@ -3,7 +3,11 @@
dist
room_keys.json
.vscode
.tmp
vendor
node_modules
build/
.tmp/
vendor/
node_modules/
build/
livego
static/
webui/.tmp/
pkged.go

59
Dockerfile

@ -1,22 +1,53 @@ @@ -1,22 +1,53 @@
FROM golang:latest as builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o livego .
FROM alpine:latest
LABEL maintainer="Ruben Cid Lara <rubencidlara@gmail.com>"
RUN mkdir -p /app/config
WORKDIR /app
# WEBUI
FROM node:13.12 as webui
ENV WEBUI_DIR /src/webui
RUN mkdir -p $WEBUI_DIR
COPY ./webui/ $WEBUI_DIR/
WORKDIR $WEBUI_DIR
RUN npm install
RUN npm run build
# BUILD
FROM golang:latest as gobuild
RUN go get github.com/markbates/pkger/cmd/pkger
WORKDIR /go/src/github.com/gwuhaolin/livego
# Download go modules
COPY go.mod .
COPY go.sum .
RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download
ENV REPO github.com/gwuhaolin/livego
COPY . /go/src/github.com/gwuhaolin/livego
RUN rm -rf /go/src/github.com/gwuhaolin/livego/static/
COPY --from=webui /src/webui/static/ /go/src/github.com/gwuhaolin/livego/static/
RUN make build
## IMAGE
FROM alpine:3.10
COPY --from=gobuild /go/src/github.com/gwuhaolin/livego/config /
COPY --from=gobuild /go/src/github.com/gwuhaolin/livego/livego /
VOLUME ["/tmp"]
ENV RTMP_PORT 1935
ENV HTTP_FLV_PORT 7001
ENV HLS_PORT 7002
ENV HTTP_OPERATION_PORT 8090
COPY --from=builder /app/config ./config
COPY --from=builder /app/livego .
EXPOSE ${RTMP_PORT}
EXPOSE ${HTTP_FLV_PORT}
EXPOSE ${HLS_PORT}
EXPOSE ${HTTP_OPERATION_PORT}
ENTRYPOINT ["./livego"]
ENTRYPOINT ["/livego"]

42
Makefile

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
GO_BIN ?= go
tidy:
ifeq ($(GO111MODULE),on)
$(GO_BIN) mod tidy
else
echo skipping go mod tidy
endif
build:
pkger
$(GO_BIN) build -v .
make tidy
test:
pkger
$(GO_BIN) test -tags ${TAGS} -cover ./...
pkger
make tidy
lint:
golangci-lint --vendor ./... --deadline=1m --skip=internal
## Build WebUI Docker image
build-webui-image:
docker build -t livego-webui -f webui/Dockerfile webui
generate-webui: build-webui-image
if [ ! -d "static" ]; then \
mkdir -p static; \
docker run --rm -v "$$PWD/static":'/src/webui/build' livego-webui npm run build; \
docker run --rm -v "$$PWD/static":'/src/webui/build' livego-webui chown -R $(shell id -u):$(shell id -g) ./build; \
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
fi
dockerize:
docker build -t gwuhaolin:livego .
binary: generate-webui
make build
default: binary

25
configure/liveconfig.go

@ -21,22 +21,20 @@ import ( @@ -21,22 +21,20 @@ import (
*/
var (
roomKeySaveFile = flag.String("KeyFile", "room_keys.json", "path to save room keys")
RedisAddr = flag.String("redis_addr", "", "redis addr to save room keys ex. localhost:6379")
RedisPwd = flag.String("redis_pwd", "", "redis password")
redisAddr = flag.String("redis_addr", "", "redis addr to save room keys ex. localhost:6379")
redisPwd = flag.String("redis_pwd", "", "redis password")
dashboard = flag.Bool("dashboard", false, "Enable dashboard ui")
)
type Application struct {
Appname string `json:"appname"`
Liveon string `json:"liveon"`
Hlson string `json:"hlson"`
StaticPush []string `json:"static_push"`
}
type JWTCfg struct {
Secret string `json:"secret"`
Algorithm string `json:"algorithm"`
}
type ServerCfg struct {
DashBoard bool `json:"dashboard"`
KeyFile string `json:"key_file"`
@ -80,22 +78,29 @@ func GetKeyFile() *string { @@ -80,22 +78,29 @@ func GetKeyFile() *string {
func GetRedisAddr() *string {
if len(RtmpServercfg.RedisAddr) > 0 {
*RedisAddr = RtmpServercfg.RedisAddr
*redisAddr = RtmpServercfg.RedisAddr
}
if len(*RedisAddr) == 0 {
if len(*redisAddr) == 0 {
return nil
}
return RedisAddr
return redisAddr
}
func ShowDashboard() bool {
if dashboard != nil && *dashboard == true {
return true
}
return RtmpServercfg.DashBoard
}
func GetRedisPwd() *string {
if len(RtmpServercfg.RedisPwd) > 0 {
*RedisPwd = RtmpServercfg.RedisPwd
*redisPwd = RtmpServercfg.RedisPwd
}
return RedisPwd
return redisPwd
}
func CheckAppName(appname string) bool {

2
go.mod

@ -5,9 +5,9 @@ go 1.13 @@ -5,9 +5,9 @@ go 1.13
require (
github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/elazarl/go-bindata-assetfs v1.0.0
github.com/go-redis/redis/v7 v7.2.0
github.com/gorilla/mux v1.7.4
github.com/markbates/pkger v0.15.1
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6
github.com/satori/go.uuid v1.2.0
github.com/smartystreets/goconvey v1.6.4 // indirect

11
go.sum

@ -2,14 +2,16 @@ github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b h1:CvoEHGm @@ -2,14 +2,16 @@ github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b h1:CvoEHGm
github.com/auth0/go-jwt-middleware v0.0.0-20190805220309-36081240882b/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-redis/redis/v7 v7.2.0 h1:CrCexy/jYWZjW0AyVoHlcJUeZN19VWlbepTh1Vq6dJs=
github.com/go-redis/redis/v7 v7.2.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg=
github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI=
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -26,6 +28,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN @@ -26,6 +28,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/markbates/pkger v0.15.1 h1:3MPelV53RnGSW07izx5xGxl4e/sdRD6zqseIk0rMASY=
github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -62,6 +66,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -62,6 +66,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
@ -72,3 +77,5 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -72,3 +77,5 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

4
protocol/dashboard/dashboard.go

@ -4,13 +4,13 @@ import ( @@ -4,13 +4,13 @@ import (
"log"
"net/http"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/gorilla/mux"
"github.com/markbates/pkger"
)
// DashboardHandler expose dashboard routes
type DashboardHandler struct {
Assets *assetfs.AssetFS
Assets *pkger.Dir
}
// Append add dashboard routes on a router

15
protocol/httpopera/http_opera.go

@ -9,13 +9,14 @@ import ( @@ -9,13 +9,14 @@ import (
"livego/av"
"livego/configure"
"livego/protocol/dashboard"
"livego/protocol/rtmp"
"livego/protocol/rtmp/rtmprelay"
jwtmiddleware "github.com/auth0/go-jwt-middleware"
"github.com/dgrijalva/jwt-go"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/gorilla/mux"
"github.com/markbates/pkger"
)
type Response struct {
@ -88,18 +89,22 @@ func JWTMiddleware(next http.Handler) http.Handler { @@ -88,18 +89,22 @@ func JWTMiddleware(next http.Handler) http.Handler {
return
}
next.ServeHTTP(w, r)
})
}
func (s *Server) Serve(l net.Listener) error {
router := mux.NewRouter()
if configure.RtmpServercfg.DashBoard {
DashboardHandler{Assets: &assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"}}.Append(router)
if configure.ShowDashboard() {
log.Printf("DASHBOARD On /dashboard")
dir := pkger.Dir("/static")
dashboard.DashboardHandler{Assets: &dir}.Append(router)
} else {
log.Printf("DASHBOARD Off")
}
router.Handle("/statics/", http.StripPrefix("/statics/", http.FileServer(http.Dir("statics"))))
// router.Handle("/statics/", http.StripPrefix("/statics/", http.FileServer(http.Dir("statics"))))
router.HandleFunc("/control/push", func(w http.ResponseWriter, r *http.Request) {
s.handlePush(w, r)

5
webui/.dockerignore

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
# compiled output
build/
# dependencies
/node_modules

2
webui/.gitignore vendored

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
/coverage
# production
/build
build/
# misc
.DS_Store

13
webui/Dockerfile

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
FROM node:13.12
ENV WEBUI_DIR /src/webui/
ENV STATIC_DIR /src/static/
RUN mkdir -p $WEBUI_DIR
RUN mkdir -p $STATIC_DIR
COPY package.json $WEBUI_DIR/
WORKDIR $WEBUI_DIR
RUN npm install
COPY . $WEBUI_DIR/

41
webui/package.json

@ -1,15 +1,8 @@ @@ -1,15 +1,8 @@
{
"name": "webui",
"version": "0.1.0",
"version": "0.0.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1"
},
"homepage": "/dashboard",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
@ -30,5 +23,35 @@ @@ -30,5 +23,35 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"dependencies": {
"@material-ui/core": "^4.2.1",
"@material-ui/icons": "^4.2.1",
"@material-ui/styles": "^4.2.1",
"chart.js": "^2.8.0",
"clsx": "^1.0.4",
"history": "^4.9.0",
"moment": "^2.24.0",
"node-sass": "^4.12.0",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-chartjs-2": "^2.7.6",
"react-dom": "^16.8.6",
"react-perfect-scrollbar": "^1.5.3",
"react-router-dom": "^5.0.1",
"react-scripts": "^3.0.1",
"recompose": "^0.30.0",
"underscore": "^1.9.1",
"uuid": "^3.3.2",
"validate.js": "^0.13.1"
},
"devDependencies": {
"eslint": "^6.6.0",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "^7.12.4",
"prettier": "^1.17.1",
"prettier-eslint": "^8.8.2",
"prettier-eslint-cli": "^4.7.1",
"typescript": "^3.5.1"
}
}

1
webui/public/_redirects

@ -0,0 +1 @@ @@ -0,0 +1 @@
/* /index.html 200

BIN
webui/public/favicon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
webui/public/images/auth.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 KiB

BIN
webui/public/images/avatars/avatar_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
webui/public/images/avatars/avatar_10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
webui/public/images/avatars/avatar_11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
webui/public/images/avatars/avatar_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

BIN
webui/public/images/avatars/avatar_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
webui/public/images/avatars/avatar_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
webui/public/images/avatars/avatar_5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

BIN
webui/public/images/avatars/avatar_6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
webui/public/images/avatars/avatar_7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
webui/public/images/avatars/avatar_8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
webui/public/images/avatars/avatar_9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

10
webui/public/images/logos/logo--white.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
webui/public/images/not_found.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
webui/public/images/products/product_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
webui/public/images/products/product_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
webui/public/images/products/product_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
webui/public/images/products/product_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
webui/public/images/products/product_5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
webui/public/images/products/product_6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

1
webui/public/images/undraw_page_not_found_su7k.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

1
webui/public/images/undraw_resume_folder_2_arse.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

41
webui/public/index.html

@ -1,43 +1,22 @@ @@ -1,43 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" dir="ltr">
<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" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="description"
content="Web site created using create-react-app"
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
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/
-->
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<link
href="https://fonts.googleapis.com/css?family=Roboto+Mono|Roboto+Slab|Roboto:300,400,500,700"
rel="stylesheet"
/>
<title>React Material Dashboard</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>

BIN
webui/public/logo192.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

BIN
webui/public/logo512.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

10
webui/public/manifest.json

@ -6,16 +6,6 @@ @@ -6,16 +6,6 @@
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",

3
webui/public/robots.txt

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

54
webui/readme.md

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
# LiveGo Web UI
Access to Livego Web UI, ex: http://localhost:8090/dashboard
## How to build (for backend developer)
Use the make file :
```shell
make build # Generate Docker image
make generate-webui # Generate static contents in `livego/static/` folder.
```
## How to build (only for frontend developer)
- prerequisite: [Node 12.11+](https://nodejs.org) [Npm](https://www.npmjs.com/)
- Go to the directory `webui`
- To install dependencies, execute the following commands:
- `npm install`
- Build static Web UI, execute the following command:
- `npm run build`
- Static contents are build in the directory `build`
**Don't change manually the files in the directory `build`**
- The build allow to:
- optimize all JavaScript
- optimize all CSS
- add vendor prefixes to CSS (cross-bowser support)
- add a hash in the file names to prevent browser cache problems
- all images will be optimized at build
- bundle JavaScript in one file
## How to edit (only for frontend developer)
**Don't change manually the files in the directory `build`**
- Go to the directory `webui`
- Edit files in `webui/src`
- Run in development mode :
- `npm run dev`
## Libraries
- [Node](https://nodejs.org)
- [Npm](https://www.npmjs.com/)
- [React](https://reactjs.org/)

38
webui/src/App.css

@ -1,38 +0,0 @@ @@ -1,38 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

58
webui/src/App.js

@ -1,26 +1,36 @@ @@ -1,26 +1,36 @@
import React from 'react';
import logo from './logo.svg';
import './App.css';
import React, { Component } from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
import { Chart } from 'react-chartjs-2';
import { ThemeProvider } from '@material-ui/styles';
import validate from 'validate.js';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
import { chartjs } from './helpers';
import theme from './theme';
import 'react-perfect-scrollbar/dist/css/styles.css';
import './assets/scss/index.scss';
import validators from './common/validators';
import Routes from './Routes';
const browserHistory = createBrowserHistory();
Chart.helpers.extend(Chart.elements.Rectangle.prototype, {
draw: chartjs.draw
});
export default App;
validate.validators = {
...validate.validators,
...validators
};
export default class App extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<Router history={browserHistory}>
<Routes />
</Router>
</ThemeProvider>
);
}
}

9
webui/src/App.test.js

@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});

93
webui/src/Routes.js

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
import React from 'react';
import { Switch, Redirect } from 'react-router-dom';
import { RouteWithLayout } from './components';
import { Main as MainLayout, Minimal as MinimalLayout } from './layouts';
import {
Dashboard as DashboardView,
ProductList as ProductListView,
UserList as UserListView,
Typography as TypographyView,
Icons as IconsView,
Account as AccountView,
Settings as SettingsView,
SignUp as SignUpView,
SignIn as SignInView,
NotFound as NotFoundView
} from './views';
const Routes = () => {
return (
<Switch>
<Redirect
exact
from="/"
to="/dashboard"
/>
<RouteWithLayout
component={DashboardView}
exact
layout={MainLayout}
path="/dashboard"
/>
<RouteWithLayout
component={UserListView}
exact
layout={MainLayout}
path="/users"
/>
<RouteWithLayout
component={ProductListView}
exact
layout={MainLayout}
path="/products"
/>
<RouteWithLayout
component={TypographyView}
exact
layout={MainLayout}
path="/typography"
/>
<RouteWithLayout
component={IconsView}
exact
layout={MainLayout}
path="/icons"
/>
<RouteWithLayout
component={AccountView}
exact
layout={MainLayout}
path="/account"
/>
<RouteWithLayout
component={SettingsView}
exact
layout={MainLayout}
path="/settings"
/>
<RouteWithLayout
component={SignUpView}
exact
layout={MinimalLayout}
path="/sign-up"
/>
<RouteWithLayout
component={SignInView}
exact
layout={MinimalLayout}
path="/sign-in"
/>
<RouteWithLayout
component={NotFoundView}
exact
layout={MinimalLayout}
path="/not-found"
/>
<Redirect to="/not-found" />
</Switch>
);
};
export default Routes;

24
webui/src/assets/scss/index.scss

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
height: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
background-color: #f4f6f8;
height: 100%;
}
a {
text-decoration: none;
}
#root {
height: 100%;
}

9
webui/src/common/validators.js

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
const checked = (value, options) => {
if (value !== true) {
return options.message || 'must be checked';
}
};
export default {
checked
};

26
webui/src/components/RouteWithLayout/RouteWithLayout.js

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
import React from 'react';
import { Route } from 'react-router-dom';
import PropTypes from 'prop-types';
const RouteWithLayout = props => {
const { layout: Layout, component: Component, ...rest } = props;
return (
<Route
{...rest}
render={matchProps => (
<Layout>
<Component {...matchProps} />
</Layout>
)}
/>
);
};
RouteWithLayout.propTypes = {
component: PropTypes.any.isRequired,
layout: PropTypes.any.isRequired,
path: PropTypes.string
};
export default RouteWithLayout;

1
webui/src/components/RouteWithLayout/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './RouteWithLayout';

56
webui/src/components/SearchInput/SearchInput.js

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/styles';
import { Paper, Input } from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search';
const useStyles = makeStyles(theme => ({
root: {
borderRadius: '4px',
alignItems: 'center',
padding: theme.spacing(1),
display: 'flex',
flexBasis: 420
},
icon: {
marginRight: theme.spacing(1),
color: theme.palette.text.secondary
},
input: {
flexGrow: 1,
fontSize: '14px',
lineHeight: '16px',
letterSpacing: '-0.05px'
}
}));
const SearchInput = props => {
const { className, onChange, style, ...rest } = props;
const classes = useStyles();
return (
<Paper
{...rest}
className={clsx(classes.root, className)}
style={style}
>
<SearchIcon className={classes.icon} />
<Input
{...rest}
className={classes.input}
disableUnderline
onChange={onChange}
/>
</Paper>
);
};
SearchInput.propTypes = {
className: PropTypes.string,
onChange: PropTypes.func,
style: PropTypes.object
};
export default SearchInput;

1
webui/src/components/SearchInput/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './SearchInput';

83
webui/src/components/StatusBullet/StatusBullet.js

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/styles';
const useStyles = makeStyles(theme => ({
root: {
display: 'inline-block',
borderRadius: '50%',
flexGrow: 0,
flexShrink: 0
},
sm: {
height: theme.spacing(1),
width: theme.spacing(1)
},
md: {
height: theme.spacing(2),
width: theme.spacing(2)
},
lg: {
height: theme.spacing(3),
width: theme.spacing(3)
},
neutral: {
backgroundColor: theme.palette.neutral
},
primary: {
backgroundColor: theme.palette.primary.main
},
info: {
backgroundColor: theme.palette.info.main
},
warning: {
backgroundColor: theme.palette.warning.main
},
danger: {
backgroundColor: theme.palette.error.main
},
success: {
backgroundColor: theme.palette.success.main
}
}));
const StatusBullet = props => {
const { className, size, color, ...rest } = props;
const classes = useStyles();
return (
<span
{...rest}
className={clsx(
{
[classes.root]: true,
[classes[size]]: size,
[classes[color]]: color
},
className
)}
/>
);
};
StatusBullet.propTypes = {
className: PropTypes.string,
color: PropTypes.oneOf([
'neutral',
'primary',
'info',
'success',
'warning',
'danger'
]),
size: PropTypes.oneOf(['sm', 'md', 'lg'])
};
StatusBullet.defaultProps = {
size: 'md',
color: 'default'
};
export default StatusBullet;

1
webui/src/components/StatusBullet/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './StatusBullet'

3
webui/src/components/index.js

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export { default as SearchInput } from './SearchInput';
export { default as StatusBullet } from './StatusBullet';
export { default as RouteWithLayout } from './RouteWithLayout';

192
webui/src/helpers/chartjs.js

@ -0,0 +1,192 @@ @@ -0,0 +1,192 @@
// ChartJS extension rounded bar chart
// https://codepen.io/jedtrow/full/ygRYgo
function draw() {
const { ctx } = this._chart;
const vm = this._view;
let { borderWidth } = vm;
let left;
let right;
let top;
let bottom;
let signX;
let signY;
let borderSkipped;
let radius;
// If radius is less than 0 or is large enough to cause drawing errors a max
// radius is imposed. If cornerRadius is not defined set it to 0.
let { cornerRadius } = this._chart.config.options;
if (cornerRadius < 0) {
cornerRadius = 0;
}
if (typeof cornerRadius === 'undefined') {
cornerRadius = 0;
}
if (!vm.horizontal) {
// bar
left = vm.x - vm.width / 2;
right = vm.x + vm.width / 2;
top = vm.y;
bottom = vm.base;
signX = 1;
signY = bottom > top ? 1 : -1;
borderSkipped = vm.borderSkipped || 'bottom';
} else {
// horizontal bar
left = vm.base;
right = vm.x;
top = vm.y - vm.height / 2;
bottom = vm.y + vm.height / 2;
signX = right > left ? 1 : -1;
signY = 1;
borderSkipped = vm.borderSkipped || 'left';
}
// Canvas doesn't allow us to stroke inside the width so we can
// adjust the sizes to fit if we're setting a stroke on the line
if (borderWidth) {
// borderWidth shold be less than bar width and bar height.
const barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
borderWidth = borderWidth > barSize ? barSize : borderWidth;
const halfStroke = borderWidth / 2;
// Adjust borderWidth when bar top position is near vm.base(zero).
const borderLeft =
left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
const borderRight =
right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
const borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
const borderBottom =
bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
// not become a vertical line?
if (borderLeft !== borderRight) {
top = borderTop;
bottom = borderBottom;
}
// not become a horizontal line?
if (borderTop !== borderBottom) {
left = borderLeft;
right = borderRight;
}
}
ctx.beginPath();
ctx.fillStyle = vm.backgroundColor;
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = borderWidth;
// Corner points, from bottom-left to bottom-right clockwise
// | 1 2 |
// | 0 3 |
const corners = [[left, bottom], [left, top], [right, top], [right, bottom]];
// Find first (starting) corner with fallback to 'bottom'
const borders = ['bottom', 'left', 'top', 'right'];
let startCorner = borders.indexOf(borderSkipped, 0);
if (startCorner === -1) {
startCorner = 0;
}
function cornerAt(index) {
return corners[(startCorner + index) % 4];
}
// Draw rectangle from 'startCorner'
let corner = cornerAt(0);
ctx.moveTo(corner[0], corner[1]);
for (let i = 1; i < 4; i += 1) {
corner = cornerAt(i);
let nextCornerId = i + 1;
if (nextCornerId === 4) {
nextCornerId = 0;
}
const width = corners[2][0] - corners[1][0];
const height = corners[0][1] - corners[1][1];
const x = corners[1][0];
const y = corners[1][1];
radius = cornerRadius;
// Fix radius being too large
if (radius > Math.abs(height) / 2) {
radius = Math.floor(Math.abs(height) / 2);
}
if (radius > Math.abs(width) / 2) {
radius = Math.floor(Math.abs(width) / 2);
}
if (height < 0) {
// Negative values in a standard bar chart
const xTl = x;
const xTr = x + width;
const yTl = y + height;
const yTr = y + height;
const xBl = x;
const xBr = x + width;
const yBl = y;
const yBr = y;
// Draw
ctx.moveTo(xBl + radius, yBl);
ctx.lineTo(xBr - radius, yBr);
ctx.quadraticCurveTo(xBr, yBr, xBr, yBr - radius);
ctx.lineTo(xTr, yTr + radius);
ctx.quadraticCurveTo(xTr, yTr, xTr - radius, yTr);
ctx.lineTo(xTl + radius, yTl);
ctx.quadraticCurveTo(xTl, yTl, xTl, yTl + radius);
ctx.lineTo(xBl, yBl - radius);
ctx.quadraticCurveTo(xBl, yBl, xBl + radius, yBl);
} else if (width < 0) {
// Negative values in a horizontal bar chart
const xTl = x + width;
const xTr = x;
const yTl = y;
const yTr = y;
const xBl = x + width;
const xBr = x;
const yBl = y + height;
const yBr = y + height;
// Draw
ctx.moveTo(xBl + radius, yBl);
ctx.lineTo(xBr - radius, yBr);
ctx.quadraticCurveTo(xBr, yBr, xBr, yBr - radius);
ctx.lineTo(xTr, yTr + radius);
ctx.quadraticCurveTo(xTr, yTr, xTr - radius, yTr);
ctx.lineTo(xTl + radius, yTl);
ctx.quadraticCurveTo(xTl, yTl, xTl, yTl + radius);
ctx.lineTo(xBl, yBl - radius);
ctx.quadraticCurveTo(xBl, yBl, xBl + radius, yBl);
} else {
// Positive Value
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(
x + width,
y + height,
x + width - radius,
y + height
);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
}
}
ctx.fill();
if (borderWidth) {
ctx.stroke();
}
}
export default {
draw
};

7
webui/src/helpers/getInitials.js

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
export default (name = '') =>
name
.replace(/\s+/, ' ')
.split(' ')
.slice(0, 2)
.map(v => v && v[0].toUpperCase())
.join('');

2
webui/src/helpers/index.js

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export { default as chartjs } from './chartjs';
export { default as getInitials } from './getInitials';

12
webui/src/icons/Facebook/index.js

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
import React from 'react';
// Material components
import { SvgIcon } from '@material-ui/core';
export default function Facebook(props) {
return (
<SvgIcon {...props}>
<path d="M9.53144612,22.005 L9.53144612,13.0552149 L6.44166667,13.0552149 L6.44166667,9.49875 L9.53144612,9.49875 L9.53144612,6.68484375 C9.53144612,5.19972656 9.95946769,4.04680661 10.8155103,3.22608401 C11.6715529,2.4053613 12.808485,1.995 14.2263057,1.995 C15.3766134,1.995 16.3129099,2.04710915 17.0351961,2.15132812 L17.0351961,5.3169726 L15.1090998,5.3169726 C14.3868137,5.3169726 13.8919142,5.47330073 13.6244006,5.78595698 C13.4103902,6.04650407 13.3033846,6.46337874 13.3033846,7.03658198 L13.3033846,9.49875 L16.71418,9.49875 L16.2326559,13.0552149 L13.3033846,13.0552149 L13.3033846,22.005 L9.53144612,22.005 Z" />
</SvgIcon>
);
}

12
webui/src/icons/Google/index.js

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
import React from 'react';
// Material components
import { SvgIcon } from '@material-ui/core';
export default function Google(props) {
return (
<SvgIcon {...props}>
<path d="M21,12.2177419 C21,13.9112905 20.6311475,15.4233869 19.8934426,16.7540323 C19.1557377,18.0846776 18.1168031,19.1249998 16.7766393,19.875 C15.4364756,20.6250002 13.8934424,21 12.147541,21 C10.4999998,21 8.97540984,20.5947579 7.57377049,19.7842742 C6.17213115,18.9737905 5.05942604,17.8790323 4.23565574,16.5 C3.41188543,15.1209677 3,13.6209679 3,12 C3,10.3790321 3.41188543,8.87903226 4.23565574,7.5 C5.05942604,6.12096774 6.17213115,5.02620949 7.57377049,4.21572581 C8.97540984,3.40524212 10.4999998,3 12.147541,3 C14.5327871,3 16.5737705,3.78629051 18.2704918,5.35887097 L15.7991803,7.71774194 C15.0122953,6.96774175 14.0655738,6.52016129 12.9590164,6.375 C11.9262295,6.22983871 10.9057375,6.375 9.89754098,6.81048387 C8.88934445,7.24596774 8.07786904,7.89919355 7.46311475,8.77016129 C6.79918033,9.71370968 6.46721311,10.7903228 6.46721311,12 C6.46721311,13.0403228 6.72540984,13.9899192 7.24180328,14.8487903 C7.75819672,15.7076615 8.4467215,16.3971776 9.30737705,16.9173387 C10.1680326,17.4374998 11.1147541,17.6975806 12.147541,17.6975806 C13.2540984,17.6975806 14.2254096,17.455645 15.0614754,16.9717742 C15.7254098,16.5846772 16.2786885,16.0645161 16.7213115,15.4112903 C17.0409838,14.8790321 17.2499998,14.3467744 17.3483607,13.8145161 L12.147541,13.8145161 L12.147541,10.6935484 L20.852459,10.6935484 C20.9508199,11.2258066 21,11.7338712 21,12.2177419 Z" />
</SvgIcon>
);
}

2
webui/src/icons/index.js

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export { default as Facebook } from './Facebook';
export { default as Google } from './Google';

13
webui/src/index.css

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

14
webui/src/index.js

@ -1,17 +1,9 @@ @@ -1,17 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

71
webui/src/layouts/Main/Main.js

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { makeStyles, useTheme } from '@material-ui/styles';
import { useMediaQuery } from '@material-ui/core';
import { Sidebar, Topbar, Footer } from './components';
const useStyles = makeStyles(theme => ({
root: {
paddingTop: 56,
height: '100%',
[theme.breakpoints.up('sm')]: {
paddingTop: 64
}
},
shiftContent: {
paddingLeft: 240
},
content: {
height: '100%'
}
}));
const Main = props => {
const { children } = props;
const classes = useStyles();
const theme = useTheme();
const isDesktop = useMediaQuery(theme.breakpoints.up('lg'), {
defaultMatches: true
});
const [openSidebar, setOpenSidebar] = useState(false);
const handleSidebarOpen = () => {
setOpenSidebar(true);
};
const handleSidebarClose = () => {
setOpenSidebar(false);
};
const shouldOpenSidebar = isDesktop ? true : openSidebar;
return (
<div
className={clsx({
[classes.root]: true,
[classes.shiftContent]: isDesktop
})}
>
<Topbar onSidebarOpen={handleSidebarOpen} />
<Sidebar
onClose={handleSidebarClose}
open={shouldOpenSidebar}
variant={isDesktop ? 'persistent' : 'temporary'}
/>
<main className={classes.content}>
{children}
<Footer />
</main>
</div>
);
};
Main.propTypes = {
children: PropTypes.node
};
export default Main;

46
webui/src/layouts/Main/components/Footer/Footer.js

@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/styles';
import { Typography, Link } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(4)
}
}));
const Footer = props => {
const { className, ...rest } = props;
const classes = useStyles();
return (
<div
{...rest}
className={clsx(classes.root, className)}
>
<Typography variant="body1">
&copy;{' '}
<Link
component="a"
href="https://devias.io/"
target="_blank"
>
Devias IO
</Link>
. 2019
</Typography>
<Typography variant="caption">
Created with love for the environment. By designers and developers who
love to work together in offices!
</Typography>
</div>
);
};
Footer.propTypes = {
className: PropTypes.string
};
export default Footer;

1
webui/src/layouts/Main/components/Footer/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Footer';

119
webui/src/layouts/Main/components/Sidebar/Sidebar.js

@ -0,0 +1,119 @@ @@ -0,0 +1,119 @@
import React from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { Divider, Drawer } from '@material-ui/core';
import DashboardIcon from '@material-ui/icons/Dashboard';
import PeopleIcon from '@material-ui/icons/People';
import ShoppingBasketIcon from '@material-ui/icons/ShoppingBasket';
import TextFieldsIcon from '@material-ui/icons/TextFields';
import ImageIcon from '@material-ui/icons/Image';
import AccountBoxIcon from '@material-ui/icons/AccountBox';
import SettingsIcon from '@material-ui/icons/Settings';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import { Profile, SidebarNav, UpgradePlan } from './components';
const useStyles = makeStyles(theme => ({
drawer: {
width: 240,
[theme.breakpoints.up('lg')]: {
marginTop: 64,
height: 'calc(100% - 64px)'
}
},
root: {
backgroundColor: theme.palette.white,
display: 'flex',
flexDirection: 'column',
height: '100%',
padding: theme.spacing(2)
},
divider: {
margin: theme.spacing(2, 0)
},
nav: {
marginBottom: theme.spacing(2)
}
}));
const Sidebar = props => {
const { open, variant, onClose, className, ...rest } = props;
const classes = useStyles();
const pages = [
{
title: 'Dashboard',
href: '/dashboard',
icon: <DashboardIcon />
},
{
title: 'Users',
href: '/users',
icon: <PeopleIcon />
},
{
title: 'Products',
href: '/products',
icon: <ShoppingBasketIcon />
},
{
title: 'Authentication',
href: '/sign-in',
icon: <LockOpenIcon />
},
{
title: 'Typography',
href: '/typography',
icon: <TextFieldsIcon />
},
{
title: 'Icons',
href: '/icons',
icon: <ImageIcon />
},
{
title: 'Account',
href: '/account',
icon: <AccountBoxIcon />
},
{
title: 'Settings',
href: '/settings',
icon: <SettingsIcon />
}
];
return (
<Drawer
anchor="left"
classes={{ paper: classes.drawer }}
onClose={onClose}
open={open}
variant={variant}
>
<div
{...rest}
className={clsx(classes.root, className)}
>
<Profile />
<Divider className={classes.divider} />
<SidebarNav
className={classes.nav}
pages={pages}
/>
<UpgradePlan />
</div>
</Drawer>
);
};
Sidebar.propTypes = {
className: PropTypes.string,
onClose: PropTypes.func,
open: PropTypes.bool.isRequired,
variant: PropTypes.string.isRequired
};
export default Sidebar;

62
webui/src/layouts/Main/components/Sidebar/components/Profile/Profile.js

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { Avatar, Typography } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
minHeight: 'fit-content'
},
avatar: {
width: 60,
height: 60
},
name: {
marginTop: theme.spacing(1)
}
}));
const Profile = props => {
const { className, ...rest } = props;
const classes = useStyles();
const user = {
name: 'Shen Zhi',
avatar: '/images/avatars/avatar_11.png',
bio: 'Brain Director'
};
return (
<div
{...rest}
className={clsx(classes.root, className)}
>
<Avatar
alt="Person"
className={classes.avatar}
component={RouterLink}
src={user.avatar}
to="/settings"
/>
<Typography
className={classes.name}
variant="h4"
>
{user.name}
</Typography>
<Typography variant="body2">{user.bio}</Typography>
</div>
);
};
Profile.propTypes = {
className: PropTypes.string
};
export default Profile;

1
webui/src/layouts/Main/components/Sidebar/components/Profile/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Profile';

88
webui/src/layouts/Main/components/Sidebar/components/SidebarNav/SidebarNav.js

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
/* eslint-disable react/no-multi-comp */
/* eslint-disable react/display-name */
import React, { forwardRef } from 'react';
import { NavLink as RouterLink } from 'react-router-dom';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { List, ListItem, Button, colors } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
root: {},
item: {
display: 'flex',
paddingTop: 0,
paddingBottom: 0
},
button: {
color: colors.blueGrey[800],
padding: '10px 8px',
justifyContent: 'flex-start',
textTransform: 'none',
letterSpacing: 0,
width: '100%',
fontWeight: theme.typography.fontWeightMedium
},
icon: {
color: theme.palette.icon,
width: 24,
height: 24,
display: 'flex',
alignItems: 'center',
marginRight: theme.spacing(1)
},
active: {
color: theme.palette.primary.main,
fontWeight: theme.typography.fontWeightMedium,
'& $icon': {
color: theme.palette.primary.main
}
}
}));
const CustomRouterLink = forwardRef((props, ref) => (
<div
ref={ref}
style={{ flexGrow: 1 }}
>
<RouterLink {...props} />
</div>
));
const SidebarNav = props => {
const { pages, className, ...rest } = props;
const classes = useStyles();
return (
<List
{...rest}
className={clsx(classes.root, className)}
>
{pages.map(page => (
<ListItem
className={classes.item}
disableGutters
key={page.title}
>
<Button
activeClassName={classes.active}
className={classes.button}
component={CustomRouterLink}
to={page.href}
>
<div className={classes.icon}>{page.icon}</div>
{page.title}
</Button>
</ListItem>
))}
</List>
);
};
SidebarNav.propTypes = {
className: PropTypes.string,
pages: PropTypes.array.isRequired
};
export default SidebarNav;

1
webui/src/layouts/Main/components/Sidebar/components/SidebarNav/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './SidebarNav';

79
webui/src/layouts/Main/components/Sidebar/components/UpgradePlan/UpgradePlan.js

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
import React from 'react';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { Typography, Button, colors } from '@material-ui/core';
const useStyles = makeStyles(theme => ({
root: {
backgroundColor: colors.grey[50]
},
media: {
paddingTop: theme.spacing(2),
height: 80,
textAlign: 'center',
'& > img': {
height: '100%',
width: 'auto'
}
},
content: {
padding: theme.spacing(1, 2)
},
actions: {
padding: theme.spacing(1, 2),
display: 'flex',
justifyContent: 'center'
}
}));
const UpgradePlan = props => {
const { className, ...rest } = props;
const classes = useStyles();
return (
<div
{...rest}
className={clsx(classes.root, className)}
>
<div className={classes.media}>
<img
alt="Upgrade to PRO"
src="/images/undraw_resume_folder_2_arse.svg"
/>
</div>
<div className={classes.content}>
<Typography
align="center"
gutterBottom
variant="h6"
>
Upgrade to PRO
</Typography>
<Typography
align="center"
variant="body2"
>
Upgrade to Devias Kit PRO and get even more components
</Typography>
</div>
<div className={classes.actions}>
<Button
color="primary"
component="a"
href="https://devias.io/products/devias-kit-pro"
variant="contained"
>
Upgrade
</Button>
</div>
</div>
);
};
UpgradePlan.propTypes = {
className: PropTypes.string
};
export default UpgradePlan;

1
webui/src/layouts/Main/components/Sidebar/components/UpgradePlan/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './UpgradePlan';

3
webui/src/layouts/Main/components/Sidebar/components/index.js

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export { default as Profile } from './Profile';
export { default as SidebarNav } from './SidebarNav';
export { default as UpgradePlan } from './UpgradePlan';

1
webui/src/layouts/Main/components/Sidebar/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Sidebar';

78
webui/src/layouts/Main/components/Topbar/Topbar.js

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
import React, { useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { AppBar, Toolbar, Badge, Hidden, IconButton } from '@material-ui/core';
import MenuIcon from '@material-ui/icons/Menu';
import NotificationsIcon from '@material-ui/icons/NotificationsOutlined';
import InputIcon from '@material-ui/icons/Input';
const useStyles = makeStyles(theme => ({
root: {
boxShadow: 'none'
},
flexGrow: {
flexGrow: 1
},
signOutButton: {
marginLeft: theme.spacing(1)
}
}));
const Topbar = props => {
const { className, onSidebarOpen, ...rest } = props;
const classes = useStyles();
const [notifications] = useState([]);
return (
<AppBar
{...rest}
className={clsx(classes.root, className)}
>
<Toolbar>
<RouterLink to="/">
<img
alt="Logo"
src="/images/logos/logo--white.svg"
/>
</RouterLink>
<div className={classes.flexGrow} />
<Hidden mdDown>
<IconButton color="inherit">
<Badge
badgeContent={notifications.length}
color="primary"
variant="dot"
>
<NotificationsIcon />
</Badge>
</IconButton>
<IconButton
className={classes.signOutButton}
color="inherit"
>
<InputIcon />
</IconButton>
</Hidden>
<Hidden lgUp>
<IconButton
color="inherit"
onClick={onSidebarOpen}
>
<MenuIcon />
</IconButton>
</Hidden>
</Toolbar>
</AppBar>
);
};
Topbar.propTypes = {
className: PropTypes.string,
onSidebarOpen: PropTypes.func
};
export default Topbar;

1
webui/src/layouts/Main/components/Topbar/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Topbar';

3
webui/src/layouts/Main/components/index.js

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
export { default as Footer } from './Footer';
export { default as Sidebar } from './Sidebar';
export { default as Topbar } from './Topbar';

1
webui/src/layouts/Main/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Main';

35
webui/src/layouts/Minimal/Minimal.js

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { Topbar } from './components';
const useStyles = makeStyles(() => ({
root: {
paddingTop: 64,
height: '100%'
},
content: {
height: '100%'
}
}));
const Minimal = props => {
const { children } = props;
const classes = useStyles();
return (
<div className={classes.root}>
<Topbar />
<main className={classes.content}>{children}</main>
</div>
);
};
Minimal.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};
export default Minimal;

42
webui/src/layouts/Minimal/components/Topbar/Topbar.js

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import clsx from 'clsx';
import PropTypes from 'prop-types';
import { makeStyles } from '@material-ui/styles';
import { AppBar, Toolbar } from '@material-ui/core';
const useStyles = makeStyles(() => ({
root: {
boxShadow: 'none'
}
}));
const Topbar = props => {
const { className, ...rest } = props;
const classes = useStyles();
return (
<AppBar
{...rest}
className={clsx(classes.root, className)}
color="primary"
position="fixed"
>
<Toolbar>
<RouterLink to="/">
<img
alt="Logo"
src="/images/logos/logo--white.svg"
/>
</RouterLink>
</Toolbar>
</AppBar>
);
};
Topbar.propTypes = {
className: PropTypes.string
};
export default Topbar;

1
webui/src/layouts/Minimal/components/Topbar/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Topbar';

1
webui/src/layouts/Minimal/components/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default as Topbar } from './Topbar';

1
webui/src/layouts/Minimal/index.js

@ -0,0 +1 @@ @@ -0,0 +1 @@
export { default } from './Minimal';

2
webui/src/layouts/index.js

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
export { default as Main } from './Main';
export { default as Minimal } from './Minimal';

7
webui/src/logo.svg

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

22
webui/src/serviceWorker.js

@ -8,13 +8,13 @@ @@ -8,13 +8,13 @@
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
// opt-in, read http://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
@ -43,7 +43,7 @@ export function register(config) { @@ -43,7 +43,7 @@ export function register(config) {
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
'worker. To learn more, visit http://bit.ly/CRA-PWA'
);
});
} else {
@ -71,7 +71,7 @@ function registerValidSW(swUrl, config) { @@ -71,7 +71,7 @@ function registerValidSW(swUrl, config) {
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
'tabs for this page are closed. See http://bit.ly/CRA-PWA.'
);
// Execute callback
@ -100,9 +100,7 @@ function registerValidSW(swUrl, config) { @@ -100,9 +100,7 @@ function registerValidSW(swUrl, config) {
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' },
})
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
@ -130,12 +128,8 @@ function checkValidServiceWorker(swUrl, config) { @@ -130,12 +128,8 @@ function checkValidServiceWorker(swUrl, config) {
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready
.then(registration => {
registration.unregister();
})
.catch(error => {
console.error(error.message);
});
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}

5
webui/src/setupTests.js

@ -1,5 +0,0 @@ @@ -1,5 +0,0 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';

17
webui/src/theme/index.js

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
import { createMuiTheme } from '@material-ui/core';
import palette from './palette';
import typography from './typography';
import overrides from './overrides';
const theme = createMuiTheme({
palette,
typography,
overrides,
zIndex: {
appBar: 1200,
drawer: 1100
}
});
export default theme;

7
webui/src/theme/overrides/MuiButton.js

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
export default {
contained: {
boxShadow:
'0 1px 1px 0 rgba(0,0,0,0.14), 0 2px 1px -1px rgba(0,0,0,0.12), 0 1px 3px 0 rgba(0,0,0,0.20)',
backgroundColor: '#FFFFFF'
}
};

10
webui/src/theme/overrides/MuiIconButton.js

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
import palette from '../palette';
export default {
root: {
color: palette.icon,
'&:hover': {
backgroundColor: 'rgba(0, 0, 0, 0.03)'
}
}
};

5
webui/src/theme/overrides/MuiPaper.js

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export default {
elevation1: {
boxShadow: '0 0 0 1px rgba(63,63,68,0.05), 0 1px 3px 0 rgba(63,63,68,0.15)'
}
};

9
webui/src/theme/overrides/MuiTableCell.js

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
import palette from '../palette';
import typography from '../typography';
export default {
root: {
...typography.body1,
borderBottom: `1px solid ${palette.divider}`
}
};

7
webui/src/theme/overrides/MuiTableHead.js

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
import { colors } from '@material-ui/core';
export default {
root: {
backgroundColor: colors.grey[50]
}
};

14
webui/src/theme/overrides/MuiTableRow.js

@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
import palette from '../palette';
export default {
root: {
'&$selected': {
backgroundColor: palette.background.default
},
'&$hover': {
'&:hover': {
backgroundColor: palette.background.default
}
}
}
};

5
webui/src/theme/overrides/MuiTypography.js

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export default {
gutterBottom: {
marginBottom: 8
}
};

15
webui/src/theme/overrides/index.js

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
import MuiButton from './MuiButton';
import MuiIconButton from './MuiIconButton';
import MuiPaper from './MuiPaper';
import MuiTableCell from './MuiTableCell';
import MuiTableHead from './MuiTableHead';
import MuiTypography from './MuiTypography';
export default {
MuiButton,
MuiIconButton,
MuiPaper,
MuiTableCell,
MuiTableHead,
MuiTypography
};

56
webui/src/theme/palette.js

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
import { colors } from '@material-ui/core';
const white = '#FFFFFF';
const black = '#000000';
export default {
black,
white,
primary: {
contrastText: white,
dark: colors.indigo[900],
main: colors.indigo[500],
light: colors.indigo[100]
},
secondary: {
contrastText: white,
dark: colors.blue[900],
main: colors.blue['A400'],
light: colors.blue['A400']
},
success: {
contrastText: white,
dark: colors.green[900],
main: colors.green[600],
light: colors.green[400]
},
info: {
contrastText: white,
dark: colors.blue[900],
main: colors.blue[600],
light: colors.blue[400]
},
warning: {
contrastText: white,
dark: colors.orange[900],
main: colors.orange[600],
light: colors.orange[400]
},
error: {
contrastText: white,
dark: colors.red[900],
main: colors.red[600],
light: colors.red[400]
},
text: {
primary: colors.blueGrey[900],
secondary: colors.blueGrey[600],
link: colors.blue[600]
},
background: {
default: '#F4F6F8',
paper: white
},
icon: colors.blueGrey[600],
divider: colors.grey[200]
};

89
webui/src/theme/typography.js

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
import palette from './palette';
export default {
h1: {
color: palette.text.primary,
fontWeight: 500,
fontSize: '35px',
letterSpacing: '-0.24px',
lineHeight: '40px'
},
h2: {
color: palette.text.primary,
fontWeight: 500,
fontSize: '29px',
letterSpacing: '-0.24px',
lineHeight: '32px'
},
h3: {
color: palette.text.primary,
fontWeight: 500,
fontSize: '24px',
letterSpacing: '-0.06px',
lineHeight: '28px'
},
h4: {
color: palette.text.primary,
fontWeight: 500,
fontSize: '20px',
letterSpacing: '-0.06px',
lineHeight: '24px'
},
h5: {
color: palette.text.primary,
fontWeight: 500,
fontSize: '16px',
letterSpacing: '-0.05px',
lineHeight: '20px'
},
h6: {
color: palette.text.primary,
fontWeight: 500,
fontSize: '14px',
letterSpacing: '-0.05px',
lineHeight: '20px'
},
subtitle1: {
color: palette.text.primary,
fontSize: '16px',
letterSpacing: '-0.05px',
lineHeight: '25px'
},
subtitle2: {
color: palette.text.secondary,
fontWeight: 400,
fontSize: '14px',
letterSpacing: '-0.05px',
lineHeight: '21px'
},
body1: {
color: palette.text.primary,
fontSize: '14px',
letterSpacing: '-0.05px',
lineHeight: '21px'
},
body2: {
color: palette.text.secondary,
fontSize: '12px',
letterSpacing: '-0.04px',
lineHeight: '18px'
},
button: {
color: palette.text.primary,
fontSize: '14px'
},
caption: {
color: palette.text.secondary,
fontSize: '11px',
letterSpacing: '0.33px',
lineHeight: '13px'
},
overline: {
color: palette.text.secondary,
fontSize: '11px',
fontWeight: 500,
letterSpacing: '0.33px',
lineHeight: '13px',
textTransform: 'uppercase'
}
};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save