Adding testing app during initial load
This commit is contained in:
@@ -36,6 +36,10 @@ import (
|
|||||||
"github.com/google/go-github/v28/github"
|
"github.com/google/go-github/v28/github"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
|
"github.com/go-git/go-billy/v5/memfs"
|
||||||
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/go-git/go-git/v5/storage/memory"
|
||||||
|
|
||||||
// Random
|
// Random
|
||||||
xj "github.com/basgys/goxml2json"
|
xj "github.com/basgys/goxml2json"
|
||||||
gyaml "github.com/ghodss/yaml"
|
gyaml "github.com/ghodss/yaml"
|
||||||
@@ -5637,6 +5641,30 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Getting apps to see if we should initialize a test
|
||||||
|
workflowapps, err := getAllWorkflowApps(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed getting apps: %s", err)
|
||||||
|
} else if err == nil && len(workflowapps) == 0 {
|
||||||
|
log.Printf("Apps: loading TEST")
|
||||||
|
fs := memfs.New()
|
||||||
|
storer := memory.NewStorage()
|
||||||
|
r, err := git.Clone(storer, fs, &git.CloneOptions{
|
||||||
|
URL: "https://github.com/frikky/shuffle-apps",
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed loading repo into memory: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := fs.ReadDir("")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("FAiled reading folder: %s", err)
|
||||||
|
}
|
||||||
|
_ = r
|
||||||
|
iterateAppGithubFolders(fs, dir, "", "testing")
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("Finished INIT")
|
log.Printf("Finished INIT")
|
||||||
|
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
@@ -5673,6 +5701,7 @@ func init() {
|
|||||||
|
|
||||||
// Apps
|
// Apps
|
||||||
r.HandleFunc("/api/v1/apps/get_existing", loadExistingApps).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/get_existing", loadExistingApps).Methods("GET", "OPTIONS")
|
||||||
|
r.HandleFunc("/api/v1/apps/get_existing/{appname}", loadExistingApps).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/validate", validateAppInput).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/{appId}", deleteWorkflowApp).Methods("DELETE", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/{appId}", deleteWorkflowApp).Methods("DELETE", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/apps/{appId}/config", getWorkflowAppConfig).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/apps/{appId}/config", getWorkflowAppConfig).Methods("GET", "OPTIONS")
|
||||||
|
|||||||
@@ -2608,7 +2608,12 @@ func getWorkflowApps(resp http.ResponseWriter, request *http.Request) {
|
|||||||
// Just need to be logged in
|
// Just need to be logged in
|
||||||
// FIXME - need to be logged in?
|
// FIXME - need to be logged in?
|
||||||
user, userErr := handleApiAuthentication(resp, request)
|
user, userErr := handleApiAuthentication(resp, request)
|
||||||
_ = userErr
|
if userErr != nil {
|
||||||
|
log.Printf("Api authentication failed in get all apps: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//if item, err := memcache.Get(ctx, memcacheName); err == memcache.ErrCacheMiss {
|
//if item, err := memcache.Get(ctx, memcacheName); err == memcache.ErrCacheMiss {
|
||||||
// // Not in cache
|
// // Not in cache
|
||||||
@@ -2986,27 +2991,33 @@ func loadExistingApps(resp http.ResponseWriter, request *http.Request) {
|
|||||||
log.Printf("FAiled reading folder: %s", err)
|
log.Printf("FAiled reading folder: %s", err)
|
||||||
}
|
}
|
||||||
_ = r
|
_ = r
|
||||||
iterateAppGithubFolders(fs, dir, "")
|
iterateAppGithubFolders(fs, dir, "", "")
|
||||||
|
|
||||||
resp.WriteHeader(200)
|
resp.WriteHeader(200)
|
||||||
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra string) error {
|
// Onlyname is used to
|
||||||
|
func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra string, onlyname string) error {
|
||||||
var err error
|
var err error
|
||||||
runUpload := false
|
runUpload := false
|
||||||
for _, file := range dir {
|
for _, file := range dir {
|
||||||
|
if len(onlyname) > 0 && file.Name() != onlyname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Folder?
|
// Folder?
|
||||||
switch mode := file.Mode(); {
|
switch mode := file.Mode(); {
|
||||||
case mode.IsDir():
|
case mode.IsDir():
|
||||||
tmpExtra := fmt.Sprintf("%s%s/", extra, file.Name())
|
tmpExtra := fmt.Sprintf("%s%s/", extra, file.Name())
|
||||||
dir, err := fs.ReadDir(tmpExtra)
|
dir, err := fs.ReadDir(tmpExtra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Failed to read dir: %s", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go routine? Hmm, this can be super quick I guess
|
// Go routine? Hmm, this can be super quick I guess
|
||||||
err = iterateAppGithubFolders(fs, dir, tmpExtra)
|
err = iterateAppGithubFolders(fs, dir, tmpExtra, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-16
@@ -163,6 +163,8 @@ const AppCreator = (props) => {
|
|||||||
alert.error("Failed to verify")
|
alert.error("Failed to verify")
|
||||||
} else {
|
} else {
|
||||||
const data = JSON.parse(responseJson.body)
|
const data = JSON.parse(responseJson.body)
|
||||||
|
console.log("LOADED IMAGE: ", data.image)
|
||||||
|
setFileBase64(data.image)
|
||||||
parseOpenapiData(data)
|
parseOpenapiData(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -338,17 +340,17 @@ const AppCreator = (props) => {
|
|||||||
"description": description,
|
"description": description,
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
},
|
},
|
||||||
"servers": [{"url": baseUrl}],
|
"servers": [{"url": baseUrl}],
|
||||||
"host": host,
|
"host": host,
|
||||||
"basePath": basePath,
|
"basePath": basePath,
|
||||||
"schemes": schemes,
|
"schemes": schemes,
|
||||||
"paths": {},
|
"paths": {},
|
||||||
"editing": isEditing,
|
"editing": isEditing,
|
||||||
"components": {
|
"components": {
|
||||||
"securitySchemes": {},
|
"securitySchemes": {},
|
||||||
},
|
},
|
||||||
"image": fileBase64,
|
"image": fileBase64,
|
||||||
"id": props.match.params.appid,
|
"id": props.match.params.appid,
|
||||||
"securityDefinitions": {},
|
"securityDefinitions": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,9 +364,10 @@ const AppCreator = (props) => {
|
|||||||
data.info["contact"] = contact
|
data.info["contact"] = contact
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("LOADED IMAGE: ", data.image)
|
||||||
|
|
||||||
for (var key in actions) {
|
for (var key in actions) {
|
||||||
const item = actions[key]
|
const item = actions[key]
|
||||||
console.log(item)
|
|
||||||
if (item.errors.length > 0) {
|
if (item.errors.length > 0) {
|
||||||
alert.error("Saving with error in action "+item.name)
|
alert.error("Saving with error in action "+item.name)
|
||||||
}
|
}
|
||||||
@@ -373,8 +376,6 @@ const AppCreator = (props) => {
|
|||||||
item.name = item.description
|
item.name = item.description
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(data.paths)
|
|
||||||
console.log(item)
|
|
||||||
if (data.paths[item.url] === null || data.paths[item.url] === undefined) {
|
if (data.paths[item.url] === null || data.paths[item.url] === undefined) {
|
||||||
data.paths[item.url] = {}
|
data.paths[item.url] = {}
|
||||||
}
|
}
|
||||||
@@ -456,7 +457,6 @@ const AppCreator = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(data)
|
|
||||||
fetch(globalUrl+"/api/v1/verify_openapi", {
|
fetch(globalUrl+"/api/v1/verify_openapi", {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -475,7 +475,6 @@ const AppCreator = (props) => {
|
|||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
console.log(responseJson)
|
|
||||||
if (!responseJson.success) {
|
if (!responseJson.success) {
|
||||||
setErrorCode(responseJson.reason)
|
setErrorCode(responseJson.reason)
|
||||||
alert.error("Failed to verify: ")
|
alert.error("Failed to verify: ")
|
||||||
@@ -1051,13 +1050,12 @@ const AppCreator = (props) => {
|
|||||||
var image = ""
|
var image = ""
|
||||||
const editHeaderImage = (event) => {
|
const editHeaderImage = (event) => {
|
||||||
const file = event.target.value
|
const file = event.target.value
|
||||||
console.log(file)
|
|
||||||
const actualFile = event.target.files[0]
|
const actualFile = event.target.files[0]
|
||||||
const fileObject = URL.createObjectURL(actualFile)
|
const fileObject = URL.createObjectURL(actualFile)
|
||||||
setFile(fileObject)
|
setFile(fileObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file !== "" && fileBase64 === "") {
|
if (file !== "") {
|
||||||
const img = document.getElementById('logo')
|
const img = document.getElementById('logo')
|
||||||
var canvas = document.createElement('canvas')
|
var canvas = document.createElement('canvas')
|
||||||
var ctx = canvas.getContext('2d')
|
var ctx = canvas.getContext('2d')
|
||||||
@@ -1067,7 +1065,9 @@ const AppCreator = (props) => {
|
|||||||
ctx.drawImage(img, 0, 0)
|
ctx.drawImage(img, 0, 0)
|
||||||
const canvasUrl = canvas.toDataURL()
|
const canvasUrl = canvas.toDataURL()
|
||||||
console.log(canvasUrl)
|
console.log(canvasUrl)
|
||||||
setFileBase64(canvasUrl)
|
if (canvasUrl !== fileBase64) {
|
||||||
|
setFileBase64(canvasUrl)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log(img.width)
|
//console.log(img.width)
|
||||||
@@ -1082,7 +1082,8 @@ const AppCreator = (props) => {
|
|||||||
// </div> :
|
// </div> :
|
||||||
// <img src={file} id="logo" style={{width: "100%", height: "100%"}} />
|
// <img src={file} id="logo" style={{width: "100%", height: "100%"}} />
|
||||||
|
|
||||||
const imageInfo = <img src={file} alt="Click to upload an image" id="logo" style={{width: "100%", height: "100%"}} />
|
const imageData = file.length > 0 ? file : fileBase64
|
||||||
|
const imageInfo = <img src={imageData} alt="Click to upload an image" id="logo" style={{width: 174, height: 174}} />
|
||||||
|
|
||||||
// Random names for type & autoComplete. Didn't research :^)
|
// Random names for type & autoComplete. Didn't research :^)
|
||||||
const landingpageDataBrowser =
|
const landingpageDataBrowser =
|
||||||
|
|||||||
@@ -167,9 +167,9 @@ const Apps = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var imageline = data.large_image.length === 0 ?
|
var imageline = data.large_image.length === 0 ?
|
||||||
<img alt="" style={{width: 100}} />
|
<img alt="Image missing" style={{width: 100, height: 100}} />
|
||||||
:
|
:
|
||||||
<img alt="" src={data.large_image} style={{width: 100, height: 100, objectFit: "cover"}} />
|
<img alt={data.title} src={data.large_image} style={{width: 100, height: 100, maxWidth: "100%"}} />
|
||||||
|
|
||||||
// FIXME - add label to apps, as this might be slow with A LOT of apps
|
// FIXME - add label to apps, as this might be slow with A LOT of apps
|
||||||
var newAppname = data.name
|
var newAppname = data.name
|
||||||
|
|||||||
+162
-6
@@ -43,11 +43,15 @@ const Dashboard = (props) => {
|
|||||||
const { globalUrl } = props;
|
const { globalUrl } = props;
|
||||||
const alert = useAlert()
|
const alert = useAlert()
|
||||||
const [bigChartData, setBgChartData] = useState("data1");
|
const [bigChartData, setBgChartData] = useState("data1");
|
||||||
|
const [dayAmount, setDayAmount] = useState(7);
|
||||||
const [firstRequest, setFirstRequest] = useState(true);
|
const [firstRequest, setFirstRequest] = useState(true);
|
||||||
const [stats, setStats] = useState({})
|
const [stats, setStats] = useState({})
|
||||||
const [changeme, setChangeme] = useState("")
|
const [changeme, setChangeme] = useState("")
|
||||||
|
const [statsRan, setStatsRan] = useState(false)
|
||||||
|
|
||||||
document.title = "Shuffle - dashboard"
|
document.title = "Shuffle - dashboard"
|
||||||
|
var dayGraphLabels = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]
|
||||||
|
var dayGraphData = [60, 80, 65, 130, 80, 105, 90, 130, 70, 115, 60, 130]
|
||||||
|
|
||||||
const fetchdata = (stats_id) => {
|
const fetchdata = (stats_id) => {
|
||||||
fetch(globalUrl+"/api/v1/stats/"+stats_id, {
|
fetch(globalUrl+"/api/v1/stats/"+stats_id, {
|
||||||
@@ -60,13 +64,12 @@ const Dashboard = (props) => {
|
|||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for stream results :O!")
|
console.log("Status not 200 for "+stats_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
})
|
})
|
||||||
.then((responseJson) => {
|
.then((responseJson) => {
|
||||||
console.log("DATA: ", responseJson)
|
|
||||||
stats[stats_id] = responseJson
|
stats[stats_id] = responseJson
|
||||||
setStats(stats)
|
setStats(stats)
|
||||||
// Used to force updates
|
// Used to force updates
|
||||||
@@ -77,6 +80,92 @@ const Dashboard = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let chart1_2_options = {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
backgroundColor: "#f5f5f5",
|
||||||
|
titleFontColor: "#333",
|
||||||
|
bodyFontColor: "#666",
|
||||||
|
bodySpacing: 4,
|
||||||
|
xPadding: 12,
|
||||||
|
mode: "nearest",
|
||||||
|
intersect: 0,
|
||||||
|
position: "nearest"
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
yAxes: [
|
||||||
|
{
|
||||||
|
barPercentage: 1.6,
|
||||||
|
gridLines: {
|
||||||
|
drawBorder: false,
|
||||||
|
color: "rgba(29,140,248,0.0)",
|
||||||
|
zeroLineColor: "transparent"
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
suggestedMin: 60,
|
||||||
|
suggestedMax: 125,
|
||||||
|
padding: 20,
|
||||||
|
fontColor: "#9a9a9a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
barPercentage: 1.6,
|
||||||
|
gridLines: {
|
||||||
|
drawBorder: false,
|
||||||
|
color: "rgba(29,140,248,0.1)",
|
||||||
|
zeroLineColor: "transparent"
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
padding: 20,
|
||||||
|
fontColor: "#9a9a9a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dayGraph = {
|
||||||
|
data: canvas => {
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
let gradientStroke = ctx.createLinearGradient(0, 230, 0, 50);
|
||||||
|
|
||||||
|
gradientStroke.addColorStop(1, "rgba(29,140,248,0.2)");
|
||||||
|
gradientStroke.addColorStop(0.4, "rgba(29,140,248,0.0)");
|
||||||
|
gradientStroke.addColorStop(0, "rgba(29,140,248,0)"); //blue colors
|
||||||
|
|
||||||
|
return {
|
||||||
|
labels: dayGraphLabels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "My First dataset",
|
||||||
|
fill: true,
|
||||||
|
backgroundColor: gradientStroke,
|
||||||
|
borderColor: "#1f8ef1",
|
||||||
|
borderWidth: 2,
|
||||||
|
borderDash: [],
|
||||||
|
borderDashOffset: 0.0,
|
||||||
|
pointBackgroundColor: "#1f8ef1",
|
||||||
|
pointBorderColor: "rgba(255,255,255,0)",
|
||||||
|
pointHoverBackgroundColor: "#1f8ef1",
|
||||||
|
pointBorderWidth: 20,
|
||||||
|
pointHoverRadius: 4,
|
||||||
|
pointHoverBorderWidth: 15,
|
||||||
|
pointRadius: 4,
|
||||||
|
data: dayGraphData,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: chart1_2_options,
|
||||||
|
}
|
||||||
|
|
||||||
// All these are currently tracked.
|
// All these are currently tracked.
|
||||||
const variables = [
|
const variables = [
|
||||||
"backend_executions",
|
"backend_executions",
|
||||||
@@ -107,14 +196,73 @@ const Dashboard = (props) => {
|
|||||||
callback: () => {
|
callback: () => {
|
||||||
runUpdate()
|
runUpdate()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
if (firstRequest) {
|
if (firstRequest) {
|
||||||
|
console.log("HELO")
|
||||||
setFirstRequest(false)
|
setFirstRequest(false)
|
||||||
start()
|
start()
|
||||||
runUpdate()
|
runUpdate()
|
||||||
}
|
} else if (!statsRan) {
|
||||||
|
// FIXME: Run this under runUpdate schedule?
|
||||||
|
// 1. Fix labels in dayGraphy.data
|
||||||
|
// 2. Add data to the daygraph
|
||||||
|
|
||||||
|
// Every time there's an update :)
|
||||||
|
|
||||||
|
// This should probably be done in the backend.. bleh
|
||||||
|
if (stats["workflow_executions"] !== undefined && stats["workflow_executions"] !== null && stats["workflow_executions"].data !== undefined) {
|
||||||
|
setStatsRan(true)
|
||||||
|
//console.log("NEW DATA?: ", stats)
|
||||||
|
console.log('SET WORKFLOW: ', stats["workflow_executions"])
|
||||||
|
//var curday = startDate.getDate()
|
||||||
|
|
||||||
|
// Index = what day are we on
|
||||||
|
|
||||||
|
// 0 = today
|
||||||
|
var newDayGraphLabels = []
|
||||||
|
var newDayGraphData = []
|
||||||
|
for (var i = dayAmount; i > 0; i--) {
|
||||||
|
var enddate = new Date()
|
||||||
|
enddate.setDate(-i)
|
||||||
|
enddate.setHours(23,59,59,999)
|
||||||
|
|
||||||
|
var startdate = new Date()
|
||||||
|
startdate.setDate(-i)
|
||||||
|
startdate.setHours(0,0,0,0)
|
||||||
|
|
||||||
|
var endtime = enddate.getTime()/1000
|
||||||
|
var starttime = startdate.getTime()/1000
|
||||||
|
|
||||||
|
console.log("START: ", starttime, "END: ", endtime, "Data: ", stats["workflow_executions"])
|
||||||
|
for (var key in stats["workflow_executions"].data) {
|
||||||
|
const item = stats["workflow_executions"]["data"][key]
|
||||||
|
console.log("ITEM: ", item.timestamp, endtime)
|
||||||
|
console.log(endtime-starttime)
|
||||||
|
if (endtime-starttime > endtime-item.timestamp && endtime.timestamp >= 0) {
|
||||||
|
console.log("HIT? ")
|
||||||
|
}
|
||||||
|
console.log(item.timestamp-endtime)
|
||||||
|
//console.log(item.timestamp-endtime)
|
||||||
|
break
|
||||||
|
if (item.timestamp > endtime && item.timestamp < starttime) {
|
||||||
|
if (newDayGraphData[i-1] === undefined) {
|
||||||
|
newDayGraphData[i-1] = 1
|
||||||
|
} else {
|
||||||
|
newDayGraphData[i-1] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
//break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newDayGraphLabels.push(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(newDayGraphLabels)
|
||||||
|
console.log(newDayGraphData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const newdata = Object.getOwnPropertyNames(stats).length > 0 ?
|
const newdata = Object.getOwnPropertyNames(stats).length > 0 ?
|
||||||
<div>
|
<div>
|
||||||
@@ -141,13 +289,21 @@ const Dashboard = (props) => {
|
|||||||
<div className="content">
|
<div className="content">
|
||||||
{newdata}
|
{newdata}
|
||||||
<Row>
|
<Row>
|
||||||
|
<Col xs="12">
|
||||||
|
<div className="chart-area">
|
||||||
|
<Line
|
||||||
|
data={dayGraph.data}
|
||||||
|
options={dayGraph.options}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
<Col xs="12">
|
<Col xs="12">
|
||||||
<Card className="card-chart">
|
<Card className="card-chart">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<Row>
|
<Row>
|
||||||
<Col className="text-left" sm="6">
|
<Col className="text-left" sm="6">
|
||||||
<h5 className="card-category">Total Shipments</h5>
|
<h5 className="card-category">Total Shipments</h5>
|
||||||
<CardTitle tag="h2">Performance</CardTitle>
|
<CardTitle tag="h2">Workflows</CardTitle>
|
||||||
</Col>
|
</Col>
|
||||||
<Col sm="6">
|
<Col sm="6">
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
@@ -304,4 +460,4 @@ const Dashboard = (props) => {
|
|||||||
return dataWrapper
|
return dataWrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Dashboard;
|
export default Dashboard
|
||||||
|
|||||||
Reference in New Issue
Block a user