Merge pull request #42 from leandrofars/feature/integracao-backend

Feature/integracao backend
This commit is contained in:
Leandro Antônio Farias Machado 2023-05-14 19:22:50 -03:00 committed by GitHub
commit 3953a0d09f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1125 additions and 2610 deletions

View File

@ -0,0 +1 @@
SECRET_API_KEY=""

View File

@ -0,0 +1 @@
/.env.local

View File

@ -5,6 +5,7 @@ package main
import ( import (
"context" "context"
"flag" "flag"
"github.com/joho/godotenv"
"github.com/leandrofars/oktopus/internal/api" "github.com/leandrofars/oktopus/internal/api"
"github.com/leandrofars/oktopus/internal/db" "github.com/leandrofars/oktopus/internal/db"
usp_msg "github.com/leandrofars/oktopus/internal/usp_message" usp_msg "github.com/leandrofars/oktopus/internal/usp_message"
@ -23,6 +24,20 @@ const VERSION = "0.0.1"
func main() { func main() {
done := make(chan os.Signal, 1) done := make(chan os.Signal, 1)
err := godotenv.Load()
localEnv := ".env.local"
if _, err := os.Stat(localEnv); err == nil {
_ = godotenv.Overload(localEnv)
log.Println("Loaded variables from '.env.local'")
} else {
log.Println("Loaded variables from '.env'")
}
if err != nil {
log.Println("Error to load environment variables:", err)
}
// Locks app running until it receives a stop command as Ctrl+C. // Locks app running until it receives a stop command as Ctrl+C.
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)

View File

@ -14,9 +14,11 @@ require (
github.com/golang/snappy v0.0.1 // indirect github.com/golang/snappy v0.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/mux v1.8.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/compress v1.13.6 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect

View File

@ -16,6 +16,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -29,6 +31,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/leandrofars/oktopus/internal/api/auth" "github.com/leandrofars/oktopus/internal/api/auth"
"github.com/leandrofars/oktopus/internal/api/cors"
"github.com/leandrofars/oktopus/internal/api/middleware" "github.com/leandrofars/oktopus/internal/api/middleware"
"github.com/leandrofars/oktopus/internal/db" "github.com/leandrofars/oktopus/internal/db"
"github.com/leandrofars/oktopus/internal/mtp" "github.com/leandrofars/oktopus/internal/mtp"
@ -41,7 +42,7 @@ func StartApi(a Api) {
authentication.HandleFunc("/login", a.generateToken).Methods("PUT") authentication.HandleFunc("/login", a.generateToken).Methods("PUT")
//authentication.HandleFunc("/register", a.registerUser).Methods("POST") //authentication.HandleFunc("/register", a.registerUser).Methods("POST")
iot := r.PathPrefix("/device").Subrouter() iot := r.PathPrefix("/device").Subrouter()
iot.HandleFunc("/", a.retrieveDevices).Methods("GET") iot.HandleFunc("", a.retrieveDevices).Methods("GET")
iot.HandleFunc("/{sn}/get", a.deviceGetMsg).Methods("PUT") iot.HandleFunc("/{sn}/get", a.deviceGetMsg).Methods("PUT")
iot.HandleFunc("/{sn}/add", a.deviceCreateMsg).Methods("PUT") iot.HandleFunc("/{sn}/add", a.deviceCreateMsg).Methods("PUT")
iot.HandleFunc("/{sn}/del", a.deviceDeleteMsg).Methods("PUT") iot.HandleFunc("/{sn}/del", a.deviceDeleteMsg).Methods("PUT")
@ -49,17 +50,21 @@ func StartApi(a Api) {
//TODO: Create operation action handler //TODO: Create operation action handler
iot.HandleFunc("/device/{sn}/act", a.deviceUpdateMsg).Methods("PUT") iot.HandleFunc("/device/{sn}/act", a.deviceUpdateMsg).Methods("PUT")
// Middleware for requests which requires user to be authenticated
iot.Use(func(handler http.Handler) http.Handler { iot.Use(func(handler http.Handler) http.Handler {
return middleware.Middleware(handler) return middleware.Middleware(handler)
}) })
// Verifies CORS configs for requests
corsOpts := cors.GetCorsConfig()
srv := &http.Server{ srv := &http.Server{
Addr: "0.0.0.0:" + a.Port, Addr: "0.0.0.0:" + a.Port,
// Good practice to set timeouts to avoid Slowloris attacks. // Good practice to set timeouts to avoid Slowloris attacks.
WriteTimeout: time.Second * 15, WriteTimeout: time.Second * 15,
ReadTimeout: time.Second * 15, ReadTimeout: time.Second * 15,
IdleTimeout: time.Second * 60, IdleTimeout: time.Second * 60,
Handler: r, // Pass our instance of gorilla/mux in. Handler: corsOpts.Handler(r), // Pass our instance of gorilla/mux in.
} }
// Run our server in a goroutine so that it doesn't block. // Run our server in a goroutine so that it doesn't block.
@ -68,6 +73,7 @@ func StartApi(a Api) {
log.Println(err) log.Println(err)
} }
}() }()
log.Println("Running Api at port", a.Port)
} }
func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) { func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
@ -92,7 +98,7 @@ func (a *Api) deviceCreateMsg(w http.ResponseWriter, r *http.Request) {
var receiver usp_msg.Add var receiver usp_msg.Add
err := json.NewDecoder(r.Body).Decode(receiver) err := json.NewDecoder(r.Body).Decode(&receiver)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)

View File

@ -3,10 +3,17 @@ package auth
import ( import (
"errors" "errors"
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"os"
"time" "time"
) )
var jwtKey = []byte("supersecretkey") func getJwtKey() []byte {
jwtKey, ok := os.LookupEnv("SECRET_API_KEY")
if !ok || jwtKey == "" {
return []byte("supersecretkey")
}
return []byte(jwtKey)
}
type JWTClaim struct { type JWTClaim struct {
Username string `json:"username"` Username string `json:"username"`
@ -15,7 +22,7 @@ type JWTClaim struct {
} }
func GenerateJWT(email string, username string) (tokenString string, err error) { func GenerateJWT(email string, username string) (tokenString string, err error) {
expirationTime := time.Now().Add(1 * time.Hour) expirationTime := time.Now().Add(4 * time.Hour)
claims := &JWTClaim{ claims := &JWTClaim{
Email: email, Email: email,
Username: username, Username: username,
@ -24,15 +31,16 @@ func GenerateJWT(email string, username string) (tokenString string, err error)
}, },
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err = token.SignedString(jwtKey) tokenString, err = token.SignedString(getJwtKey())
return return
} }
func ValidateToken(signedToken string) (email string, err error) { func ValidateToken(signedToken string) (email string, err error) {
token, err := jwt.ParseWithClaims( token, err := jwt.ParseWithClaims(
signedToken, signedToken,
&JWTClaim{}, &JWTClaim{},
func(token *jwt.Token) (interface{}, error) { func(token *jwt.Token) (interface{}, error) {
return []byte(jwtKey), nil return getJwtKey(), nil
}, },
) )
if err != nil { if err != nil {

View File

@ -0,0 +1,27 @@
package cors
import (
"github.com/rs/cors"
"net/http"
)
func GetCorsConfig() cors.Cors {
return *cors.New(cors.Options{
AllowedOrigins: []string{
"http://localhost:3000",
},
AllowedMethods: []string{
http.MethodGet,
http.MethodPost,
http.MethodPut,
http.MethodPatch,
http.MethodDelete,
http.MethodOptions,
http.MethodHead,
},
AllowedHeaders: []string{
"*", //or you can your header key values which you are using in your application
},
})
}

View File

@ -1,3 +1,5 @@
This broker is an implementation of mochi. I had to fork it to customize CONNACK packet userProperty. This broker is an implementation of mochi. I've to forked it to customize CONNACK packet userProperty, although mochi lib might have a better approach to do it.
To run this project you might have Go compiler in your machine, and inside cmd folder there is a run.sh script, which runs the project with the right arguments; also inside the same folder is the auth.json file, that carries configs of RBAC.
![img.png](img.png) ![img.png](img.png)

View File

@ -0,0 +1 @@
go run . -path auth.json

2
frontend/.gitignore vendored
View File

@ -17,7 +17,7 @@ out
.DS_Store .DS_Store
.eslintcache .eslintcache
.idea .idea
/.env .env
/.env.local /.env.local
/.env.development.local /.env.development.local
/.env.test.local /.env.test.local

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -83,9 +83,9 @@ export const AuthProvider = (props) => {
if (isAuthenticated) { if (isAuthenticated) {
const user = { const user = {
id: '5e86809283e28b96d2d38537', id: '5e86809283e28b96d2d38537',
avatar: '/assets/avatars/avatar-anika-visser.png', avatar: '/assets/avatars/default-avatar.png',
name: 'Anika Visser', name: 'Anika Visser',
email: 'anika.visser@devias.io' email: 'anika.visser@devias.io',
}; };
dispatch({ dispatch({
@ -116,9 +116,9 @@ export const AuthProvider = (props) => {
const user = { const user = {
id: '5e86809283e28b96d2d38537', id: '5e86809283e28b96d2d38537',
avatar: '/assets/avatars/avatar-anika-visser.png', avatar: '/assets/avatars/default-avatar.png',
name: 'Anika Visser', name: 'Anika Visser',
email: 'anika.visser@devias.io' email: 'anika.visser@devias.io',
}; };
dispatch({ dispatch({
@ -128,10 +128,31 @@ export const AuthProvider = (props) => {
}; };
const signIn = async (email, password) => { const signIn = async (email, password) => {
if (email !== 'demo@oktopus.io' || password !== 'Password123!') {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"email": email,
"password": password
});
var requestOptions = {
method: 'PUT',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
let result = await fetch(process.env.NEXT_PUBLIC_REST_ENPOINT+"/auth/login", requestOptions)
if (result.status != 200) {
throw new Error('Please check your email and password'); throw new Error('Please check your email and password');
} }
const token = await result.json()
try { try {
window.sessionStorage.setItem('authenticated', 'true'); window.sessionStorage.setItem('authenticated', 'true');
} catch (err) { } catch (err) {
@ -140,11 +161,14 @@ export const AuthProvider = (props) => {
const user = { const user = {
id: '5e86809283e28b96d2d38537', id: '5e86809283e28b96d2d38537',
avatar: '/assets/avatars/avatar-anika-visser.png', avatar: '/assets/avatars/default-avatar.png',
name: 'Anika Visser', name: 'Anika Visser',
email: 'anika.visser@devias.io' email: 'anika.visser@devias.io',
token: token
}; };
localStorage.setItem("token", token)
dispatch({ dispatch({
type: HANDLERS.SIGN_IN, type: HANDLERS.SIGN_IN,
payload: user payload: user
@ -168,7 +192,7 @@ export const AuthProvider = (props) => {
skip, skip,
signIn, signIn,
signUp, signUp,
signOut signOut,
}} }}
> >
{children} {children}

View File

@ -9,6 +9,7 @@ export const AccountPopover = (props) => {
const router = useRouter(); const router = useRouter();
const auth = useAuth(); const auth = useAuth();
const handleSignOut = useCallback( const handleSignOut = useCallback(
() => { () => {
onClose?.(); onClose?.();
@ -42,7 +43,7 @@ export const AccountPopover = (props) => {
color="text.secondary" color="text.secondary"
variant="body2" variant="body2"
> >
Anika Visser {auth.user.name}
</Typography> </Typography>
</Box> </Box>
<Divider /> <Divider />

View File

@ -16,6 +16,7 @@ import {
import { alpha } from '@mui/material/styles'; import { alpha } from '@mui/material/styles';
import { usePopover } from 'src/hooks/use-popover'; import { usePopover } from 'src/hooks/use-popover';
import { AccountPopover } from './account-popover'; import { AccountPopover } from './account-popover';
import { useAuth } from 'src/hooks/use-auth';
const SIDE_NAV_WIDTH = 280; const SIDE_NAV_WIDTH = 280;
const TOP_NAV_HEIGHT = 64; const TOP_NAV_HEIGHT = 64;
@ -24,8 +25,9 @@ export const TopNav = (props) => {
const { onNavOpen } = props; const { onNavOpen } = props;
const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg')); const lgUp = useMediaQuery((theme) => theme.breakpoints.up('lg'));
const accountPopover = usePopover(); const accountPopover = usePopover();
const auth = useAuth();
return ( return ( auth.user &&
<> <>
<Box <Box
component="header" component="header"
@ -106,7 +108,7 @@ export const TopNav = (props) => {
height: 40, height: 40,
width: 40 width: 40
}} }}
src="/assets/avatars/avatar-anika-visser.png" src={auth.user.avatar}
/> />
</Stack> </Stack>
</Stack> </Stack>

View File

@ -25,8 +25,8 @@ const Page = () => {
const [method, setMethod] = useState('email'); const [method, setMethod] = useState('email');
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
email: 'demo@oktopus.io', email: '',
password: 'Password123!', password: '',
submit: null submit: null
}, },
validationSchema: Yup.object({ validationSchema: Yup.object({
@ -99,7 +99,7 @@ const Page = () => {
<Typography variant="h4"> <Typography variant="h4">
Login Login
</Typography> </Typography>
<Typography {/* <Typography
color="text.secondary" color="text.secondary"
variant="body2" variant="body2"
> >
@ -113,7 +113,7 @@ const Page = () => {
> >
Register Register
</Link> </Link>
</Typography> </Typography> */}
</Stack> </Stack>
{/*<Tabs {/*<Tabs
onChange={handleMethodChange} onChange={handleMethodChange}
@ -158,9 +158,9 @@ const Page = () => {
value={formik.values.password} value={formik.values.password}
/> />
</Stack> </Stack>
<FormHelperText sx={{ mt: 1 }}> {/* <FormHelperText sx={{ mt: 1 }}>
Optionally you can skip. Optionally you can skip.
</FormHelperText> </FormHelperText> */}
{formik.errors.submit && ( {formik.errors.submit && (
<Typography <Typography
color="error" color="error"
@ -179,21 +179,25 @@ const Page = () => {
> >
Continue Continue
</Button> </Button>
<Button {/* <Button
fullWidth fullWidth
size="large" size="large"
sx={{ mt: 3 }} sx={{ mt: 3 }}
onClick={handleSkip} onClick={handleSkip}
> >
Skip authentication Skip authentication
</Button> </Button> */}
<Alert <Alert
color="primary" color="primary"
severity="info" severity="info"
sx={{ mt: 3 }} sx={{ mt: 3 }}
> >
<div> <div>
You can use <b>demo@oktopus.io</b> and password <b>Password123!</b> Don't have an account? ask one for us at <Link
href="https://github.com/leandrofars/oktopus"
underline="hover">
Github
</Link>
</div> </div>
</Alert> </Alert>
</form> </form>

View File

@ -1,108 +1,67 @@
import React, { useState, useEffect } from 'react';
import Head from 'next/head'; import Head from 'next/head';
import { subDays, subHours } from 'date-fns';
import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material'; import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material';
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout'; import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
import { OverviewBudget } from 'src/sections/overview/overview-budget';
import { OverviewLatestOrders } from 'src/sections/overview/overview-latest-orders'; import { OverviewLatestOrders } from 'src/sections/overview/overview-latest-orders';
import { OverviewLatestProducts } from 'src/sections/overview/overview-latest-products'; import { useAuth } from 'src/hooks/use-auth';
import { OverviewSales } from 'src/sections/overview/overview-sales';
import { OverviewTasksProgress } from 'src/sections/overview/overview-tasks-progress';
import { OverviewTotalCustomers } from 'src/sections/overview/overview-total-customers';
import { OverviewTotalProfit } from 'src/sections/overview/overview-total-profit';
import { OverviewTraffic } from 'src/sections/overview/overview-traffic';
const now = new Date(); const Page = () => {
const auth = useAuth();
const [devices, setDevices] = useState([]);
const Page = () => ( useEffect(() => {
if (auth.user.token) {
console.log("auth.user.token =", auth.user.token)
}else{
auth.user.token = localStorage.getItem("token")
}
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", auth.user.token);
var requestOptions = {
method: 'GET',
headers: myHeaders,
redirect: 'follow'
}
fetch(process.env.NEXT_PUBLIC_REST_ENPOINT+'/device', requestOptions)
.then(response => response.json())
.then(json => setDevices(json))
.catch(error => console.error('Error:', error));
}, []);
return (devices &&
<> <>
<Head> <Head>
<title> <title>
Oktopus | TR-369 Oktopus | TR-369
</title> </title>
</Head> </Head>
<Box <Box
component="main" component="main"
sx={{ sx={{
flexGrow: 1, flexGrow: 1,
py: 8, py: 8,
}} }}
> >
<Container maxWidth="xl" > <Container maxWidth="xl" >
<Grid <Grid
container container
spacing={3} spacing={3}
> >
</Grid> </Grid>
<OverviewLatestOrders <OverviewLatestOrders
orders={[ orders={devices}
{ sx={{ height: '100%' }}
id: 'f69f88012978187a6c12897f', />
ref: 'DEV1049', </Container>
amount: 30.5, </Box>
customer: {
name: 'Ekaterina Tankova'
},
createdAt: 1555016400000,
status: 'Associating'
},
{
id: '9eaa1c7dd4433f413c308ce2',
ref: 'DEV1048',
amount: 25.1,
customer: {
name: 'Cao Yu'
},
createdAt: 1555016400000,
status: 'Online'
},
{
id: '01a5230c811bd04996ce7c13',
ref: 'DEV1047',
amount: 10.99,
customer: {
name: 'Alexa Richardson'
},
createdAt: 1554930000000,
status: 'Offline'
},
{
id: '1f4e1bd0a87cea23cdb83d18',
ref: 'DEV1046',
amount: 96.43,
customer: {
name: 'Anje Keizer'
},
createdAt: 1554757200000,
status: 'Associating'
},
{
id: '9f974f239d29ede969367103',
ref: 'DEV1045',
amount: 32.54,
customer: {
name: 'Clarke Gillebert'
},
createdAt: 1554670800000,
status: 'Online'
},
{
id: 'ffc83c1560ec2f66a1c05596',
ref: 'DEV1044',
amount: 16.76,
customer: {
name: 'Adam Denisov'
},
createdAt: 1554670800000,
status: 'Online'
}
]}
sx={{ height: '100%' }}
/>
</Container>
</Box>
</> </>
); )
}
Page.getLayout = (page) => ( Page.getLayout = (page) => (
<DashboardLayout> <DashboardLayout>
{page} {page}
@ -110,125 +69,3 @@ Page.getLayout = (page) => (
); );
export default Page; export default Page;
/*
<OverviewSales
chartSeries={[
{
name: 'This year',
data: [18, 16, 5, 8, 3, 14, 14, 16, 17, 19, 18, 20]
},
{
name: 'Last year',
data: [12, 11, 4, 6, 2, 9, 9, 10, 11, 12, 13, 13]
}
]}
sx={{ height: '100%' }}
/>
<OverviewLatestProducts
products={[
{
id: '5ece2c077e39da27658aa8a9',
image: '/assets/products/product-1.png',
name: 'Healthcare Erbology',
updatedAt: subHours(now, 6).getTime()
},
{
id: '5ece2c0d16f70bff2cf86cd8',
image: '/assets/products/product-2.png',
name: 'Makeup Lancome Rouge',
updatedAt: subDays(subHours(now, 8), 2).getTime()
},
{
id: 'b393ce1b09c1254c3a92c827',
image: '/assets/products/product-5.png',
name: 'Skincare Soja CO',
updatedAt: subDays(subHours(now, 1), 1).getTime()
},
{
id: 'a6ede15670da63f49f752c89',
image: '/assets/products/product-6.png',
name: 'Makeup Lipstick',
updatedAt: subDays(subHours(now, 3), 3).getTime()
},
{
id: 'bcad5524fe3a2f8f8620ceda',
image: '/assets/products/product-7.png',
name: 'Healthcare Ritual',
updatedAt: subDays(subHours(now, 5), 6).getTime()
}
]}
sx={{ height: '100%' }}
/>
</Grid>
<Grid
xs={12}
md={12}
lg={8}
>
<OverviewLatestOrders
orders={[
{
id: 'f69f88012978187a6c12897f',
ref: 'DEV1049',
amount: 30.5,
customer: {
name: 'Ekaterina Tankova'
},
createdAt: 1555016400000,
status: 'pending'
},
{
id: '9eaa1c7dd4433f413c308ce2',
ref: 'DEV1048',
amount: 25.1,
customer: {
name: 'Cao Yu'
},
createdAt: 1555016400000,
status: 'Online'
},
{
id: '01a5230c811bd04996ce7c13',
ref: 'DEV1047',
amount: 10.99,
customer: {
name: 'Alexa Richardson'
},
createdAt: 1554930000000,
status: 'refunded'
},
{
id: '1f4e1bd0a87cea23cdb83d18',
ref: 'DEV1046',
amount: 96.43,
customer: {
name: 'Anje Keizer'
},
createdAt: 1554757200000,
status: 'pending'
},
{
id: '9f974f239d29ede969367103',
ref: 'DEV1045',
amount: 32.54,
customer: {
name: 'Clarke Gillebert'
},
createdAt: 1554670800000,
status: 'Online'
},
{
id: 'ffc83c1560ec2f66a1c05596',
ref: 'DEV1044',
amount: 16.76,
customer: {
name: 'Adam Denisov'
},
createdAt: 1554670800000,
status: 'Online'
}
]}
sx={{ height: '100%' }}
/>
*/

View File

@ -21,11 +21,23 @@ import { SeverityPill } from 'src/components/severity-pill';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
const statusMap = { const statusMap = {
Associating: 'warning', 1: 'warning',
Online: 'success', 0: 'success',
Offline: 'error' 2: 'error'
}; };
const status = (s)=>{
if (s == 0){
return "Online"
} else if (s == 1){
return "Associating"
}else if (s==2){
return "Offline"
}else {
return "Unknown"
}
}
export const OverviewLatestOrders = (props) => { export const OverviewLatestOrders = (props) => {
const { orders = [], sx } = props; const { orders = [], sx } = props;
@ -39,15 +51,18 @@ export const OverviewLatestOrders = (props) => {
<Table> <Table>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell align="center">
Serial Number
</TableCell>
<TableCell> <TableCell>
Model Model
</TableCell> </TableCell>
<TableCell> <TableCell>
Customer
</TableCell>
<TableCell sortDirection="desc">
Vendor Vendor
</TableCell> </TableCell>
<TableCell>
Version
</TableCell>
<TableCell> <TableCell>
Status Status
</TableCell> </TableCell>
@ -58,26 +73,28 @@ export const OverviewLatestOrders = (props) => {
</TableHead> </TableHead>
<TableBody> <TableBody>
{orders.map((order) => { {orders.map((order) => {
const createdAt = format(order.createdAt, 'dd/MM/yyyy');
return ( return (
<TableRow <TableRow
hover hover
key={order.id} key={order.SN}
> >
<TableCell> <TableCell TableCell align="center">
{order.ref} {order.SN}
</TableCell> </TableCell>
<TableCell> <TableCell>
{order.customer.name} {order.Model}
</TableCell> </TableCell>
<TableCell> <TableCell>
{createdAt} {order.Vendor}
</TableCell> </TableCell>
<TableCell> <TableCell>
<SeverityPill color={statusMap[order.status]}> {order.Version}
{order.status} </TableCell>
</SeverityPill> <TableCell>
<SeverityPill color={statusMap[order.Status]}>
{status(order.Status)}
</SeverityPill>
</TableCell> </TableCell>
<TableCell> <TableCell>
<SvgIcon <SvgIcon