feat(controller): remove enterprise switch + feat: filter devices
This commit is contained in:
parent
32acb8e9ad
commit
badeaf9820
|
|
@ -24,7 +24,6 @@ type Api struct {
|
||||||
db db.Database
|
db db.Database
|
||||||
kv jetstream.KeyValue
|
kv jetstream.KeyValue
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
enterpise config.Enterprise
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const REQUEST_TIMEOUT = time.Second * 30
|
const REQUEST_TIMEOUT = time.Second * 30
|
||||||
|
|
@ -38,16 +37,10 @@ func NewApi(c *config.Config, js jetstream.JetStream, nc *nats.Conn, bridge brid
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
db: d,
|
db: d,
|
||||||
kv: kv,
|
kv: kv,
|
||||||
enterpise: c.Enterprise,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Api) StartApi() {
|
func (a *Api) StartApi() {
|
||||||
|
|
||||||
if a.enterpise.SupportPassword != "" && a.enterpise.SupportEmail != "" {
|
|
||||||
go registerEnterpriseSupport(a.enterpise.SupportEmail, a.enterpise.SupportPassword, a.db)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
authentication := r.PathPrefix("/api/auth").Subrouter()
|
authentication := r.PathPrefix("/api/auth").Subrouter()
|
||||||
authentication.HandleFunc("/login", a.generateToken).Methods("PUT")
|
authentication.HandleFunc("/login", a.generateToken).Methods("PUT")
|
||||||
|
|
@ -57,13 +50,6 @@ func (a *Api) StartApi() {
|
||||||
authentication.HandleFunc("/password", a.changePassword).Methods("PUT")
|
authentication.HandleFunc("/password", a.changePassword).Methods("PUT")
|
||||||
authentication.HandleFunc("/admin/register", a.registerAdminUser).Methods("POST")
|
authentication.HandleFunc("/admin/register", a.registerAdminUser).Methods("POST")
|
||||||
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
|
authentication.HandleFunc("/admin/exists", a.adminUserExists).Methods("GET")
|
||||||
if a.enterpise.Enable {
|
|
||||||
mapRoutes := r.PathPrefix("/api/map").Subrouter()
|
|
||||||
mapRoutes.HandleFunc("", a.devicesLocation).Methods("GET")
|
|
||||||
mapRoutes.Use(func(handler http.Handler) http.Handler {
|
|
||||||
return middleware.Middleware(handler)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
iot := r.PathPrefix("/api/device").Subrouter()
|
iot := r.PathPrefix("/api/device").Subrouter()
|
||||||
iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT")
|
iot.HandleFunc("/alias", a.setDeviceAlias).Methods("PUT")
|
||||||
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE")
|
iot.HandleFunc("/auth", a.deviceAuth).Methods("GET", "POST", "DELETE")
|
||||||
|
|
@ -74,7 +60,7 @@ func (a *Api) StartApi() {
|
||||||
iot.HandleFunc("/cwmp/{sn}/addObject", a.cwmpAddObjectMsg).Methods("PUT")
|
iot.HandleFunc("/cwmp/{sn}/addObject", a.cwmpAddObjectMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/cwmp/{sn}/deleteObject", a.cwmpDeleteObjectMsg).Methods("PUT")
|
iot.HandleFunc("/cwmp/{sn}/deleteObject", a.cwmpDeleteObjectMsg).Methods("PUT")
|
||||||
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
|
iot.HandleFunc("", a.retrieveDevices).Methods("GET")
|
||||||
iot.HandleFunc("/{id}", a.retrieveDevices).Methods("GET")
|
iot.HandleFunc("/filterOptions", a.filterOptions).Methods("GET")
|
||||||
iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/{mtp}/get", a.deviceGetMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/{mtp}/add", a.deviceCreateMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/{mtp}/add", a.deviceCreateMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/{mtp}/del", a.deviceDeleteMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/{mtp}/del", a.deviceDeleteMsg).Methods("PUT")
|
||||||
|
|
@ -84,13 +70,6 @@ func (a *Api) StartApi() {
|
||||||
iot.HandleFunc("/{sn}/{mtp}/instances", a.deviceGetParameterInstances).Methods("PUT")
|
iot.HandleFunc("/{sn}/{mtp}/instances", a.deviceGetParameterInstances).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/{mtp}/operate", a.deviceOperateMsg).Methods("PUT")
|
iot.HandleFunc("/{sn}/{mtp}/operate", a.deviceOperateMsg).Methods("PUT")
|
||||||
iot.HandleFunc("/{sn}/{mtp}/fw_update", a.deviceFwUpdate).Methods("PUT") //TODO: put it to work and generalize for usp and cwmp
|
iot.HandleFunc("/{sn}/{mtp}/fw_update", a.deviceFwUpdate).Methods("PUT") //TODO: put it to work and generalize for usp and cwmp
|
||||||
if a.enterpise.Enable {
|
|
||||||
iot.HandleFunc("/{sn}/sitesurvey", a.deviceSiteSurvey).Methods("GET")
|
|
||||||
iot.HandleFunc("/{sn}/connecteddevices", a.deviceConnectedDevices).Methods("GET")
|
|
||||||
iot.HandleFunc("/{sn}/traceroute", a.deviceTraceRoute).Methods("GET", "PUT")
|
|
||||||
iot.HandleFunc("/{sn}/speedtest", a.deviceSpeedTest).Methods("PUT")
|
|
||||||
iot.HandleFunc("/{sn}/ping", a.devicePing).Methods("PUT", "GET")
|
|
||||||
}
|
|
||||||
iot.HandleFunc("/{sn}/wifi", a.deviceWifi).Methods("PUT", "GET")
|
iot.HandleFunc("/{sn}/wifi", a.deviceWifi).Methods("PUT", "GET")
|
||||||
dash := r.PathPrefix("/api/info").Subrouter()
|
dash := r.PathPrefix("/api/info").Subrouter()
|
||||||
dash.HandleFunc("/vendors", a.vendorsInfo).Methods("GET")
|
dash.HandleFunc("/vendors", a.vendorsInfo).Methods("GET")
|
||||||
|
|
@ -131,32 +110,3 @@ func (a *Api) StartApi() {
|
||||||
}()
|
}()
|
||||||
log.Println("Running REST API at port", a.port)
|
log.Println("Running REST API at port", a.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerEnterpriseSupport(email, password string, d db.Database) {
|
|
||||||
|
|
||||||
user := db.User{
|
|
||||||
Email: email,
|
|
||||||
Password: password,
|
|
||||||
Name: "Enterprise Support",
|
|
||||||
Level: db.AdminUser,
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
if err := user.HashPassword(password); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.RegisterUser(user)
|
|
||||||
if err != nil {
|
|
||||||
if err == db.ErrorUserExists {
|
|
||||||
log.Println("Enterprise support user already registered.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("Error to register enterprise support user:", err)
|
|
||||||
time.Sleep(time.Second * 5)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Println("Enterprise support user registered successfully.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,11 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/leandrofars/oktopus/internal/bridge"
|
"github.com/leandrofars/oktopus/internal/bridge"
|
||||||
"github.com/leandrofars/oktopus/internal/db"
|
"github.com/leandrofars/oktopus/internal/db"
|
||||||
|
"github.com/leandrofars/oktopus/internal/entity"
|
||||||
local "github.com/leandrofars/oktopus/internal/nats"
|
local "github.com/leandrofars/oktopus/internal/nats"
|
||||||
"github.com/leandrofars/oktopus/internal/utils"
|
"github.com/leandrofars/oktopus/internal/utils"
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
|
|
@ -16,6 +18,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
|
func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method == http.MethodDelete {
|
||||||
|
id := r.URL.Query().Get("id")
|
||||||
|
if id == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err := json.NewEncoder(w).Encode("No id provided")
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := strings.Split(id, ",")
|
||||||
|
|
||||||
|
msg, err := bridge.NatsReq[int64](local.NATS_ADAPTER_SUBJECT+"devices.delete", utils.Marshall(ids), w, a.nc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
|
"number_of_deleted_devices": msg.Msg,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const PAGE_SIZE_LIMIT = 50
|
const PAGE_SIZE_LIMIT = 50
|
||||||
const PAGE_SIZE_DEFAULT = 20
|
const PAGE_SIZE_DEFAULT = 20
|
||||||
|
|
||||||
|
|
@ -33,9 +62,36 @@ func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statusOrderFromUser := r.URL.Query().Get("statusOrder")
|
||||||
|
var statusOrder int
|
||||||
|
if statusOrderFromUser != "" {
|
||||||
|
if statusOrderFromUser == "asc" {
|
||||||
|
statusOrder = 1
|
||||||
|
} else if statusOrderFromUser == "desc" {
|
||||||
|
statusOrder = -1
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode("Status order must be 'asc' or 'desc'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statusOrder = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sort := bson.M{}
|
||||||
|
sort["status"] = statusOrder
|
||||||
|
|
||||||
|
version := r.URL.Query().Get("version")
|
||||||
|
vendor := r.URL.Query().Get("vendor")
|
||||||
|
productClass := r.URL.Query().Get("type")
|
||||||
|
alias := r.URL.Query().Get("alias")
|
||||||
|
model := r.URL.Query().Get("model")
|
||||||
|
status := r.URL.Query().Get("status")
|
||||||
|
|
||||||
// Get devices with pagination
|
// Get devices with pagination
|
||||||
page_n := r.URL.Query().Get("page_number")
|
page_n := r.URL.Query().Get("page_number")
|
||||||
page_s := r.URL.Query().Get("page_size")
|
page_s := r.URL.Query().Get("page_size")
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var page_number int64
|
var page_number int64
|
||||||
|
|
@ -70,52 +126,69 @@ func (a *Api) retrieveDevices(w http.ResponseWriter, r *http.Request) {
|
||||||
page_size = PAGE_SIZE_DEFAULT
|
page_size = PAGE_SIZE_DEFAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
total, err := getDeviceCount(w, a.nc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
skip := page_number * (page_size - 1)
|
skip := page_number * (page_size - 1)
|
||||||
if total < page_size {
|
|
||||||
skip = 0
|
filter := map[string]interface{}{
|
||||||
|
"status_order": statusOrder,
|
||||||
|
"limit": page_size,
|
||||||
|
"skip": skip,
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: fix status ordering
|
if version != "" {
|
||||||
statusOrder := r.URL.Query().Get("status")
|
filter["version"] = version
|
||||||
if statusOrder != "" {
|
}
|
||||||
if statusOrder == "asc" {
|
if vendor != "" {
|
||||||
statusOrder = "1"
|
filter["vendor"] = vendor
|
||||||
} else if statusOrder == "desc" {
|
}
|
||||||
statusOrder = "-1"
|
if productClass != "" {
|
||||||
} else {
|
filter["productClass"] = productClass
|
||||||
|
}
|
||||||
|
if alias != "" {
|
||||||
|
filter["alias"] = alias
|
||||||
|
}
|
||||||
|
if model != "" {
|
||||||
|
filter["model"] = model
|
||||||
|
}
|
||||||
|
if status != "" {
|
||||||
|
fmtStatus, err := strconv.Atoi(status)
|
||||||
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
json.NewEncoder(w).Encode("Status order must be 'asc' or 'desc'")
|
json.NewEncoder(w).Encode("Status must be an integer")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
filter["status"] = fmtStatus
|
||||||
|
|
||||||
sort := bson.M{}
|
|
||||||
sort["status"] = 1
|
|
||||||
|
|
||||||
//TODO: Create filters
|
|
||||||
|
|
||||||
filter := bson.A{
|
|
||||||
//bson.M{"$match": filter},
|
|
||||||
bson.M{"$sort": sort}, // shows online devices first
|
|
||||||
bson.M{"$skip": skip},
|
|
||||||
bson.M{"$limit": page_size},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
devices, err := getDevices(w, filter, a.nc)
|
devices, err := getDevices(w, filter, a.nc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Println("Error getting devices", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if devices.Total == 0 {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
err := json.NewEncoder(w).Encode("No devices found")
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip >= devices.Total {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
err := json.NewEncoder(w).Encode("Page number is out of range")
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(map[string]interface{}{
|
err = json.NewEncoder(w).Encode(map[string]interface{}{
|
||||||
"pages": total / page_size,
|
"pages": devices.Total / page_size,
|
||||||
"page": page_number,
|
"page": page_number,
|
||||||
"size": page_size,
|
"size": page_size,
|
||||||
"devices": devices,
|
"devices": devices.Devices,
|
||||||
|
"total": devices.Total,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
|
@ -283,3 +356,14 @@ func (a *Api) setDeviceAlias(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Api) filterOptions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
resp, err := bridge.NatsReq[entity.FilterOptions](local.NATS_ADAPTER_SUBJECT+"devices.filterOptions", nil, w, a.nc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(resp.Code)
|
||||||
|
w.Write(utils.Marshall(resp.Msg))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"github.com/leandrofars/oktopus/internal/bridge"
|
|
||||||
"github.com/leandrofars/oktopus/internal/entity"
|
|
||||||
"github.com/leandrofars/oktopus/internal/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (a *Api) getEnterpriseResource(
|
|
||||||
resource string,
|
|
||||||
action string,
|
|
||||||
device *entity.Device,
|
|
||||||
sn string,
|
|
||||||
w http.ResponseWriter,
|
|
||||||
body []byte,
|
|
||||||
protocol, datamodel string,
|
|
||||||
) error {
|
|
||||||
model, err := cwmpGetDeviceModel(device, w)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bridge.NatsEnterpriseInteraction("enterprise.v1."+protocol+"."+datamodel+"."+model+"."+sn+"."+resource+"."+action, body, w, a.nc)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) getMapsResource(
|
|
||||||
action string,
|
|
||||||
w http.ResponseWriter,
|
|
||||||
body []byte,
|
|
||||||
) error {
|
|
||||||
|
|
||||||
err := bridge.NatsEnterpriseInteraction("geolocation.v1."+action, body, w, a.nc)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) devicesLocation(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method == http.MethodGet {
|
|
||||||
a.getMapsResource("get", w, []byte{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) deviceSiteSurvey(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
sn := vars["sn"]
|
|
||||||
|
|
||||||
device, err := getDeviceInfo(w, sn, a.nc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method == http.MethodGet {
|
|
||||||
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
a.getEnterpriseResource("sitesurvey", "get", device, sn, w, []byte{}, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
|
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
|
||||||
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Device is Offline"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) deviceConnectedDevices(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
sn := vars["sn"]
|
|
||||||
|
|
||||||
device, err := getDeviceInfo(w, sn, a.nc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method == http.MethodGet {
|
|
||||||
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
a.getEnterpriseResource("connecteddevices", "get", device, sn, w, []byte{}, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
|
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
|
||||||
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Device is Offline"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) deviceTraceRoute(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
sn := vars["sn"]
|
|
||||||
|
|
||||||
device, err := getDeviceInfo(w, sn, a.nc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method == http.MethodGet {
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
|
||||||
w.Write(utils.Marshall("Get traceroute configuration is not implemented yet"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method == http.MethodPut {
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
a.getEnterpriseResource("traceroute", "set", device, sn, w, []byte{}, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
|
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
|
||||||
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Device is Offline"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) deviceSpeedTest(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
sn := vars["sn"]
|
|
||||||
|
|
||||||
device, err := getDeviceInfo(w, sn, a.nc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Error reading request body"))
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
a.getEnterpriseResource("speedTest", "set", device, sn, w, payload, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
|
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
|
||||||
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Device is Offline"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Api) devicePing(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
sn := vars["sn"]
|
|
||||||
|
|
||||||
device, err := getDeviceInfo(w, sn, a.nc)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Cwmp != entity.Online {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Device is Offline"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.Method == http.MethodGet {
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
a.getEnterpriseResource("ping", "get", device, sn, w, []byte{}, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
payload, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
w.Write(utils.Marshall("Error reading request body"))
|
|
||||||
return
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Cwmp == entity.Online {
|
|
||||||
a.getEnterpriseResource("ping", "set", device, sn, w, payload, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if device.Mqtt == entity.Online || device.Stomp == entity.Online || device.Websockets == entity.Online {
|
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
|
||||||
w.Write(utils.Marshall("This feature is only working with CWMP devices"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -176,7 +176,7 @@ func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) {
|
||||||
utils.MarshallEncoder(err, w)
|
utils.MarshallEncoder(err, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !adminUserExists(users, a.enterpise.SupportEmail) {
|
if !adminUserExists(users) {
|
||||||
var user db.User
|
var user db.User
|
||||||
err = json.NewDecoder(r.Body).Decode(&user)
|
err = json.NewDecoder(r.Body).Decode(&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -235,14 +235,14 @@ func (a *Api) registerAdminUser(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func adminUserExists(users []map[string]interface{}, supportEmail string) bool {
|
func adminUserExists(users []map[string]interface{}) bool {
|
||||||
|
|
||||||
if len(users) == 0 {
|
if len(users) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, x := range users {
|
for _, x := range users {
|
||||||
if db.UserLevels(x["level"].(int32)) == db.AdminUser && x["email"].(string) != supportEmail {
|
if db.UserLevels(x["level"].(int32)) == db.AdminUser {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,7 +257,7 @@ func (a *Api) adminUserExists(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
adminExits := adminUserExists(users, a.enterpise.SupportEmail)
|
adminExits := adminUserExists(users)
|
||||||
json.NewEncoder(w).Encode(adminExits)
|
json.NewEncoder(w).Encode(adminExits)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import (
|
||||||
local "github.com/leandrofars/oktopus/internal/nats"
|
local "github.com/leandrofars/oktopus/internal/nats"
|
||||||
"github.com/leandrofars/oktopus/internal/utils"
|
"github.com/leandrofars/oktopus/internal/utils"
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errInvalidMtp = errors.New("Invalid MTP, valid options are: mqtt, ws, stomp")
|
var errInvalidMtp = errors.New("Invalid MTP, valid options are: mqtt, ws, stomp")
|
||||||
|
|
@ -105,8 +104,8 @@ func getDeviceCount(w http.ResponseWriter, nc *nats.Conn) (int64, error) {
|
||||||
return msg.Msg, err
|
return msg.Msg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDevices(w http.ResponseWriter, filter primitive.A, nc *nats.Conn) (*[]entity.Device, error) {
|
func getDevices(w http.ResponseWriter, filter map[string]interface{}, nc *nats.Conn) (*entity.DevicesList, error) {
|
||||||
msg, err := bridge.NatsReq[[]entity.Device](
|
msg, err := bridge.NatsReq[entity.DevicesList](
|
||||||
local.NATS_ADAPTER_SUBJECT+"devices.retrieve",
|
local.NATS_ADAPTER_SUBJECT+"devices.retrieve",
|
||||||
utils.Marshall(filter),
|
utils.Marshall(filter),
|
||||||
w,
|
w,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -213,11 +212,6 @@ func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if device.Cwmp == entity.Online {
|
if device.Cwmp == entity.Online {
|
||||||
|
|
||||||
if a.enterpise.Enable {
|
|
||||||
a.getEnterpriseResource("wifi", "get", device, sn, w, []byte{}, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
NUMBER_OF_WIFI_PARAMS_TO_GET = 5
|
NUMBER_OF_WIFI_PARAMS_TO_GET = 5
|
||||||
)
|
)
|
||||||
|
|
@ -347,17 +341,6 @@ func (a *Api) deviceWifi(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if device.Cwmp == entity.Online {
|
if device.Cwmp == entity.Online {
|
||||||
|
|
||||||
if a.enterpise.Enable {
|
|
||||||
payload, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
w.Write(utils.Marshall(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.getEnterpriseResource("wifi", "set", device, sn, w, payload, "cwmp", "098")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var body []WiFi
|
var body []WiFi
|
||||||
|
|
||||||
err := utils.MarshallDecoder(&body, r.Body)
|
err := utils.MarshallDecoder(&body, r.Body)
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,6 @@ func NatsReq[T entity.DataType](
|
||||||
|
|
||||||
err = json.Unmarshal(msg.Data, &answer)
|
err = json.Unmarshal(msg.Data, &answer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
var errMsg *entity.MsgAnswer[*string]
|
var errMsg *entity.MsgAnswer[*string]
|
||||||
err = json.Unmarshal(msg.Data, &errMsg)
|
err = json.Unmarshal(msg.Data, &errMsg)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,10 @@ type RestApi struct {
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
type Enterprise struct {
|
|
||||||
Enable bool
|
|
||||||
SupportPassword string
|
|
||||||
SupportEmail string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
RestApi RestApi
|
RestApi RestApi
|
||||||
Nats Nats
|
Nats Nats
|
||||||
Mongo Mongo
|
Mongo Mongo
|
||||||
Enterprise Enterprise
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tls struct {
|
type Tls struct {
|
||||||
|
|
@ -62,9 +55,6 @@ func NewConfig() *Config {
|
||||||
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
|
serverCA := flag.String("server_ca", lookupEnvOrString("SERVER_CA", "rootCA.pem"), "server CA file to TLS connection")
|
||||||
flApiPort := flag.String("api_port", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port")
|
flApiPort := flag.String("api_port", lookupEnvOrString("REST_API_PORT", "8000"), "Rest api port")
|
||||||
mongoUri := flag.String("mongo_uri", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "uri for mongodb server")
|
mongoUri := flag.String("mongo_uri", lookupEnvOrString("MONGO_URI", "mongodb://localhost:27017"), "uri for mongodb server")
|
||||||
enterpise := flag.Bool("enterprise", lookupEnvOrBool("ENTERPRISE", false), "enterprise version enable")
|
|
||||||
enterprise_support_password := flag.String("enterprise_support_password", lookupEnvOrString("ENTERPRISE_SUPPORT_PASSWORD", ""), "enterprise support password")
|
|
||||||
enterpise_support_email := flag.String("enterprise_support_email", lookupEnvOrString("ENTERPRISE_SUPPORT_EMAIL", ""), "enterprise support email")
|
|
||||||
flHelp := flag.Bool("help", false, "Help")
|
flHelp := flag.Bool("help", false, "Help")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -103,11 +93,6 @@ func NewConfig() *Config {
|
||||||
Uri: *mongoUri,
|
Uri: *mongoUri,
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
},
|
},
|
||||||
Enterprise: Enterprise{
|
|
||||||
Enable: *enterpise,
|
|
||||||
SupportPassword: *enterprise_support_password,
|
|
||||||
SupportEmail: *enterpise_support_email,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,15 @@ type StatusCount struct {
|
||||||
Status int `bson:"_id" json:"status"`
|
Status int `bson:"_id" json:"status"`
|
||||||
Count int `bson:"count" json:"count"`
|
Count int `bson:"count" json:"count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DevicesList struct {
|
||||||
|
Devices []Device `json:"devices" bson:"documents"`
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterOptions struct {
|
||||||
|
Models []string `json:"models"`
|
||||||
|
ProductClasses []string `json:"productClasses"`
|
||||||
|
Vendors []string `json:"vendors"`
|
||||||
|
Versions []string `json:"versions"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package entity
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type DataType interface {
|
type DataType interface {
|
||||||
[]map[string]interface{} | *string | Device | int64 | []Device | []VendorsCount | []ProductClassCount | []StatusCount | time.Duration | []byte
|
[]map[string]interface{} | *string | Device | int64 | []Device | []VendorsCount | []ProductClassCount | []StatusCount | time.Duration | []byte | []string | FilterOptions | DevicesList
|
||||||
}
|
}
|
||||||
|
|
||||||
type MsgAnswer[T DataType] struct {
|
type MsgAnswer[T DataType] struct {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
)
|
)
|
||||||
|
|
@ -41,6 +42,18 @@ type Device struct {
|
||||||
Cwmp Status
|
Cwmp Status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DevicesList struct {
|
||||||
|
Devices []Device `json:"devices" bson:"documents"`
|
||||||
|
Total int64 `json:"total" bson:"totalCount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterOptions struct {
|
||||||
|
Models []string `json:"models"`
|
||||||
|
ProductClasses []string `json:"productClasses"`
|
||||||
|
Vendors []string `json:"vendors"`
|
||||||
|
Versions []string `json:"versions"`
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Database) CreateDevice(device Device) error {
|
func (d *Database) CreateDevice(device Device) error {
|
||||||
var result bson.M
|
var result bson.M
|
||||||
var deviceExistent Device
|
var deviceExistent Device
|
||||||
|
|
@ -100,32 +113,86 @@ func (d *Database) CreateDevice(device Device) error {
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
func (d *Database) RetrieveDevices(filter bson.A) ([]Device, error) {
|
func (d *Database) RetrieveDevices(filter bson.A) (*DevicesList, error) {
|
||||||
|
|
||||||
var results []Device
|
var results []DevicesList
|
||||||
|
|
||||||
cursor, err := d.devices.Aggregate(d.ctx, filter)
|
cursor, err := d.devices.Aggregate(d.ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return results, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cursor.Err() != nil {
|
if cursor.Err() != nil {
|
||||||
return results, cursor.Err()
|
return nil, cursor.Err()
|
||||||
|
}
|
||||||
|
defer cursor.Close(d.ctx)
|
||||||
|
if err := cursor.All(d.ctx, &results); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for cursor.Next(d.ctx) {
|
//log.Printf("results: %++v", results)
|
||||||
var device Device
|
|
||||||
|
|
||||||
err := cursor.Decode(&device)
|
return &results[0], err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) RetrieveDeviceFilterOptions() (FilterOptions, error) {
|
||||||
|
filter := bson.A{
|
||||||
|
bson.D{
|
||||||
|
{"$group",
|
||||||
|
bson.D{
|
||||||
|
{"_id", primitive.Null{}},
|
||||||
|
{"vendors", bson.D{{"$addToSet", "$vendor"}}},
|
||||||
|
{"versions", bson.D{{"$addToSet", "$version"}}},
|
||||||
|
{"productClasses", bson.D{{"$addToSet", "$productclass"}}},
|
||||||
|
{"models", bson.D{{"$addToSet", "$model"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bson.D{
|
||||||
|
{"$project",
|
||||||
|
bson.D{
|
||||||
|
{"_id", 0},
|
||||||
|
{"vendors", 1},
|
||||||
|
{"versions", 1},
|
||||||
|
{"productClasses", 1},
|
||||||
|
{"models", 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []FilterOptions
|
||||||
|
cursor, err := d.devices.Aggregate(d.ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error to decode device info fields")
|
log.Println(err)
|
||||||
continue
|
return FilterOptions{}, err
|
||||||
|
}
|
||||||
|
defer cursor.Close(d.ctx)
|
||||||
|
|
||||||
|
if err := cursor.All(d.ctx, &results); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return FilterOptions{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, device)
|
if len(results) > 0 {
|
||||||
|
return results[0], nil
|
||||||
|
} else {
|
||||||
|
return FilterOptions{
|
||||||
|
Models: []string{},
|
||||||
|
ProductClasses: []string{},
|
||||||
|
Vendors: []string{},
|
||||||
|
Versions: []string{},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return results, err
|
func (d *Database) DeleteDevices(filter bson.D) (int64, error) {
|
||||||
|
|
||||||
|
result, err := d.devices.DeleteMany(d.ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
return result.DeletedCount, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Database) RetrieveDevice(sn string) (Device, error) {
|
func (d *Database) RetrieveDevice(sn string) (Device, error) {
|
||||||
|
|
|
||||||
|
|
@ -52,18 +52,135 @@ func StartRequestsListener(ctx context.Context, nc *nats.Conn, db db.Database) {
|
||||||
|
|
||||||
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.retrieve", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
|
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.retrieve", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
|
||||||
|
|
||||||
var filter bson.A
|
var criteria map[string]interface{}
|
||||||
|
|
||||||
err := json.Unmarshal(msg.Data, &filter)
|
err := json.Unmarshal(msg.Data, &criteria)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondMsg(msg.Respond, 500, err.Error())
|
respondMsg(msg.Respond, 500, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//log.Println(criteria)
|
||||||
|
propertiesFilter := bson.D{{}}
|
||||||
|
|
||||||
|
vendorFilter := criteria["vendor"]
|
||||||
|
if vendorFilter != nil {
|
||||||
|
log.Println("Vendor filter", vendorFilter)
|
||||||
|
propertiesFilter = append(propertiesFilter, bson.E{Key: "vendor", Value: vendorFilter})
|
||||||
|
}
|
||||||
|
|
||||||
|
versionFilter := criteria["version"]
|
||||||
|
if versionFilter != nil {
|
||||||
|
log.Println("Version filter", versionFilter)
|
||||||
|
propertiesFilter = append(propertiesFilter, bson.E{Key: "version", Value: versionFilter})
|
||||||
|
}
|
||||||
|
|
||||||
|
typeFilter := criteria["productClass"]
|
||||||
|
if typeFilter != nil {
|
||||||
|
log.Println("Type filter", typeFilter)
|
||||||
|
propertiesFilter = append(propertiesFilter, bson.E{Key: "productclass", Value: typeFilter})
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasFilter := criteria["alias"]
|
||||||
|
if aliasFilter != nil {
|
||||||
|
log.Println("Type filter", aliasFilter)
|
||||||
|
propertiesFilter = append(propertiesFilter, bson.E{Key: "alias", Value: aliasFilter})
|
||||||
|
}
|
||||||
|
|
||||||
|
modelFilter := criteria["model"]
|
||||||
|
if modelFilter != nil {
|
||||||
|
log.Println("Model filter", modelFilter)
|
||||||
|
propertiesFilter = append(propertiesFilter, bson.E{Key: "model", Value: modelFilter})
|
||||||
|
}
|
||||||
|
|
||||||
|
statusFilter := criteria["status"]
|
||||||
|
if statusFilter != nil {
|
||||||
|
log.Println("Status filter", statusFilter)
|
||||||
|
propertiesFilter = append(propertiesFilter, bson.E{Key: "status", Value: statusFilter})
|
||||||
|
}
|
||||||
|
|
||||||
|
filter := bson.A{
|
||||||
|
bson.D{
|
||||||
|
{"$match",
|
||||||
|
propertiesFilter,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bson.D{
|
||||||
|
{"$facet",
|
||||||
|
bson.D{
|
||||||
|
{"totalCount",
|
||||||
|
bson.A{
|
||||||
|
bson.D{{"$count", "count"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"documents",
|
||||||
|
bson.A{
|
||||||
|
bson.D{{"$sort", bson.D{{"status", criteria["status_order"]}}}},
|
||||||
|
bson.D{{"$skip", criteria["skip"]}},
|
||||||
|
bson.D{{"$limit", criteria["limit"]}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bson.D{
|
||||||
|
{"$project",
|
||||||
|
bson.D{
|
||||||
|
{"totalCount",
|
||||||
|
bson.D{
|
||||||
|
{"$arrayElemAt",
|
||||||
|
bson.A{
|
||||||
|
"$totalCount.count",
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{"documents", 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
devicesList, err := db.RetrieveDevices(filter)
|
devicesList, err := db.RetrieveDevices(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondMsg(msg.Respond, 500, err.Error())
|
respondMsg(msg.Respond, 500, err.Error())
|
||||||
}
|
}
|
||||||
respondMsg(msg.Respond, 200, devicesList)
|
respondMsg(msg.Respond, 200, &devicesList)
|
||||||
|
})
|
||||||
|
|
||||||
|
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.delete", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
|
||||||
|
|
||||||
|
var serialNumbersList []string
|
||||||
|
|
||||||
|
err := json.Unmarshal(msg.Data, &serialNumbersList)
|
||||||
|
if err != nil {
|
||||||
|
respondMsg(msg.Respond, 500, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var criteria bson.A
|
||||||
|
|
||||||
|
for _, sn := range serialNumbersList {
|
||||||
|
criteria = append(criteria, bson.D{{"sn", sn}})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the filter with the $or operator
|
||||||
|
filter := bson.D{
|
||||||
|
{"$or", criteria},
|
||||||
|
}
|
||||||
|
|
||||||
|
deletedCount, err := db.DeleteDevices(filter)
|
||||||
|
if err != nil {
|
||||||
|
respondMsg(msg.Respond, 500, err.Error())
|
||||||
|
}
|
||||||
|
respondMsg(msg.Respond, 200, deletedCount)
|
||||||
|
})
|
||||||
|
|
||||||
|
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.filterOptions", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
|
||||||
|
result, err := db.RetrieveDeviceFilterOptions()
|
||||||
|
if err != nil {
|
||||||
|
respondMsg(msg.Respond, 500, err.Error())
|
||||||
|
}
|
||||||
|
respondMsg(msg.Respond, 200, result)
|
||||||
})
|
})
|
||||||
|
|
||||||
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.class", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
|
nc.QueueSubscribe(local.ADAPTER_SUBJECT+"devices.class", local.ADAPTER_QUEUE, func(msg *nats.Msg) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Container,
|
Container,
|
||||||
|
|
@ -10,29 +11,201 @@ import {
|
||||||
SvgIcon,
|
SvgIcon,
|
||||||
Stack,
|
Stack,
|
||||||
Pagination,
|
Pagination,
|
||||||
CircularProgress
|
CircularProgress,
|
||||||
|
Button,
|
||||||
|
Menu,
|
||||||
|
MenuItem,
|
||||||
|
Checkbox,
|
||||||
|
ListItemText,
|
||||||
|
SpeedDial,
|
||||||
|
SpeedDialAction,
|
||||||
|
SpeedDialIcon,
|
||||||
|
CardHeader,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
FormControl,
|
||||||
|
Tooltip,
|
||||||
|
Chip,
|
||||||
|
Dialog,
|
||||||
|
DialogTitle,
|
||||||
|
DialogContent,
|
||||||
|
DialogActions,
|
||||||
|
InputLabel,
|
||||||
|
Input,
|
||||||
|
TextField,
|
||||||
|
Select,
|
||||||
|
DialogContentText,
|
||||||
|
TableContainer,
|
||||||
|
TablePagination
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
|
import ViewColumnsIcon from '@heroicons/react/24/outline/ViewColumnsIcon';
|
||||||
|
import ArrowTopRightOnSquareIcon from '@heroicons/react/24/solid/ArrowTopRightOnSquareIcon';
|
||||||
|
import FunnelIcon from "@heroicons/react/24/outline/FunnelIcon";
|
||||||
|
import PencilIcon from '@heroicons/react/24/outline/PencilIcon';
|
||||||
|
import TagIcon from '@heroicons/react/24/outline/TagIcon';
|
||||||
|
import ShareIcon from '@heroicons/react/24/outline/ShareIcon';
|
||||||
|
import CommandLineIcon from '@heroicons/react/24/outline/CommandLineIcon';
|
||||||
|
import ChevronUpIcon from '@heroicons/react/24/outline/ChevronUpIcon';
|
||||||
|
import ChevronDownIcon from '@heroicons/react/24/outline/ChevronDownIcon';
|
||||||
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
import MagnifyingGlassIcon from '@heroicons/react/24/solid/MagnifyingGlassIcon';
|
||||||
|
import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
||||||
|
|
||||||
|
import { Scrollbar } from 'src/components/scrollbar';
|
||||||
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
import { OverviewLatestOrders } from 'src/sections/overview/overview-latest-orders';
|
|
||||||
import { useAuth } from 'src/hooks/use-auth';
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const auth = useAuth();
|
const auth = useAuth();
|
||||||
|
|
||||||
const [devices, setDevices] = useState([]);
|
const [devices, setDevices] = useState([]);
|
||||||
|
const [total, setTotal] = useState(null);
|
||||||
const [deviceFound, setDeviceFound] = useState(true)
|
const [deviceFound, setDeviceFound] = useState(true)
|
||||||
const [pages, setPages] = useState(0);
|
const [pages, setPages] = useState(0);
|
||||||
const [page, setPage] = useState(null);
|
const [page, setPage] = useState(0);
|
||||||
const [Loading, setLoading] = useState(true);
|
const [Loading, setLoading] = useState(true);
|
||||||
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
|
const [statusOrder, setStatusOrder] = useState("desc");
|
||||||
|
|
||||||
|
const [filterOptions, setFilterOptions] = useState(null);
|
||||||
|
const defaultFiltersList = {
|
||||||
|
alias: "",
|
||||||
|
model: "",
|
||||||
|
vendor: "",
|
||||||
|
version: "",
|
||||||
|
status: "",
|
||||||
|
type: "",
|
||||||
|
}
|
||||||
|
const [filtersList, setFiltersList] = useState(defaultFiltersList);
|
||||||
|
const [newFiltersList, setNewFiltersList] = useState(defaultFiltersList);
|
||||||
|
|
||||||
|
const cleanFilters = () => {
|
||||||
|
setFiltersList(defaultFiltersList)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowsPerPageOptions = [20,30,40];
|
||||||
|
const [rowsPerPage, setRowsPerPage] = useState(20);
|
||||||
|
|
||||||
|
const [showSetDeviceAlias, setShowSetDeviceAlias] = useState(false);
|
||||||
|
const [deviceAlias, setDeviceAlias] = useState(null);
|
||||||
|
const [deviceToBeChanged, setDeviceToBeChanged] = useState(null);
|
||||||
|
const [showFilter, setShowFilter] = useState(false);
|
||||||
|
const [selected, setSelected] = useState([]);
|
||||||
|
const [selectAll, setSelectAll] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
const [columns, setColumns] = useState({
|
||||||
|
version: true,
|
||||||
|
sn: true,
|
||||||
|
alias: true,
|
||||||
|
model: true,
|
||||||
|
vendor: true,
|
||||||
|
status: true,
|
||||||
|
actions: true,
|
||||||
|
label: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const [showSpeedDial, setShowSpeedDial] = useState(false);
|
||||||
|
|
||||||
|
const getColumns = () => {
|
||||||
|
localStorage.getItem("columns") ? setColumns(JSON.parse(localStorage.getItem("columns"))) : setColumns({
|
||||||
|
version: true,
|
||||||
|
sn: true,
|
||||||
|
alias: false,
|
||||||
|
model: true,
|
||||||
|
vendor: true,
|
||||||
|
status: true,
|
||||||
|
actions: true,
|
||||||
|
label: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeColumn = (column) => {
|
||||||
|
console.log("columns old:", columns)
|
||||||
|
setColumns({ ...columns, [column]: !columns[column] })
|
||||||
|
localStorage.setItem("columns", JSON.stringify({ ...columns, [column]: !columns[column] }))
|
||||||
|
}
|
||||||
|
|
||||||
|
function objsEqual(obj1,obj2){
|
||||||
|
return JSON.stringify(obj1)===JSON.stringify(obj2);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (selected.length > 0) {
|
||||||
|
let speedDial = false
|
||||||
|
selected.map((s) => {
|
||||||
|
if (s == true) {
|
||||||
|
speedDial = true
|
||||||
|
} else if (s == false) {
|
||||||
|
setSelectAll(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setShowSpeedDial(speedDial)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}, [selected])
|
||||||
|
|
||||||
|
const statusMap = {
|
||||||
|
1: 'warning',
|
||||||
|
2: 'success',
|
||||||
|
0: 'error'
|
||||||
|
};
|
||||||
|
|
||||||
|
const status = (s) => {
|
||||||
|
if (s == 0) {
|
||||||
|
return "Offline"
|
||||||
|
} else if (s == 1) {
|
||||||
|
return "Associating"
|
||||||
|
} else if (s == 2) {
|
||||||
|
return "Online"
|
||||||
|
} else {
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getDeviceProtocol = (order) => {
|
||||||
|
if (order.Cwmp != null) {
|
||||||
|
return "cwmp"
|
||||||
|
} else {
|
||||||
|
return "usp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = Boolean(anchorEl);
|
||||||
|
const handleClick = (event) => {
|
||||||
|
setAnchorEl(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const actions = [
|
||||||
|
/*{ icon: <SvgIcon><PencilIcon /></SvgIcon>, name: 'Alias', onClickEvent: () => {
|
||||||
|
console.log("edit device alias")
|
||||||
|
// setDeviceToBeChanged(selected.indexOf(true))
|
||||||
|
// setDeviceAlias(orders[selected.indexOf(true)].Alias)
|
||||||
|
// setShowSetDeviceAlias(true)
|
||||||
|
} },*/
|
||||||
|
//{ icon: <SvgIcon><TagIcon /></SvgIcon>, name: 'Label' },
|
||||||
|
//{ icon: <SvgIcon><ShareIcon /></SvgIcon>, name: 'Share' },
|
||||||
|
{ icon: <SvgIcon><TrashIcon /></SvgIcon>, name: 'Remove' },
|
||||||
|
{ icon: <SvgIcon><CommandLineIcon /></SvgIcon>, name: 'Action' },
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getColumns()
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
if (auth.user.token) {
|
if (auth.user.token) {
|
||||||
console.log("auth.user.token =", auth.user.token)
|
console.log("auth.user.token =", auth.user.token)
|
||||||
}else{
|
} else {
|
||||||
auth.user.token = localStorage.getItem("token")
|
auth.user.token = localStorage.getItem("token")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,32 +219,102 @@ const Page = () => {
|
||||||
redirect: 'follow'
|
redirect: 'follow'
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device`, requestOptions)
|
let status;
|
||||||
|
|
||||||
|
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device?statusOrder=${statusOrder}&page_number=${page}&page_size=${rowsPerPage}&vendor=${filtersList["vendor"]}&version=${filtersList["version"]}&alias=${filtersList["alias"]}&type=${filtersList["type"]}&status=${filtersList["status"]}&model=${filtersList["model"]}`, requestOptions)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.status === 401)
|
if (response.status === 401){
|
||||||
router.push("/auth/login")
|
router.push("/auth/login")
|
||||||
|
}
|
||||||
|
status = response.status
|
||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then(json => {
|
.then(json => {
|
||||||
|
if (status == 404) {
|
||||||
|
console.log("device not found")
|
||||||
|
setLoading(false)
|
||||||
|
setDeviceFound(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log("Status:", status)
|
||||||
setPages(json.pages + 1)
|
setPages(json.pages + 1)
|
||||||
setPage(json.page +1)
|
setPage(json.page + 1)
|
||||||
|
setTotal(json.total)
|
||||||
setDevices(json.devices)
|
setDevices(json.devices)
|
||||||
|
setSelected(new Array(json.devices.length).fill(false))
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
return setDeviceFound(true)
|
return setDeviceFound(true)
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
return console.error('Error:', error)
|
return console.error('Error:', error)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/filterOptions`, requestOptions)
|
||||||
|
.then(response => {
|
||||||
|
if (response.status === 401)
|
||||||
|
router.push("/auth/login")
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then(json => {
|
||||||
|
return setFilterOptions(json)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
return console.error('Error:', error)
|
||||||
|
});
|
||||||
|
|
||||||
}, [auth.user]);
|
}, [auth.user]);
|
||||||
|
|
||||||
const handleChangePage = (event, value) => {
|
const setNewDeviceAlias = async (alias, sn) => {
|
||||||
console.log("new page: ", value)
|
var myHeaders = new Headers();
|
||||||
setPage(value)
|
myHeaders.append("Content-Type", "application/json");
|
||||||
fetchDevicePerPage(value)
|
myHeaders.append("Authorization", localStorage.getItem("token"));
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: myHeaders,
|
||||||
|
body: alias,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = await fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device/alias?id=${sn}`, requestOptions)
|
||||||
|
console.log("result:", result)
|
||||||
|
if (result.status === 401) {
|
||||||
|
router.push("/auth/login")
|
||||||
|
} else if (result.status != 200) {
|
||||||
|
console.log("Status:", result.status)
|
||||||
|
let content = await result.json()
|
||||||
|
console.log("Message:", content)
|
||||||
|
setShowSetDeviceAlias(false)
|
||||||
|
setDeviceAlias(null)
|
||||||
|
setDeviceToBeChanged(null)
|
||||||
|
} else {
|
||||||
|
let content = await result.json()
|
||||||
|
console.log("set alias result:", content)
|
||||||
|
setShowSetDeviceAlias(false)
|
||||||
|
setDeviceAlias(null)
|
||||||
|
orders[deviceToBeChanged].Alias = alias
|
||||||
|
setDeviceToBeChanged(null)
|
||||||
|
}
|
||||||
|
// .then(response => {
|
||||||
|
// if (response.status === 401) {
|
||||||
|
// router.push("/auth/login")
|
||||||
|
// }
|
||||||
|
// return response.json()
|
||||||
|
// })
|
||||||
|
// .then(result => {
|
||||||
|
// console.log("alias result:", result)
|
||||||
|
// setShowSetDeviceAlias(false)
|
||||||
|
// setDeviceAlias(null)
|
||||||
|
// })
|
||||||
|
// .catch(error => {
|
||||||
|
// console.log('error:', error)
|
||||||
|
// setShowSetDeviceAlias(false)
|
||||||
|
// setDeviceAlias(null)
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchDevicePerPage = async (p) => {
|
const fetchDevicePerPage = async (p, s, localFilterList, page_size) => {
|
||||||
setLoading(true)
|
//setLoading(true)
|
||||||
|
|
||||||
var myHeaders = new Headers();
|
var myHeaders = new Headers();
|
||||||
myHeaders.append("Content-Type", "application/json");
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
|
@ -83,18 +326,29 @@ const Page = () => {
|
||||||
redirect: 'follow'
|
redirect: 'follow'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (localFilterList == undefined) {
|
||||||
|
localFilterList = filtersList
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page_size == undefined) {
|
||||||
|
page_size = rowsPerPage
|
||||||
|
}
|
||||||
|
|
||||||
p = p - 1
|
p = p - 1
|
||||||
p = p.toString()
|
p = p.toString()
|
||||||
|
|
||||||
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device?page_number=+${p}`, requestOptions)
|
fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device?page_number=${p}&page_size=${page_size}&statusOrder=${s}&vendor=${localFilterList["vendor"]}&version=${localFilterList["version"]}&alias=${localFilterList["alias"]}&type=${localFilterList["type"]}&status=${localFilterList["status"]}&model=${localFilterList["model"]}`, requestOptions)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.status === 401)
|
if (response.status === 401)
|
||||||
router.push("/auth/login")
|
router.push("/auth/login")
|
||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then(json => {
|
.then(json => {
|
||||||
|
setTotal(json.total)
|
||||||
setDevices(json.devices)
|
setDevices(json.devices)
|
||||||
setLoading(false)
|
setPages(json.pages + 1)
|
||||||
|
setPage(json.page + 1)
|
||||||
|
//setLoading(false)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
|
|
@ -115,8 +369,8 @@ const Page = () => {
|
||||||
redirect: 'follow'
|
redirect: 'follow'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id == ""){
|
if (id == "") {
|
||||||
return fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device`, requestOptions)
|
return fetch(`${process.env.NEXT_PUBLIC_REST_ENDPOINT || ""}/api/device?vendor=${filtersList["vendor"]}&version=${filtersList["version"]}&alias=${filtersList["alias"]}&type=${filtersList["type"]}&status=${filtersList["status"]}&model=${filtersList["model"]}`, requestOptions)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.status === 401)
|
if (response.status === 401)
|
||||||
router.push("/auth/login")
|
router.push("/auth/login")
|
||||||
|
|
@ -124,7 +378,8 @@ const Page = () => {
|
||||||
})
|
})
|
||||||
.then(json => {
|
.then(json => {
|
||||||
setPages(json.pages + 1)
|
setPages(json.pages + 1)
|
||||||
setPage(json.page)
|
setPage(json.page + 1)
|
||||||
|
setTotal(json.total)
|
||||||
setDevices(json.devices)
|
setDevices(json.devices)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
return setDeviceFound(true)
|
return setDeviceFound(true)
|
||||||
|
|
@ -138,18 +393,19 @@ const Page = () => {
|
||||||
if (response.status === 401)
|
if (response.status === 401)
|
||||||
router.push("/auth/login")
|
router.push("/auth/login")
|
||||||
let json = await response.json()
|
let json = await response.json()
|
||||||
if (json.SN != undefined){
|
if (json.SN != undefined) {
|
||||||
setDevices([json])
|
setDevices([json])
|
||||||
|
setTotal(1)
|
||||||
setDeviceFound(true)
|
setDeviceFound(true)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
setPages(1)
|
setPages(1)
|
||||||
setPage(1)
|
setPage(1)
|
||||||
}else{
|
} else {
|
||||||
setDeviceFound(false)
|
setDeviceFound(false)
|
||||||
setDevices([])
|
setDevices(null)
|
||||||
setPages(1)
|
setTotal(null)
|
||||||
setPage(1)
|
setPages(null)
|
||||||
setLoading(false)
|
setPage(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -166,16 +422,16 @@ const Page = () => {
|
||||||
component="main"
|
component="main"
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
py: 8,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Container maxWidth="xl" >
|
<Container maxWidth="xl" >
|
||||||
<Stack spacing={3}>
|
<Stack spacing={3}>
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
|
spacing={2}
|
||||||
|
py={1}
|
||||||
>
|
>
|
||||||
<Grid xs={8}>
|
<Grid xs={4} item>
|
||||||
</Grid>
|
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
xs={4}
|
xs={4}
|
||||||
defaultValue=""
|
defaultValue=""
|
||||||
|
|
@ -200,40 +456,509 @@ const Page = () => {
|
||||||
sx={{ maxWidth: 500 }}
|
sx={{ maxWidth: 500 }}
|
||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
{deviceFound ?
|
<Grid xs={4} item>
|
||||||
( !Loading ?
|
<Button
|
||||||
<OverviewLatestOrders
|
sx={{ backgroundColor: "rgba(48, 109, 111, 0.04)" }}
|
||||||
orders={devices}
|
onClick={() => { setShowFilter(true) }}
|
||||||
sx={{ height: '100%' }}
|
>
|
||||||
/> : <CircularProgress></CircularProgress>
|
<SvgIcon>
|
||||||
)
|
<FunnelIcon />
|
||||||
:
|
</SvgIcon>
|
||||||
<Box
|
</Button>
|
||||||
sx={{
|
{Object.keys(filtersList).map((key) => (
|
||||||
display: 'flex',
|
(filtersList[key] &&
|
||||||
justifyContent: 'center'
|
<Chip label={`${key} : ${filtersList[key]}`} sx={{ml:1, mt:1}} onDelete={()=>{
|
||||||
|
setFiltersList({ ...filtersList, [key]: "" })
|
||||||
|
setNewFiltersList({ ...newFiltersList, [key]: ""})
|
||||||
|
fetchDevicePerPage(1, statusOrder, { ...filtersList, [key]: "" })
|
||||||
|
}}/>)
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<div style={{ display: "flex", justifyContent: "flex-end" }}>
|
||||||
|
<Button
|
||||||
|
id="basic-button"
|
||||||
|
aria-controls={open ? 'basic-menu' : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={open ? 'true' : undefined}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<SvgIcon>
|
||||||
|
<ViewColumnsIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Button>
|
||||||
|
<Menu
|
||||||
|
id="basic-menu"
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
MenuListProps={{
|
||||||
|
'aria-labelledby': 'basic-button',
|
||||||
|
}}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "center"
|
||||||
|
}}
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: "top",
|
||||||
|
horizontal: "center"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p>Device Not Found</p>
|
<MenuItem dense onClick={() => changeColumn("sn")}><Checkbox checked={columns["sn"]} /*onChange={()=>changeColumn("sn")}*/ /><ListItemText primary="Serial Number" /></MenuItem>
|
||||||
</Box>
|
<MenuItem dense onClick={() => changeColumn("alias")}><Checkbox checked={columns["alias"]} /*onChange={()=>changeColumn("alias")}*/ /><ListItemText primary="Alias" /></MenuItem>
|
||||||
|
<MenuItem dense onClick={() => changeColumn("model")}><Checkbox checked={columns["model"]} /*onChange={() => changeColumn("model")}*/ /><ListItemText primary="Model" /></MenuItem>
|
||||||
|
<MenuItem dense onClick={() => changeColumn("vendor")}><Checkbox checked={columns["vendor"]} /*onChange={() => changeColumn("vendor")}*/ /><ListItemText primary="Vendor" /></MenuItem>
|
||||||
|
<MenuItem dense onClick={() => changeColumn("version")}><Checkbox checked={columns["version"]} /*onChange={() => changeColumn("version")}*/ /><ListItemText primary="Version" /></MenuItem>
|
||||||
|
<MenuItem dense onClick={() => changeColumn("status")}><Checkbox checked={columns["status"]} /*onChange={() => changeColumn("status")}*/ /><ListItemText primary="Status" /></MenuItem>
|
||||||
|
<MenuItem dense onClick={() => changeColumn("actions")}><Checkbox checked={columns["actions"]} /*onChange={() => changeColumn("actions")}*/ /><ListItemText primary="Actions" /></MenuItem>
|
||||||
|
{/* <MenuItem dense onClick={() => changeColumn("label")}><Checkbox checked={columns["label"]} /><ListItemText primary="Labels" /></MenuItem> */}
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Card sx={{ height: "100%" }}>
|
||||||
|
<CardHeader title="Devices" />
|
||||||
|
<Scrollbar sx={{ flexGrow: 1 }}>
|
||||||
|
<Box sx={{ minWidth: 800 }}>
|
||||||
|
<TableContainer sx={{ maxHeight: 600 }}>
|
||||||
|
<Table stickyHeader>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
{/* <TableCell align="center">
|
||||||
|
<Checkbox
|
||||||
|
size="small"
|
||||||
|
style={{ margin: 0, padding: 0, color: theme.palette.primary.lightest }}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelected(new Array(devices.length).fill(e.target.checked))
|
||||||
|
//console.log("selected:", selected)
|
||||||
|
setSelectAll(e.target.checked)
|
||||||
|
}}
|
||||||
|
checked={selectAll}
|
||||||
|
/>
|
||||||
|
</TableCell> */}
|
||||||
|
{columns["sn"] && <TableCell align="center">
|
||||||
|
Serial Number
|
||||||
|
</TableCell>}
|
||||||
|
{columns["alias"] && <TableCell>
|
||||||
|
Alias
|
||||||
|
</TableCell>}
|
||||||
|
{
|
||||||
|
columns["label"] && <TableCell >
|
||||||
|
Labels
|
||||||
|
</TableCell>
|
||||||
}
|
}
|
||||||
<Box
|
{columns["model"] && <TableCell>
|
||||||
sx={{
|
Model
|
||||||
display: 'flex',
|
</TableCell>}
|
||||||
justifyContent: 'center'
|
{columns["vendor"] && <TableCell>
|
||||||
|
Vendor
|
||||||
|
</TableCell>}
|
||||||
|
{columns["version"] && <TableCell>
|
||||||
|
Version
|
||||||
|
</TableCell>}
|
||||||
|
{columns["status"] &&
|
||||||
|
<TableCell>
|
||||||
|
{/*//TODO: create function to fetch devices by status order*/}
|
||||||
|
<Tooltip title="Change status display order" placement="top">
|
||||||
|
<span style={{ cursor: "pointer" }} onClick={() => {
|
||||||
|
if (statusOrder == "asc") {
|
||||||
|
setStatusOrder("desc")
|
||||||
|
fetchDevicePerPage(page, "desc")
|
||||||
|
} else {
|
||||||
|
setStatusOrder("asc")
|
||||||
|
fetchDevicePerPage(page, "asc")
|
||||||
|
}
|
||||||
|
}}>Status ↑↓</span>
|
||||||
|
</Tooltip>
|
||||||
|
{/* <Box >
|
||||||
|
<Tooltip title="Change status display order" placement="top">
|
||||||
|
<SvgIcon fontSize='small' style={{
|
||||||
|
marginLeft: "10px",
|
||||||
|
cursor: "pointer"
|
||||||
|
}}>
|
||||||
|
<ArrowsUpDownIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</Box> */}
|
||||||
|
</TableCell>}
|
||||||
|
{columns["actions"] && <TableCell align="center">
|
||||||
|
Actions
|
||||||
|
</TableCell>}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
{!Loading ? <TableBody>
|
||||||
|
{devices && devices.map((order, index) => {
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
hover
|
||||||
|
key={order.SN}
|
||||||
|
>
|
||||||
|
{/* <TableCell align="center">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
size="small"
|
||||||
|
onChange={(e) => {
|
||||||
|
let newData = [...selected]
|
||||||
|
newData.splice(index, 1, e.target.checked);
|
||||||
|
console.log("newData:", newData)
|
||||||
|
setSelected(newData)
|
||||||
|
}}
|
||||||
|
checked={selected[index]}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</TableCell> */}
|
||||||
|
{columns["sn"] && <TableCell align="center">
|
||||||
|
{order.SN}
|
||||||
|
</TableCell>}
|
||||||
|
{columns["alias"] && <TableCell>
|
||||||
|
{order.Alias}
|
||||||
|
</TableCell>}
|
||||||
|
{
|
||||||
|
columns["label"] && <TableCell>
|
||||||
|
<Chip label="Teste1" />
|
||||||
|
<Chip label="Teste2" />
|
||||||
|
<Chip label="Teste3" />
|
||||||
|
</TableCell>
|
||||||
|
}
|
||||||
|
{columns["model"] && <TableCell>
|
||||||
|
{order.Model || order.ProductClass}
|
||||||
|
</TableCell>}
|
||||||
|
{columns["vendor"] && <TableCell>
|
||||||
|
{order.Vendor}
|
||||||
|
</TableCell>}
|
||||||
|
{columns["version"] && <TableCell>
|
||||||
|
{order.Version}
|
||||||
|
</TableCell>}
|
||||||
|
{columns["status"] && <TableCell>
|
||||||
|
{/* <SeverityPill color={statusMap[order.Status]}>
|
||||||
|
{status(order.Status)}
|
||||||
|
</SeverityPill> */}
|
||||||
|
<Chip label={status(order.Status)} color={statusMap[order.Status]} />
|
||||||
|
</TableCell>}
|
||||||
|
{columns["actions"] && <TableCell align="center">
|
||||||
|
{order.Status == 2 &&
|
||||||
|
<Tooltip title="Access the device">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
router.push("devices/" + getDeviceProtocol(order) + "/" + order.SN)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{pages ? <Pagination
|
<SvgIcon
|
||||||
|
fontSize="small"
|
||||||
|
sx={{ cursor: 'pointer' }}
|
||||||
|
>
|
||||||
|
<ArrowTopRightOnSquareIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>}
|
||||||
|
{/* <Tooltip title="Edit the device alias">
|
||||||
|
<Button
|
||||||
|
onClick={()=>{
|
||||||
|
setDeviceToBeChanged(index)
|
||||||
|
setDeviceAlias(order.Alias)
|
||||||
|
setShowSetDeviceAlias(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
fontSize="small"
|
||||||
|
sx={{cursor: 'pointer'}}
|
||||||
|
>
|
||||||
|
<PencilIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title="Edit device labels">
|
||||||
|
<Button
|
||||||
|
onClick={()=>{
|
||||||
|
setDeviceToBeChanged(index)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
fontSize="small"
|
||||||
|
sx={{cursor: 'pointer'}}
|
||||||
|
>
|
||||||
|
<TagIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
</Button>
|
||||||
|
</Tooltip> */}
|
||||||
|
</TableCell>}
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TableBody>:
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={7} align="center">
|
||||||
|
{
|
||||||
|
deviceFound ? <CircularProgress/> : "No device found"
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
}
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
{(pages.lenght > 0) && total && <TablePagination
|
||||||
|
rowsPerPageOptions={rowsPerPageOptions}
|
||||||
|
component="div"
|
||||||
|
count={total}
|
||||||
|
rowsPerPage={rowsPerPage}
|
||||||
|
page={page-1}
|
||||||
|
onPageChange={(e, newPage)=>{
|
||||||
|
setPage(newPage+1)
|
||||||
|
fetchDevicePerPage(newPage+1, statusOrder, filtersList, rowsPerPage)
|
||||||
|
}}
|
||||||
|
onRowsPerPageChange={(e)=>{
|
||||||
|
setRowsPerPage(e.target.value)
|
||||||
|
setPage(1)
|
||||||
|
fetchDevicePerPage(1, statusOrder, filtersList, e.target.value)
|
||||||
|
}}
|
||||||
|
/>}
|
||||||
|
</Box>
|
||||||
|
</Scrollbar>
|
||||||
|
</Card>
|
||||||
|
{showSetDeviceAlias &&
|
||||||
|
<Dialog open={showSetDeviceAlias}>
|
||||||
|
<DialogContent>
|
||||||
|
<InputLabel>Device Alias</InputLabel>
|
||||||
|
<Input value={deviceAlias} onChange={(e) => { setDeviceAlias(e.target.value) }}
|
||||||
|
onKeyUp={e => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
setNewDeviceAlias(deviceAlias, orders[deviceToBeChanged].SN)
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
</Input>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => {
|
||||||
|
setShowSetDeviceAlias(false)
|
||||||
|
setDeviceAlias(null)
|
||||||
|
setDeviceToBeChanged(null)
|
||||||
|
}}>Cancel</Button>
|
||||||
|
<Button onClick={() => {
|
||||||
|
setNewDeviceAlias(deviceAlias, orders[deviceToBeChanged].SN)
|
||||||
|
}}>Save</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>}
|
||||||
|
</div>
|
||||||
|
{/* / :
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: 'flex',
|
||||||
|
// justifyContent: 'center'
|
||||||
|
// }}
|
||||||
|
// >
|
||||||
|
// <p>Device Not Found</p>
|
||||||
|
// </Box>
|
||||||
|
// }
|
||||||
|
// <Box
|
||||||
|
// sx={{
|
||||||
|
// display: 'flex',
|
||||||
|
// justifyContent: 'center'
|
||||||
|
// }}
|
||||||
|
// > */}
|
||||||
|
{/* {pages ? <Pagination
|
||||||
count={pages}
|
count={pages}
|
||||||
size="small"
|
size="small"
|
||||||
page={page}
|
page={page}
|
||||||
onChange={handleChangePage}
|
onChange={handleChangePage}
|
||||||
/>: null}
|
/> : null} */}
|
||||||
{/* //TODO: show loading */}
|
{/* //TODO: show loading */}
|
||||||
</Box>
|
{/* </Box> */}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
|
<SpeedDial
|
||||||
|
ariaLabel="SpeedDial basic example"
|
||||||
|
hidden={!showSpeedDial}
|
||||||
|
//FabProps={{size: 'small'}}
|
||||||
|
sx={{ position: 'fixed', bottom: 16, right: 16, }}
|
||||||
|
icon={<SpeedDialIcon icon={<SvgIcon ><ChevronDownIcon /></SvgIcon>}
|
||||||
|
openIcon={<SvgIcon><ChevronUpIcon /></SvgIcon>} />}
|
||||||
|
>
|
||||||
|
{actions.map((action) => (
|
||||||
|
<SpeedDialAction
|
||||||
|
key={action.name}
|
||||||
|
icon={action.icon}
|
||||||
|
tooltipTitle={action.name}
|
||||||
|
onClick={action.onClickEvent}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SpeedDial>
|
||||||
|
<Dialog open={showFilter} fullWidth>
|
||||||
|
<DialogTitle>
|
||||||
|
<SvgIcon style={{ marginRight: "10px", marginBottom: "-5px" }}>
|
||||||
|
<FunnelIcon />
|
||||||
|
</SvgIcon>
|
||||||
|
Filter
|
||||||
|
</DialogTitle>
|
||||||
|
{filterOptions && <DialogContent>
|
||||||
|
<Stack spacing={2} marginTop={1} minWidth={400}>
|
||||||
|
<Stack
|
||||||
|
spacing={2}
|
||||||
|
direction={'row'}
|
||||||
|
>
|
||||||
|
<TextField label="Alias" variant="filled" sx={{minWidth:"48%"}}
|
||||||
|
value={newFiltersList["alias"]}
|
||||||
|
onChange={(e) => setNewFiltersList({ ...newFiltersList, "alias": e.target.value })}
|
||||||
|
/>
|
||||||
|
<FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Type</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={newFiltersList["type"]}
|
||||||
|
onChange={(e) => setNewFiltersList({ ...newFiltersList, "type": e.target.value })}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{
|
||||||
|
filterOptions.productClasses.map((v) => {
|
||||||
|
return <MenuItem value={v}>{v}</MenuItem>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
{/* <TextField label="Serial Number" variant="filled" /> */}
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
spacing={2}
|
||||||
|
direction={'row'}
|
||||||
|
>
|
||||||
|
<FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Vendor</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={newFiltersList["vendor"]}
|
||||||
|
onChange={(e) => setNewFiltersList({ ...newFiltersList, "vendor": e.target.value })}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{
|
||||||
|
filterOptions.vendors.map((v) => {
|
||||||
|
return <MenuItem value={v}>{v}</MenuItem>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Version</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={newFiltersList["version"]}
|
||||||
|
onChange={(e) => setNewFiltersList({ ...newFiltersList, "version": e.target.value })}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{
|
||||||
|
filterOptions.versions.map((v) => {
|
||||||
|
return <MenuItem value={v}>{v}</MenuItem>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Stack>
|
||||||
|
<Stack
|
||||||
|
spacing={2}
|
||||||
|
direction={'row'}
|
||||||
|
>
|
||||||
|
<FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Status</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
newFiltersList["status"]
|
||||||
|
// ()=>{
|
||||||
|
// if (newFiltersList["status"] == 2) {
|
||||||
|
// return "online"
|
||||||
|
// }else if (newFiltersList["status"] == 0) {
|
||||||
|
// return "offline"
|
||||||
|
// }else {
|
||||||
|
// return ""
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
onChange={(e) => {
|
||||||
|
// let value = 0
|
||||||
|
// if (e.target.value == "online") {
|
||||||
|
// value = 2
|
||||||
|
// }else if (e.target.value == "offline") {
|
||||||
|
// value = 0
|
||||||
|
// }else {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
setNewFiltersList({ ...newFiltersList, "status": e.target.value })
|
||||||
|
}}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<MenuItem value={"2"}>Online</MenuItem>
|
||||||
|
<MenuItem value={"0"}>Offline</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
{/* <FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Label</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={labelFilter}
|
||||||
|
onChange={(e)=> setLabelFilter(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{
|
||||||
|
filterOptions.labels.map((v) => {
|
||||||
|
return <MenuItem value={v}>{v}</MenuItem>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl> */}
|
||||||
|
<FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Model</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={newFiltersList["model"]}
|
||||||
|
onChange={(e) => setNewFiltersList({ ...newFiltersList, "model": e.target.value })}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{
|
||||||
|
filterOptions.models.map((v) => {
|
||||||
|
return <MenuItem value={v}>{v}</MenuItem>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Stack>
|
||||||
|
{/* <Stack
|
||||||
|
spacing={2}
|
||||||
|
direction={'row'}
|
||||||
|
>
|
||||||
|
<FormControl variant="filled" sx={{ minWidth: "48%" }}>
|
||||||
|
<InputLabel>Type</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={typeFilter}
|
||||||
|
onChange={(e) => setTypeFilter(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{
|
||||||
|
filterOptions.productClasses.map((v) => {
|
||||||
|
return <MenuItem value={v}>{v}</MenuItem>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</Stack> */}
|
||||||
|
</Stack>
|
||||||
|
</DialogContent>}
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => {
|
||||||
|
|
||||||
|
setNewFiltersList(filtersList)
|
||||||
|
//cleanFilters()
|
||||||
|
setShowFilter(false)
|
||||||
|
//if (!objsEqual(filtersList,defaultFiltersList)) {
|
||||||
|
/* fetchDevicePerPage(1, statusOrder, defaultFiltersList)
|
||||||
|
}
|
||||||
|
if (!objsEqual(filtersList, newFiltersList)){
|
||||||
|
|
||||||
|
} */
|
||||||
|
|
||||||
|
//setFiltersList(defaultFiltersList)
|
||||||
|
}}>Cancel</Button>
|
||||||
|
<Button onClick={() => {
|
||||||
|
setFiltersList(newFiltersList)
|
||||||
|
setShowFilter(false)
|
||||||
|
console.log("filters list:", filtersList)
|
||||||
|
fetchDevicePerPage(1, statusOrder, newFiltersList)
|
||||||
|
}}>Apply</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user