mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
update all
This commit is contained in:
parent
1e49c9bd84
commit
270ae41104
@ -1,13 +1,15 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
const aliases = {
|
const aliases = {
|
||||||
schemas: path.resolve(__dirname, './schemas'),
|
"@antd": path.resolve(__dirname, "../../node_modules/antd"),
|
||||||
controllers: path.resolve(__dirname, "./src/controllers"),
|
"@": path.resolve(__dirname, 'src'),
|
||||||
extensions: path.resolve(__dirname, './src/extensions'),
|
schemas: path.resolve(__dirname, 'constants'),
|
||||||
|
controllers: path.resolve(__dirname, 'src/controllers'),
|
||||||
|
extensions: path.resolve(__dirname, 'src/extensions'),
|
||||||
theme: path.join(__dirname, 'src/theme'),
|
theme: path.join(__dirname, 'src/theme'),
|
||||||
locales: path.join(__dirname, 'src/locales'),
|
locales: path.join(__dirname, 'src/locales'),
|
||||||
core: path.join(__dirname, 'src/core'),
|
core: path.join(__dirname, 'src/core'),
|
||||||
pages: path.join(__dirname, 'src/pages'),
|
"@pages": path.join(__dirname, 'src/pages'),
|
||||||
components: path.join(__dirname, 'src/components'),
|
components: path.join(__dirname, 'src/components'),
|
||||||
models: path.join(__dirname, 'src/models'),
|
models: path.join(__dirname, 'src/models'),
|
||||||
}
|
}
|
||||||
@ -23,6 +25,10 @@ module.exports = (config) => {
|
|||||||
...config.resolve.alias,
|
...config.resolve.alias,
|
||||||
...aliases,
|
...aliases,
|
||||||
}
|
}
|
||||||
|
config.aliases = {
|
||||||
|
...config.resolve.alias,
|
||||||
|
...aliases,
|
||||||
|
}
|
||||||
|
|
||||||
config.css = {
|
config.css = {
|
||||||
preprocessorOptions: {
|
preprocessorOptions: {
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
import defaultTheme from "../constants/defaultTheme.json"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
defaultTheme: defaultTheme,
|
||||||
|
|
||||||
logo: {
|
logo: {
|
||||||
alt: "https://dl.ragestudio.net/branding/comty/alt/SVG/t3t3.svg"
|
alt: "https://dl.ragestudio.net/branding/comty/alt/SVG/t3t3.svg"
|
||||||
},
|
},
|
||||||
api: {
|
api: {
|
||||||
address: process.env.NODE_ENV !== 'production' ? `http://${window.location.hostname}:3000` : "https://api.amimet.es",
|
address: process.env.NODE_ENV !== 'production' ? `http://${window.location.hostname}:3000` : "https://api.amimet.es",
|
||||||
},
|
},
|
||||||
theme: {
|
|
||||||
"primary-color": "#32b7bb",
|
|
||||||
},
|
|
||||||
app: {
|
app: {
|
||||||
siteName: 'Comty™',
|
siteName: 'Comty™',
|
||||||
copyright: 'RageStudio©',
|
copyright: 'RageStudio©',
|
||||||
|
10
packages/app/constants/defaultSidebar.json
Normal file
10
packages/app/constants/defaultSidebar.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[
|
||||||
|
"main",
|
||||||
|
"fabric",
|
||||||
|
"workloads",
|
||||||
|
"users",
|
||||||
|
"geo",
|
||||||
|
"vault",
|
||||||
|
"statistics",
|
||||||
|
"reports"
|
||||||
|
]
|
30
packages/app/constants/defaultTheme.json
Normal file
30
packages/app/constants/defaultTheme.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"staticVars": {
|
||||||
|
"primaryColor": "#32B7BB"
|
||||||
|
},
|
||||||
|
"defaultVariant": "light",
|
||||||
|
"variants": {
|
||||||
|
"light": {
|
||||||
|
"background-color-primary": "#ffffff",
|
||||||
|
"background-color-accent": "#f0f2f5",
|
||||||
|
"background-color-contrast": "#4b4b4b",
|
||||||
|
"border-color": "var(--background-color-contrast)",
|
||||||
|
"sidebar-background-color": "var(--background-color-accent)",
|
||||||
|
"sidedrawer-background-color": "var(--background-color-accent)"
|
||||||
|
},
|
||||||
|
"dark": {
|
||||||
|
"text-color": "#d2d2d2",
|
||||||
|
"svg-color": "var(--text-color)",
|
||||||
|
"background-color-primary": "#262626",
|
||||||
|
"background-color-accent": "#353535",
|
||||||
|
"background_disabled": "#0A0A0A",
|
||||||
|
"background-color-contrast": "#ffffff",
|
||||||
|
"border-color": "var(--background-color-contrast)",
|
||||||
|
"header-text-color": "#d2d2d2",
|
||||||
|
"button-background-color": "var(--primaryColor)",
|
||||||
|
"button-text-color": "var(--background-color-contrast)",
|
||||||
|
"sidebar-background-color": "var(--background-color-accent)",
|
||||||
|
"sidedrawer-background-color": "var(--background-color-accent)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
"regions": {
|
"regions": {
|
||||||
"icon": "Globe",
|
"icon": "Globe",
|
||||||
"title": "Regions"
|
"title": "Locations"
|
||||||
},
|
},
|
||||||
"vault": {
|
"vault": {
|
||||||
"icon": "Archive",
|
"icon": "Archive",
|
59
packages/app/constants/settingsList.json
Normal file
59
packages/app/constants/settingsList.json
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "expire_session",
|
||||||
|
"group": "general",
|
||||||
|
"type": "Switch",
|
||||||
|
"icon": "Key",
|
||||||
|
"title": "Expire Session"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edit_sidebar",
|
||||||
|
"group": "sidebar",
|
||||||
|
"type": "Button",
|
||||||
|
"icon": "Edit",
|
||||||
|
"title": "Edit Sidebar",
|
||||||
|
"emitEvent": "edit_sidebar",
|
||||||
|
"noStorage": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "collapseOnLooseFocus",
|
||||||
|
"group": "sidebar",
|
||||||
|
"type": "Switch",
|
||||||
|
"title": "Collapse when loose focus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reduceAnimations",
|
||||||
|
"group": "aspect",
|
||||||
|
"type": "Switch",
|
||||||
|
"icon": "MdOutlineAnimation",
|
||||||
|
"title": "Reduce animation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "darkMode",
|
||||||
|
"group": "aspect",
|
||||||
|
"type": "Switch",
|
||||||
|
"icon": "Moon",
|
||||||
|
"title": "Dark Mode",
|
||||||
|
"emitEvent": "darkMode",
|
||||||
|
"experimental": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "primaryColor",
|
||||||
|
"group": "aspect",
|
||||||
|
"type": "SliderColorPicker",
|
||||||
|
"title": "Primary color",
|
||||||
|
"emitEvent": "modifyTheme",
|
||||||
|
"updateValueKey": "primaryColor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "resetTheme",
|
||||||
|
"group": "aspect",
|
||||||
|
"type": "Button",
|
||||||
|
"title": "Reset theme",
|
||||||
|
"props": {
|
||||||
|
"children": "Default Theme"
|
||||||
|
},
|
||||||
|
"emitEvent": "resetTheme",
|
||||||
|
"noStorage": true
|
||||||
|
}
|
||||||
|
]
|
99
packages/app/constants/sidebar.json
Normal file
99
packages/app/constants/sidebar.json
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Dashboard",
|
||||||
|
"icon": "Home",
|
||||||
|
"locked": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "workloads",
|
||||||
|
"title": "Workloads",
|
||||||
|
"icon": "GitCommit",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "workloads/pool",
|
||||||
|
"title": "Pool",
|
||||||
|
"icon": "Archive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "workloads/sections",
|
||||||
|
"title": "Sections",
|
||||||
|
"icon": "Target"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "workloads/items",
|
||||||
|
"title": "Items",
|
||||||
|
"icon": "Tag"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "users",
|
||||||
|
"title": "Users",
|
||||||
|
"icon": "Users",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "users/contracts",
|
||||||
|
"title": "Contracts",
|
||||||
|
"icon": "Briefcase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "users/roles",
|
||||||
|
"title": "Roles",
|
||||||
|
"icon": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "users/workshifts",
|
||||||
|
"title": "Workshifts",
|
||||||
|
"icon": "Clock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "users/accounts",
|
||||||
|
"title": "Accounts",
|
||||||
|
"icon": "User"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "geo",
|
||||||
|
"title": "Geo",
|
||||||
|
"icon": "MapPin",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "locations",
|
||||||
|
"title": "Locations",
|
||||||
|
"icon": "Map"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fabric",
|
||||||
|
"title": "Fabric",
|
||||||
|
"icon": "Box"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vault",
|
||||||
|
"title": "Vault",
|
||||||
|
"icon": "Archive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "reports",
|
||||||
|
"title": "Reports",
|
||||||
|
"icon": "Umbrella"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "server_manager",
|
||||||
|
"title": "Server",
|
||||||
|
"icon": "Database"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "launchpad",
|
||||||
|
"title": "Launchpad",
|
||||||
|
"icon": "Command"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "statistics",
|
||||||
|
"title": "Statistics",
|
||||||
|
"icon": "BarChart"
|
||||||
|
}
|
||||||
|
]
|
22
packages/app/constants/vaultItemStatements.json
Normal file
22
packages/app/constants/vaultItemStatements.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"active": {
|
||||||
|
"value": "active",
|
||||||
|
"label": "Active",
|
||||||
|
"tagColor": "green"
|
||||||
|
},
|
||||||
|
"retired": {
|
||||||
|
"value": "retired",
|
||||||
|
"label": "Retired",
|
||||||
|
"tagColor": "red"
|
||||||
|
},
|
||||||
|
"storaged": {
|
||||||
|
"value": "storaged",
|
||||||
|
"label": "Storaged",
|
||||||
|
"tagColor": "blue"
|
||||||
|
},
|
||||||
|
"unknown": {
|
||||||
|
"value": "unknown",
|
||||||
|
"label": "Unknown",
|
||||||
|
"tagColor": "gray"
|
||||||
|
}
|
||||||
|
}
|
15
packages/app/constants/vaultItemsTypes.json
Normal file
15
packages/app/constants/vaultItemsTypes.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"computers": [
|
||||||
|
"desktop",
|
||||||
|
"laptop",
|
||||||
|
"phone",
|
||||||
|
"tablet"
|
||||||
|
],
|
||||||
|
"peripherals": [
|
||||||
|
"keyboard",
|
||||||
|
"mouse",
|
||||||
|
"monitor",
|
||||||
|
"printer",
|
||||||
|
"scanner"
|
||||||
|
]
|
||||||
|
}
|
@ -11,8 +11,7 @@
|
|||||||
"timeago.js": "^4.0.2",
|
"timeago.js": "^4.0.2",
|
||||||
"@ant-design/icons": "^4.7.0",
|
"@ant-design/icons": "^4.7.0",
|
||||||
"@corenode/utils": "^0.28.26",
|
"@corenode/utils": "^0.28.26",
|
||||||
"@react-pdf/renderer": "^1.6.17",
|
"antd": "^4.17.2",
|
||||||
"antd": "^4.17.0",
|
|
||||||
"chart.js": "^3.5.1",
|
"chart.js": "^3.5.1",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"corenode": "^0.28.26",
|
"corenode": "^0.28.26",
|
||||||
@ -26,7 +25,6 @@
|
|||||||
"howler": "^2.2.3",
|
"howler": "^2.2.3",
|
||||||
"js-cookie": "^2.2.1",
|
"js-cookie": "^2.2.1",
|
||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
"less-vars-to-js": "^1.3.0",
|
|
||||||
"linebridge": "^0.8.4",
|
"linebridge": "^0.8.4",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"mpegts.js": "^1.6.10",
|
"mpegts.js": "^1.6.10",
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"key": "inspect_element",
|
|
||||||
"title": "Inspect",
|
|
||||||
"icon": "Command",
|
|
||||||
"require": "embedded",
|
|
||||||
"params": {
|
|
||||||
"onClick": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,4 +0,0 @@
|
|||||||
[
|
|
||||||
"main",
|
|
||||||
"posts"
|
|
||||||
]
|
|
@ -1,4 +0,0 @@
|
|||||||
[
|
|
||||||
{"key":"pro_boost","icon":"RocketOutlined","type":"switch","title":"CPRO™ Boost","description":"","require":"pro","value":false},
|
|
||||||
{"key":"allow_comments","icon":"CommentOutlined","type":"switch","title":"Allow Comments","description":"","require":"","value":true}
|
|
||||||
]
|
|
@ -1,49 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "expire_session",
|
|
||||||
"icon": "Key",
|
|
||||||
"title": "Expire Session",
|
|
||||||
"group": "general",
|
|
||||||
"type": "Switch"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "post_autoposition",
|
|
||||||
"icon": "AlignCenter",
|
|
||||||
"type": "Switch",
|
|
||||||
"title": "Center on click",
|
|
||||||
"description": "Center posts element when then is clicked"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "search_ontype",
|
|
||||||
"icon": "CornerDownRight",
|
|
||||||
"type": "Switch",
|
|
||||||
"title": "Auto search on input",
|
|
||||||
"description": "Search automaticly when type something"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "post_hidebar",
|
|
||||||
"icon": "Menu",
|
|
||||||
"type": "Switch",
|
|
||||||
"title": "Auto hide postbar",
|
|
||||||
"description": "Hide post actions bar when loose focus"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "edit_sidebar",
|
|
||||||
"group": "sidebar",
|
|
||||||
"icon": "Edit",
|
|
||||||
"title": "Edit Sidebar",
|
|
||||||
"type": "Button"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "collapseOnLooseFocus",
|
|
||||||
"group": "sidebar",
|
|
||||||
"title": "Collapse when loose focus",
|
|
||||||
"type": "Switch"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "reduceAnimations",
|
|
||||||
"group": "aspect",
|
|
||||||
"title": "Reduce animations",
|
|
||||||
"type": "Switch"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,14 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "main",
|
|
||||||
"title": "Dashboard",
|
|
||||||
"icon": "Home",
|
|
||||||
"locked": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "posts",
|
|
||||||
"title": "Posts",
|
|
||||||
"icon": "Home",
|
|
||||||
"locked": true
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,38 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "backgroundImage",
|
|
||||||
"icon": "Image",
|
|
||||||
"title": "Background",
|
|
||||||
"description": "Change the background of the app"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "overlay",
|
|
||||||
"icon": "Sidebar",
|
|
||||||
"title": "Overlay",
|
|
||||||
"description": "Description blah blah"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "color",
|
|
||||||
"icon": "Droplet",
|
|
||||||
"title": "Colors",
|
|
||||||
"description": "Texts, Buttons, Sliders ...etc"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "text",
|
|
||||||
"icon": "FontColorsOutlined",
|
|
||||||
"title": "Text",
|
|
||||||
"description": "Sizes, Fonts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sounds",
|
|
||||||
"icon": "Volume2",
|
|
||||||
"title": "Sounds",
|
|
||||||
"description": "BlipBlopBLup"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "darkmode",
|
|
||||||
"icon": "Moon",
|
|
||||||
"title": "Dark Mode",
|
|
||||||
"description": "Yeaah, no more daying"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,27 +1,47 @@
|
|||||||
import React from "react"
|
// Patch global prototypes
|
||||||
import { Helmet } from "react-helmet"
|
|
||||||
import progressBar from "nprogress"
|
|
||||||
import * as antd from "antd"
|
|
||||||
import classnames from "classnames"
|
|
||||||
|
|
||||||
import { Icons } from "components/Icons"
|
|
||||||
import { CreateEviteApp, BindPropsProvider } from "evite"
|
|
||||||
|
|
||||||
import config from "config"
|
|
||||||
import { Session, User } from "models"
|
|
||||||
import { NotFound, RenderError } from "components"
|
|
||||||
import { SettingsController, SidebarController } from "controllers"
|
|
||||||
import { API, Render, Debug, Sound } from "extensions"
|
|
||||||
|
|
||||||
import { Sidebar, Header, Drawer, Sidedrawer } from "./layout"
|
|
||||||
import "theme/index.less"
|
|
||||||
|
|
||||||
// append method to array prototype
|
|
||||||
Array.prototype.move = function (from, to) {
|
Array.prototype.move = function (from, to) {
|
||||||
this.splice(to, 0, this.splice(from, 1)[0])
|
this.splice(to, 0, this.splice(from, 1)[0])
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String.prototype.toTitleCase = function () {
|
||||||
|
return this.replace(/\w\S*/g, function (txt) {
|
||||||
|
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
import React from "react"
|
||||||
|
import { CreateEviteApp, BindPropsProvider } from "evite"
|
||||||
|
import { Helmet } from "react-helmet"
|
||||||
|
import * as antd from "antd"
|
||||||
|
import progressBar from "nprogress"
|
||||||
|
import classnames from "classnames"
|
||||||
|
|
||||||
|
import { SidebarController, SettingsController } from "controllers"
|
||||||
|
import { Session, User } from "models"
|
||||||
|
import { API, Render, Splash, Theme, Sound } from "extensions"
|
||||||
|
import config from "config"
|
||||||
|
|
||||||
|
import { NotFound, RenderError, FabricCreator, Settings } from "components"
|
||||||
|
import { Sidebar, Header, Drawer, Sidedrawer } from "./layout"
|
||||||
|
import { Icons } from "components/Icons"
|
||||||
|
|
||||||
|
import "theme/index.less"
|
||||||
|
|
||||||
|
const SplashExtension = Splash.extension({
|
||||||
|
logo: config.logo.alt,
|
||||||
|
preset: "fadeOut",
|
||||||
|
velocity: 1000,
|
||||||
|
props: {
|
||||||
|
logo: {
|
||||||
|
style: {
|
||||||
|
marginBottom: "10%",
|
||||||
|
stroke: "black",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
class ThrowCrash {
|
class ThrowCrash {
|
||||||
constructor(message, description) {
|
constructor(message, description) {
|
||||||
this.message = message
|
this.message = message
|
||||||
@ -50,6 +70,9 @@ class App {
|
|||||||
|
|
||||||
this.eventBus = this.contexts.main.eventBus
|
this.eventBus = this.contexts.main.eventBus
|
||||||
|
|
||||||
|
this.eventBus.on("app_ready", () => {
|
||||||
|
this.setState({ initialized: true })
|
||||||
|
})
|
||||||
this.eventBus.on("top_loadBar_start", () => {
|
this.eventBus.on("top_loadBar_start", () => {
|
||||||
this.progressBar.start()
|
this.progressBar.start()
|
||||||
})
|
})
|
||||||
@ -123,6 +146,26 @@ class App {
|
|||||||
|
|
||||||
static windowContext() {
|
static windowContext() {
|
||||||
return {
|
return {
|
||||||
|
openSettings: (goTo) => {
|
||||||
|
window.app.DrawerController.open("settings", Settings, {
|
||||||
|
props: {
|
||||||
|
width: "40%",
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
goTo,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openFabric: (defaultType) => {
|
||||||
|
window.app.DrawerController.open("FabricCreator", FabricCreator, {
|
||||||
|
props: {
|
||||||
|
width: "70%",
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
defaultType,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
configuration: this.configuration,
|
configuration: this.configuration,
|
||||||
isValidSession: this.isValidSession,
|
isValidSession: this.isValidSession,
|
||||||
getSettings: (...args) => this.contexts.app.configuration?.settings?.get(...args),
|
getSettings: (...args) => this.contexts.app.configuration?.settings?.get(...args),
|
||||||
@ -131,6 +174,7 @@ class App {
|
|||||||
|
|
||||||
static appContext() {
|
static appContext() {
|
||||||
return {
|
return {
|
||||||
|
renderRef: this.renderRef,
|
||||||
sessionController: this.sessionController,
|
sessionController: this.sessionController,
|
||||||
userController: this.userController,
|
userController: this.userController,
|
||||||
configuration: this.configuration,
|
configuration: this.configuration,
|
||||||
@ -145,10 +189,14 @@ class App {
|
|||||||
RenderError: (props) => {
|
RenderError: (props) => {
|
||||||
return <RenderError {...props} />
|
return <RenderError {...props} />
|
||||||
},
|
},
|
||||||
|
initialization: () => {
|
||||||
|
return <Splash.SplashComponent logo={config.logo.alt} />
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
// app
|
// app
|
||||||
|
initialized: false,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
crash: false,
|
crash: false,
|
||||||
isOnTransition: false,
|
isOnTransition: false,
|
||||||
@ -158,8 +206,6 @@ class App {
|
|||||||
data: null,
|
data: null,
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutContentRef = React.createRef()
|
|
||||||
|
|
||||||
flushState = () => {
|
flushState = () => {
|
||||||
this.setState({ session: null, data: null })
|
this.setState({ session: null, data: null })
|
||||||
}
|
}
|
||||||
@ -174,12 +220,16 @@ class App {
|
|||||||
|
|
||||||
initialization = async () => {
|
initialization = async () => {
|
||||||
try {
|
try {
|
||||||
|
this.eventBus.emit("splash_show")
|
||||||
await this.contexts.app.initializeDefaultBridge()
|
await this.contexts.app.initializeDefaultBridge()
|
||||||
await this.__init_session()
|
await this.__init_session()
|
||||||
await this.__init_user()
|
await this.__init_user()
|
||||||
|
this.eventBus.emit("app_ready")
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.eventBus.emit("splash_close")
|
||||||
throw new ThrowCrash(error.message, error.description)
|
throw new ThrowCrash(error.message, error.description)
|
||||||
}
|
}
|
||||||
|
this.eventBus.emit("splash_close")
|
||||||
}
|
}
|
||||||
|
|
||||||
__init_session = async () => {
|
__init_session = async () => {
|
||||||
@ -228,34 +278,40 @@ class App {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.state.initialized) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{config.app.siteName}</title>
|
<title>{config.app.siteName}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<antd.Layout style={{ height: "100%" }}>
|
<antd.ConfigProvider>
|
||||||
<Drawer />
|
<antd.Layout className="app_layout" style={{ height: "100%" }}>
|
||||||
<Sidebar user={this.state.user} />
|
<Drawer />
|
||||||
<antd.Layout className="app_layout">
|
<Sidebar user={this.state.user} />
|
||||||
<Header visible={this.state.headerVisible} />
|
<antd.Layout className="content_layout">
|
||||||
<antd.Layout.Content className="app_wrapper">
|
<Header />
|
||||||
<div className={classnames("fade-transverse-active", { "fade-transverse-leave": this.state.isOnTransition })}>
|
<antd.Layout.Content className="layout_page">
|
||||||
<BindPropsProvider
|
<div className={classnames("fade-transverse-active", { "fade-transverse-leave": this.state.isOnTransition })}>
|
||||||
user={this.state.user}
|
<BindPropsProvider
|
||||||
session={this.state.session}
|
user={this.state.user}
|
||||||
>
|
session={this.state.session}
|
||||||
<Render.RenderRouter staticRenders={App.staticRenders} />
|
>
|
||||||
</BindPropsProvider>
|
<Render.RenderRouter staticRenders={App.staticRenders} />
|
||||||
</div>
|
</BindPropsProvider>
|
||||||
</antd.Layout.Content>
|
</div>
|
||||||
|
</antd.Layout.Content>
|
||||||
|
</antd.Layout>
|
||||||
|
<Sidedrawer />
|
||||||
</antd.Layout>
|
</antd.Layout>
|
||||||
<Sidedrawer />
|
</antd.ConfigProvider>
|
||||||
</antd.Layout>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CreateEviteApp(App, {
|
export default CreateEviteApp(App, {
|
||||||
extensions: [Sound.extension, Render.extension, API, Debug],
|
extensions: [Sound.extension, Render.extension, Theme.extension, API, SplashExtension],
|
||||||
})
|
})
|
@ -7,7 +7,7 @@ import config from "config"
|
|||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
export class AboutApp extends React.Component {
|
export class AboutCard extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
visible: true,
|
visible: true,
|
||||||
}
|
}
|
||||||
@ -68,5 +68,5 @@ export function openModal() {
|
|||||||
const component = document.createElement("div")
|
const component = document.createElement("div")
|
||||||
document.body.appendChild(component)
|
document.body.appendChild(component)
|
||||||
|
|
||||||
ReactDOM.render(<AboutApp />, component)
|
ReactDOM.render(<AboutCard />, component)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React from 'react'
|
import React from "react"
|
||||||
import { Card } from 'antd'
|
import classnames from "classnames"
|
||||||
|
import "./index.less"
|
||||||
import './index.less'
|
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
const { children } = props
|
const { children, float } = props
|
||||||
|
|
||||||
return <Card style={props.style} className="actionsBar_card">
|
return <div style={props.style} className={classnames("actionsBar_card", { ["float"]: float })}>
|
||||||
<div style={props.wrapperStyle} className="actionsBar_flexWrapper">
|
<div style={props.wrapperStyle} className="actionsBar_flexWrapper">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
}
|
}
|
@ -1,15 +1,30 @@
|
|||||||
.actionsBar_card {
|
.actionsBar_card {
|
||||||
border-radius: 8px;
|
border: 1px solid #e0e0e0;
|
||||||
transition: all 200ms ease-in-out;
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 200ms ease-in-out;
|
||||||
|
background-color: #0c0c0c15;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
|
||||||
|
&.float {
|
||||||
|
z-index: 1000;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.actionsBar_flexWrapper{
|
.actionsBar_flexWrapper {
|
||||||
transition: all 200ms ease-in-out;
|
transition: all 200ms ease-in-out;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
display: flex;
|
||||||
}
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { LoadingOutlined } from "@ant-design/icons"
|
|
||||||
import { Result } from "antd"
|
|
||||||
|
|
||||||
export default (props = {}) => {
|
|
||||||
return <div>
|
|
||||||
<Result title={props.title ?? "Loading"} icon={<LoadingOutlined spin />} />
|
|
||||||
</div>
|
|
||||||
}
|
|
@ -9,9 +9,9 @@
|
|||||||
vertical-align: middle !important;
|
vertical-align: middle !important;
|
||||||
|
|
||||||
.ant-input {
|
.ant-input {
|
||||||
background-color: @app_background_accent!important;
|
background-color: var(--background-color-accent)!important;
|
||||||
border-color: @app_background_accent!important;
|
border-color: var(--background-color-accent)!important;
|
||||||
color: @app_background_contrast!important;
|
color: var(--background-color-contrast)!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input-group {
|
.ant-input-group {
|
||||||
@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn {
|
.ant-btn {
|
||||||
background-color: #eeeeee!important;
|
background-color: var(--background-color-primary)!important;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,166 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import * as antd from "antd"
|
|
||||||
import { RefreshCw } from "feather-reactjs"
|
|
||||||
import { getCircularReplacer, decycle } from "@corenode/utils"
|
|
||||||
|
|
||||||
const serializeFlags = {
|
|
||||||
__cycle_flag: true, // with id 0
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFlagId(e, id) {
|
|
||||||
return serializeFlags[Object.keys(e)[id ?? 0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseError = (error) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: "12px 16px",
|
|
||||||
height: "47px",
|
|
||||||
backgroundColor: "#d9d9d9",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
This could not be rendered
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<antd.Collapse>
|
|
||||||
<antd.Collapse.Panel header="See error">
|
|
||||||
<div style={{ margin: "0 5px 15px 5px", wordBreak: "break-all" }}>
|
|
||||||
<span>{error.toString()}</span>
|
|
||||||
</div>
|
|
||||||
</antd.Collapse.Panel>
|
|
||||||
</antd.Collapse>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseDecorator = (data, json) => {
|
|
||||||
const type = typeof data
|
|
||||||
console.log(type)
|
|
||||||
switch (type) {
|
|
||||||
case "string": {
|
|
||||||
return `(${json.length}) characters`
|
|
||||||
}
|
|
||||||
case "object": {
|
|
||||||
if (data == null) {
|
|
||||||
return `Empty (null/undefined)`
|
|
||||||
}
|
|
||||||
if (isFlagId(data, 0)) {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<RefreshCw /> Cylic
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (typeof data.length !== "undefined") {
|
|
||||||
return `Length (${data.length})`
|
|
||||||
}
|
|
||||||
if (typeof Object.keys(data).length !== "undefined") {
|
|
||||||
return `Length (${Object.keys(data).length})`
|
|
||||||
}
|
|
||||||
return `Immeasurable`
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
return `Length (${data})`
|
|
||||||
}
|
|
||||||
case "boolean": {
|
|
||||||
return <antd.Tag color={data ? "blue" : "volcano"}> {data ? "true" : "false"} </antd.Tag>
|
|
||||||
}
|
|
||||||
case "number": {
|
|
||||||
return <antd.Tag> {data} </antd.Tag>
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return `Immeasurable / Invalid`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseData = (data) => {
|
|
||||||
try {
|
|
||||||
switch (typeof data) {
|
|
||||||
case "object": {
|
|
||||||
if (data == null) {
|
|
||||||
return `${data}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFlagId(data, 0)) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: "12px 16px",
|
|
||||||
height: "47px",
|
|
||||||
backgroundColor: "#d9d9d9",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RefreshCw /> Circular
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(data).length > 0) {
|
|
||||||
return <div>{ElementList(data)}</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
return JSON.stringify(data, getCircularReplacer())
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
return JSON.stringify(data, getCircularReplacer())
|
|
||||||
}
|
|
||||||
case "boolean": {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return `${data}`
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return parseError(data, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseType = (data) => {
|
|
||||||
if (data !== null && isFlagId(data, 0)) {
|
|
||||||
return `[loop]`
|
|
||||||
}
|
|
||||||
return `[${typeof data}]`
|
|
||||||
}
|
|
||||||
|
|
||||||
const excludedTypesFromContent = ["boolean"]
|
|
||||||
|
|
||||||
export default function ElementList(data) {
|
|
||||||
if (!data) return false
|
|
||||||
|
|
||||||
data = decycle(data)
|
|
||||||
const keys = Object.keys(data)
|
|
||||||
|
|
||||||
return keys.map((key) => {
|
|
||||||
const value = data[key]
|
|
||||||
const content = parseData(value)
|
|
||||||
const type = parseType(value)
|
|
||||||
const decorator = parseDecorator(value, content)
|
|
||||||
|
|
||||||
const header = (
|
|
||||||
<div>
|
|
||||||
{type} <strong>{key}</strong> | {decorator}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<antd.Collapse ghost expandIconPosition="right" bordered="false" style={{ border: "0px" }} key={key}>
|
|
||||||
{excludedTypesFromContent.includes(typeof value) ? (
|
|
||||||
<antd.Collapse.Panel key={key} header={header} />
|
|
||||||
) : (
|
|
||||||
<antd.Collapse.Panel key={key} header={header}>
|
|
||||||
<div style={{ margin: "0 5px 15px 5px", wordBreak: "break-all" }}>
|
|
||||||
<span>{content}</span>
|
|
||||||
</div>
|
|
||||||
</antd.Collapse.Panel>
|
|
||||||
)}
|
|
||||||
</antd.Collapse>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ import React from "react"
|
|||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const api = window.app.apiBridge
|
const api = window.app.request
|
||||||
|
|
||||||
export default class Operations extends React.Component {
|
export default class Operations extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
|
@ -2,18 +2,26 @@ import React from 'react'
|
|||||||
import * as antd from 'antd'
|
import * as antd from 'antd'
|
||||||
import { Icons as FIcons, createIconRender } from "components/Icons"
|
import { Icons as FIcons, createIconRender } from "components/Icons"
|
||||||
import * as MDIcons from "react-icons/md"
|
import * as MDIcons from "react-icons/md"
|
||||||
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
const Icons = {
|
const Icons = {
|
||||||
...FIcons,
|
...FIcons,
|
||||||
...MDIcons
|
...MDIcons
|
||||||
}
|
}
|
||||||
|
|
||||||
import "./index.less"
|
|
||||||
|
|
||||||
const FormComponents = {
|
const FormComponents = {
|
||||||
"input": antd.Input,
|
"input": antd.Input,
|
||||||
"textarea": antd.Input.TextArea,
|
"textarea": antd.Input.TextArea,
|
||||||
"select": antd.Select,
|
"select": antd.Select,
|
||||||
|
"datepicker": antd.DatePicker,
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestModifyByType = {
|
||||||
|
"vaultItem": {
|
||||||
|
"additions": ["essc"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIELDS
|
// FIELDS
|
||||||
@ -40,25 +48,74 @@ const FieldsForms = {
|
|||||||
return update.target.value
|
return update.target.value
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
location: {
|
||||||
|
label: "Location",
|
||||||
|
component: "select",
|
||||||
|
updateEvent: "onChange",
|
||||||
|
children: async () => {
|
||||||
|
const api = window.app.request
|
||||||
|
const regions = await api.get.regions()
|
||||||
|
|
||||||
|
return regions.map(region => {
|
||||||
|
return <antd.Select.Option value={region.name}>{region.name}</antd.Select.Option>
|
||||||
|
})
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
placeholder: "Select a location",
|
||||||
|
}
|
||||||
|
},
|
||||||
vaultItemTypeSelector: {
|
vaultItemTypeSelector: {
|
||||||
label: "Type",
|
label: "Type",
|
||||||
component: "select",
|
component: "select",
|
||||||
updateEvent: "onChange",
|
updateEvent: "onChange",
|
||||||
|
children: async () => {
|
||||||
|
let types = await import("schemas/vaultItemsTypes.json")
|
||||||
|
|
||||||
|
types = types.default || types
|
||||||
|
|
||||||
|
return Object.keys(types).map((group) => {
|
||||||
|
return <antd.Select.OptGroup key={group} label={String(group).toTitleCase()}>
|
||||||
|
{types[group].map((type) => {
|
||||||
|
return <antd.Select.Option key={type} value={`${group}-${type}`}>{String(type).toTitleCase()}</antd.Select.Option>
|
||||||
|
})}
|
||||||
|
</antd.Select.OptGroup>
|
||||||
|
})
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
placeholder: "Select a type",
|
placeholder: "Select a type",
|
||||||
children: [
|
}
|
||||||
<antd.Select.OptGroup label="Computers">
|
},
|
||||||
<antd.Select.Option value="computers-desktop">Desktop</antd.Select.Option>
|
vaultItemSerial: {
|
||||||
<antd.Select.Option value="computers-laptop">Laptop</antd.Select.Option>
|
label: "Serial number",
|
||||||
<antd.Select.Option value="computers-phone">Phone</antd.Select.Option>
|
component: "input",
|
||||||
<antd.Select.Option value="computers-tablet">Tablet</antd.Select.Option>
|
updateEvent: "onChange",
|
||||||
<antd.Select.Option value="computers-other">Other</antd.Select.Option>
|
onUpdate: (update) => {
|
||||||
</antd.Select.OptGroup>,
|
return update.target.value
|
||||||
<antd.Select.OptGroup label="Peripherals">
|
},
|
||||||
<antd.Select.Option value="peripherals-monitor">Monitor</antd.Select.Option>
|
props: {
|
||||||
<antd.Select.Option value="peripherals-printer">Printer</antd.Select.Option>
|
placeholder: "S/N 00000000X",
|
||||||
</antd.Select.OptGroup>,
|
}
|
||||||
]
|
},
|
||||||
|
vaultItemManufacturer: {
|
||||||
|
label: "Manufacturer",
|
||||||
|
component: "input",
|
||||||
|
updateEvent: "onChange",
|
||||||
|
onUpdate: (update) => {
|
||||||
|
return update.target.value
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
placeholder: "e.g. Hewlett Packard",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
vaultItemManufacturedYear: {
|
||||||
|
label: "Manufactured Year",
|
||||||
|
component: "datepicker",
|
||||||
|
updateEvent: "onChange",
|
||||||
|
onUpdate: (update) => {
|
||||||
|
return update.year()
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
picker: "year"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -94,7 +151,12 @@ const TaskFormula = {
|
|||||||
|
|
||||||
const VaultItemFormula = {
|
const VaultItemFormula = {
|
||||||
defaultFields: [
|
defaultFields: [
|
||||||
|
// TODO: include location
|
||||||
"vaultItemTypeSelector",
|
"vaultItemTypeSelector",
|
||||||
|
"vaultItemSerial",
|
||||||
|
"vaultItemManufacturer",
|
||||||
|
"vaultItemManufacturedYear",
|
||||||
|
"location",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,20 +182,34 @@ const FabricItemTypes = ["product", "operation", "phase", "task", "vaultItem"]
|
|||||||
export default class FabricCreator extends React.Component {
|
export default class FabricCreator extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
values: {},
|
submitting: false,
|
||||||
|
error: null,
|
||||||
fields: [],
|
|
||||||
|
|
||||||
name: null,
|
name: null,
|
||||||
type: null,
|
type: null,
|
||||||
uuid: null,
|
fields: [],
|
||||||
|
values: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = async () => {
|
componentDidMount = async () => {
|
||||||
await this.setItemType("product")
|
await this.setItemType(this.props.defaultType ?? "product")
|
||||||
this.setState({ loading: false })
|
this.setState({ loading: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toogleLoading = (to) => {
|
||||||
|
this.setState({ loading: to ?? !this.state.loading })
|
||||||
|
}
|
||||||
|
|
||||||
|
toogleSubmitting = (to) => {
|
||||||
|
this.setState({ submitting: to ?? !this.state.submitting })
|
||||||
|
}
|
||||||
|
|
||||||
|
clearError = () => {
|
||||||
|
if (this.state.error != null) {
|
||||||
|
this.setState({ error: null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
clearValues = async () => {
|
clearValues = async () => {
|
||||||
await this.setState({ values: {} })
|
await this.setState({ values: {} })
|
||||||
}
|
}
|
||||||
@ -155,39 +231,58 @@ export default class FabricCreator extends React.Component {
|
|||||||
this.appendFieldByType(field)
|
this.appendFieldByType(field)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({ type: type, name: "New item" })
|
await this.setState({ type: type, name: "New item" })
|
||||||
} else {
|
} else {
|
||||||
console.error(`Cannot load default fields from formula with type ${type}`)
|
console.error(`Cannot load default fields from formula with type ${type}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendFieldByType = (fieldType) => {
|
appendFieldByType = (fieldType) => {
|
||||||
const form = FieldsForms[fieldType]
|
const field = FieldsForms[fieldType]
|
||||||
|
|
||||||
if (typeof form === "undefined") {
|
if (typeof field === "undefined") {
|
||||||
console.error(`No form available for field [${fieldType}]`)
|
console.error(`No form available for field [${fieldType}]`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = this.state.fields
|
const fields = this.state.fields
|
||||||
fields.push(this.generateFieldRender({ type: fieldType, ...form }))
|
|
||||||
|
if (this.fieldsHasTypeKey(fieldType) && !field.allowMultiple) {
|
||||||
|
console.error(`Field [${fieldType}] already exists, and only can exists 1`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.push(this.generateFieldRender({ type: fieldType, ...field }))
|
||||||
|
|
||||||
this.setState({ fields: fields })
|
this.setState({ fields: fields })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fieldsHasTypeKey = (key) => {
|
||||||
|
let isOnFields = false
|
||||||
|
|
||||||
|
const fields = this.state.fields
|
||||||
|
|
||||||
|
fields.forEach(field => {
|
||||||
|
field.props.type === key ? isOnFields = true : null
|
||||||
|
})
|
||||||
|
|
||||||
|
return isOnFields
|
||||||
|
}
|
||||||
|
|
||||||
renderFieldSelectorMenu = () => {
|
renderFieldSelectorMenu = () => {
|
||||||
return <antd.Menu
|
return <antd.Menu
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
this.appendFieldByType(e.key)
|
this.appendFieldByType(e.key)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Object.keys(FieldsForms).map((field) => {
|
{Object.keys(FieldsForms).map((key) => {
|
||||||
const form = FieldsForms[field]
|
const field = FieldsForms[key]
|
||||||
const icon = form.icon && createIconRender(form.icon)
|
const icon = field.icon && createIconRender(field.icon)
|
||||||
|
const disabled = this.fieldsHasTypeKey(key) && !field.allowMultiple
|
||||||
|
|
||||||
return <antd.Menu.Item key={field}>
|
return <antd.Menu.Item disabled={disabled} key={key}>
|
||||||
{icon ?? null}
|
{icon ?? null}
|
||||||
{field.charAt(0).toUpperCase() + field.slice(1)}
|
{key.charAt(0).toUpperCase() + key.slice(1)}
|
||||||
</antd.Menu.Item>
|
</antd.Menu.Item>
|
||||||
})}
|
})}
|
||||||
</antd.Menu>
|
</antd.Menu>
|
||||||
@ -210,8 +305,50 @@ export default class FabricCreator extends React.Component {
|
|||||||
</antd.Menu>
|
</antd.Menu>
|
||||||
}
|
}
|
||||||
|
|
||||||
onDone = () => {
|
onDone = async () => {
|
||||||
console.log(this.getValues())
|
this.clearError()
|
||||||
|
this.toogleSubmitting(true)
|
||||||
|
|
||||||
|
const api = window.app.request
|
||||||
|
let properties = {}
|
||||||
|
|
||||||
|
this.getProperties().forEach((property) => {
|
||||||
|
if (typeof properties[property.type] !== "undefined") {
|
||||||
|
return properties[property.id] = property.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties[property.type] = property.value
|
||||||
|
})
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
type: this.state.type,
|
||||||
|
name: this.state.name,
|
||||||
|
properties: properties,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof requestModifyByType[this.state.type] !== "undefined") {
|
||||||
|
payload = {
|
||||||
|
...payload,
|
||||||
|
...requestModifyByType[this.state.type],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await api.put.fabric(payload).catch((response) => {
|
||||||
|
console.error(response)
|
||||||
|
this.setState({ error: response })
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
this.toogleSubmitting(false)
|
||||||
|
|
||||||
|
if (!this.state.error && typeof this.props.close === "function") {
|
||||||
|
this.props.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeName = (event) => {
|
||||||
|
this.setState({ name: event.target.value })
|
||||||
}
|
}
|
||||||
|
|
||||||
onUpdateValue = (event, value) => {
|
onUpdateValue = (event, value) => {
|
||||||
@ -224,46 +361,86 @@ export default class FabricCreator extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeField = (key) => {
|
removeField = (key) => {
|
||||||
this.setState({ fields: this.state.fields.filter(field => field.key != key) })
|
let values = this.state.values
|
||||||
|
let fields = this.state.fields.filter(field => field.key != key)
|
||||||
|
|
||||||
|
delete values[key]
|
||||||
|
|
||||||
|
this.setState({ fields: fields, values: values })
|
||||||
}
|
}
|
||||||
|
|
||||||
getValues = () => {
|
getProperties = () => {
|
||||||
return this.state.fields.map((field) => {
|
return this.state.fields.map((field) => {
|
||||||
return {
|
return {
|
||||||
type: field.props.type,
|
type: field.props.type,
|
||||||
|
id: field.props.id,
|
||||||
value: this.state.values[field.key],
|
value: this.state.values[field.key],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFieldRender = (field) => {
|
getKeyFromLatestFieldType = (type) => {
|
||||||
let { key, style, type, icon, component, label, updateEvent, props, onUpdate } = field
|
let latestByType = 0
|
||||||
|
|
||||||
if (!key) {
|
this.state.fields.forEach((field) => {
|
||||||
key = this.state.fields.length
|
field.props.type === type ? latestByType++ : null
|
||||||
|
})
|
||||||
|
|
||||||
|
return `${type}-${latestByType}`
|
||||||
|
}
|
||||||
|
|
||||||
|
generateFieldRender = (field) => {
|
||||||
|
if (!field.key) {
|
||||||
|
field.key = this.getKeyFromLatestFieldType(field.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof FormComponents[component] === "undefined") {
|
if (typeof FormComponents[field.component] === "undefined") {
|
||||||
console.error(`No component type available for field [${key}]`)
|
console.error(`No component type available for field [${field.key}]`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div key={key} id={`${type}-${key}`} type={type} className="field" style={style}>
|
const getSubmittingState = () => {
|
||||||
<div className="close" onClick={() => { this.removeField(key) }}><Icons.X /></div>
|
return this.state.submitting
|
||||||
<h4>{icon && createIconRender(icon)}{label}</h4>
|
}
|
||||||
<div className="fieldContent">
|
|
||||||
{React.createElement(FormComponents[component], {
|
|
||||||
...props,
|
|
||||||
value: this.state.values[key],
|
|
||||||
[updateEvent]: (...args) => {
|
|
||||||
if (typeof onUpdate === "function") {
|
|
||||||
return this.onUpdateValue({ updateEvent, key }, onUpdate(...args))
|
|
||||||
}
|
|
||||||
return this.onUpdateValue({ updateEvent, key }, ...args)
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
let fieldComponentProps = {
|
||||||
|
...field.props,
|
||||||
|
value: this.state.values[field.key],
|
||||||
|
disabled: getSubmittingState(),
|
||||||
|
[field.updateEvent]: (...args) => {
|
||||||
|
if (typeof field.onUpdate === "function") {
|
||||||
|
return this.onUpdateValue({ updateEvent: field.updateEvent, key: field.key }, field.onUpdate(...args))
|
||||||
|
}
|
||||||
|
return this.onUpdateValue({ updateEvent: field.updateEvent, key: field.key }, ...args)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let RenderComponent = null
|
||||||
|
|
||||||
|
if (typeof field.children === "function") {
|
||||||
|
RenderComponent = loadable(async () => {
|
||||||
|
try {
|
||||||
|
const children = await field.children()
|
||||||
|
return () => React.createElement(FormComponents[field.component], fieldComponentProps, children)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return ()=> <div>
|
||||||
|
<Icons.XCircle /> Load Error
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
fallback: <div>Loading...</div>,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
RenderComponent = () => React.createElement(FormComponents[field.component], fieldComponentProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div key={field.key} id={`${field.type}-${field.key}`} type={field.type} className="field" style={field.style}>
|
||||||
|
<div className="close" onClick={() => { this.removeField(field.key) }}><Icons.X /></div>
|
||||||
|
<h4>{field.icon && createIconRender(field.icon)}{field.label}</h4>
|
||||||
|
<div className="fieldContent">
|
||||||
|
<RenderComponent />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +448,6 @@ export default class FabricCreator extends React.Component {
|
|||||||
if (this.state.loading) {
|
if (this.state.loading) {
|
||||||
return <antd.Skeleton active />
|
return <antd.Skeleton active />
|
||||||
}
|
}
|
||||||
|
|
||||||
const TypeIcon = FabricItemTypesIcons[this.state.type] && createIconRender(FabricItemTypesIcons[this.state.type])
|
const TypeIcon = FabricItemTypesIcons[this.state.type] && createIconRender(FabricItemTypesIcons[this.state.type])
|
||||||
|
|
||||||
return <div className="fabric_creator">
|
return <div className="fabric_creator">
|
||||||
@ -281,19 +457,24 @@ export default class FabricCreator extends React.Component {
|
|||||||
{TypeIcon ?? <Icons.HelpCircle />}
|
{TypeIcon ?? <Icons.HelpCircle />}
|
||||||
</antd.Dropdown>
|
</antd.Dropdown>
|
||||||
</div>
|
</div>
|
||||||
<antd.Input defaultValue={this.state.name} />
|
<antd.Input defaultValue={this.state.name} onChange={this.onChangeName} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="fields">
|
<div className="fields">
|
||||||
<div className="wrap">
|
<div className="wrap">
|
||||||
{this.state.fields}
|
{this.state.submitting ? <antd.Skeleton active /> : this.state.fields}
|
||||||
</div>
|
</div>
|
||||||
<div className="bottom_actions">
|
<div className="bottom_actions">
|
||||||
<antd.Dropdown trigger={['click']} placement="topCenter" overlay={this.renderFieldSelectorMenu}>
|
<antd.Dropdown trigger={['click']} placement="topCenter" overlay={this.renderFieldSelectorMenu}>
|
||||||
<Icons.Plus />
|
<Icons.Plus />
|
||||||
</antd.Dropdown>
|
</antd.Dropdown>
|
||||||
<antd.Button onClick={this.onDone}>Done</antd.Button>
|
|
||||||
|
<antd.Button loading={this.state.submitting} onClick={this.onDone}>Done</antd.Button>
|
||||||
</div>
|
</div>
|
||||||
|
{this.state.error && <div className="error">
|
||||||
|
{this.state.error}
|
||||||
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,5 +111,12 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-color: #5e5e5e;
|
border-color: #5e5e5e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 5px;
|
||||||
|
transition: all 150ms ease-out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,84 +0,0 @@
|
|||||||
@import "~theme/index.less";
|
|
||||||
|
|
||||||
.main {
|
|
||||||
font-family: "Nunito", sans-serif;
|
|
||||||
margin: 20px 0 0;
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
overflow: auto;
|
|
||||||
color: @__global_color;
|
|
||||||
background-color: @__global_background;
|
|
||||||
padding: 15px;
|
|
||||||
border-radius: 10px;
|
|
||||||
|
|
||||||
:global {
|
|
||||||
.ant-list-item {
|
|
||||||
padding-top: 7px;
|
|
||||||
padding-bottom: 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-list-split .ant-list-item {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-list-item-meta-title {
|
|
||||||
color: rgba(0, 0, 0, 0.733);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuList {
|
|
||||||
user-select: none;
|
|
||||||
width: 224px;
|
|
||||||
:global {
|
|
||||||
.ant-menu-inline {
|
|
||||||
color: @__global_color;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menuContainer {
|
|
||||||
flex: 1;
|
|
||||||
padding-top: 8px;
|
|
||||||
padding-right: 40px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
padding-left: 40px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
color: @__global_color;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 28px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.horizontal {
|
|
||||||
flex-direction: column;
|
|
||||||
.menuList {
|
|
||||||
padding: 15px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: @screen-md) {
|
|
||||||
.main {
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.leftMenu {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
padding: 40px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import { Menu, Result } from 'antd'
|
|
||||||
import classnames from 'classnames'
|
|
||||||
import { Icons } from 'components/Icons'
|
|
||||||
|
|
||||||
import styles from './index.less'
|
|
||||||
import { objectToArrayMap } from '@corenode/utils'
|
|
||||||
|
|
||||||
export default class ListedMenu extends React.Component {
|
|
||||||
state = {
|
|
||||||
renderOptionTitle: true,
|
|
||||||
loading: true,
|
|
||||||
selectKey: '',
|
|
||||||
menus: [],
|
|
||||||
mode: this.props.mode ?? "inline"
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryMenu() {
|
|
||||||
this.setState({ loading: true })
|
|
||||||
this.setState({ menus: objectToArrayMap(this.props.menuArray), loading: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
getMenu() {
|
|
||||||
return this.state.menus.map(item => (
|
|
||||||
<Menu.Item key={item.key}>
|
|
||||||
<span>{item.icon} {item.title}</span>
|
|
||||||
</Menu.Item>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
selectKey = (key) => {
|
|
||||||
this.setState({
|
|
||||||
selectKey: key,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
renderChildren = () => {
|
|
||||||
let titlesArray = []
|
|
||||||
this.state.menus.forEach(e => { titlesArray[e.key] = e })
|
|
||||||
|
|
||||||
const OptionTitle = () => {
|
|
||||||
if (this.state.renderOptionTitle) {
|
|
||||||
return <div>
|
|
||||||
<h3>{React.createElement(Icons[titlesArray[this.state.selectKey].icon]) || null}{titlesArray[this.state.selectKey].title || null}</h3>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.selectKey && titlesArray[this.state.selectKey]) {
|
|
||||||
return <>
|
|
||||||
<OptionTitle />
|
|
||||||
{this.props.childrens[this.state.selectKey]}
|
|
||||||
</>
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Result title="Select an Option" state="info" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { childrens, menuArray, defaultKey } = this.props
|
|
||||||
const keyIndex = new URLSearchParams(location.search).get('key')
|
|
||||||
|
|
||||||
if (keyIndex && typeof (this.props.childrens[keyIndex]) !== "undefined") {
|
|
||||||
this.selectKey(keyIndex)
|
|
||||||
} else if (defaultKey != null) {
|
|
||||||
this.selectKey(defaultKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.renderOptionTitle != null) {
|
|
||||||
this.setState({ renderOptionTitle: this.props.renderOptionTitle })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childrens != null && menuArray != null) {
|
|
||||||
this.queryMenu()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { selectKey, loading } = this.state
|
|
||||||
const isMode = (e) => {
|
|
||||||
return this.state.mode === `${e}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div style={this.props.wrapperStyle ?? null} className={classnames(styles.main, { [styles.horizontal]: isMode("horizontal") })}>
|
|
||||||
<div className={styles.menuList}>
|
|
||||||
<h3>
|
|
||||||
{React.createElement(Icons[this.props.icon]) ?? null} {this.props.title ?? "Menu"}
|
|
||||||
</h3>
|
|
||||||
<Menu
|
|
||||||
mode={this.state.mode}
|
|
||||||
selectedKeys={[selectKey]}
|
|
||||||
onClick={({ key }) => this.selectKey(key)}
|
|
||||||
>
|
|
||||||
{this.getMenu()}
|
|
||||||
</Menu>
|
|
||||||
</div>
|
|
||||||
<div className={styles.menuContainer}>{this.renderChildren()}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { LoadingOutlined } from "@ant-design/icons"
|
|
||||||
import { Result } from "antd"
|
|
||||||
|
|
||||||
export default (props = {}) => {
|
|
||||||
return <Result title={props.title ?? "Loading"} icon={<LoadingOutlined spin />} />
|
|
||||||
}
|
|
@ -1,27 +1,30 @@
|
|||||||
import React from 'react'
|
import React from "react"
|
||||||
import { Result, Button, Typography } from "antd"
|
import { Result, Button, Typography } from "antd"
|
||||||
import { CloseCircleOutlined } from "@ant-design/icons"
|
import { CloseCircleOutlined } from "@ant-design/icons"
|
||||||
|
import config from "config"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
const { Paragraph, Text } = Typography
|
const { Paragraph, Text } = Typography
|
||||||
|
|
||||||
|
const ErrorEntry = (props) => {
|
||||||
|
const { error } = props
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
return <div className="error">
|
||||||
|
<CloseCircleOutlined />
|
||||||
|
Unhandled error
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="error">
|
||||||
|
<CloseCircleOutlined />
|
||||||
|
{error.info.toString()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
let errors = []
|
let errors = []
|
||||||
const getErrors = () => {
|
|
||||||
return errors.map((err) => {
|
|
||||||
if (err instanceof Error) {
|
|
||||||
return (
|
|
||||||
<Paragraph>
|
|
||||||
<CloseCircleOutlined style={{
|
|
||||||
color: "red",
|
|
||||||
marginRight: "10px",
|
|
||||||
}} />
|
|
||||||
{err.toString()}
|
|
||||||
</Paragraph>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return <div></div>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(props.error)) {
|
if (Array.isArray(props.error)) {
|
||||||
errors = props.error
|
errors = props.error
|
||||||
@ -29,27 +32,42 @@ export default (props) => {
|
|||||||
errors.push(props.error)
|
errors.push(props.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onClickGoMain = () => {
|
||||||
|
window.app.setLocation(config.app.mainPath ?? "/main")
|
||||||
|
}
|
||||||
|
const onClickReload = () => {
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Result
|
<Result
|
||||||
status="error"
|
status="error"
|
||||||
title="Render Error"
|
title="Render Error"
|
||||||
subTitle="It seems that the application is having problems displaying this page, we have detected some unrecoverable errors due to a bug. (This error will be automatically reported to the developers to find a solution as soon as possible)"
|
subTitle="It seems that the application is having problems displaying this page, we have detected some unrecoverable errors due to a bug. (This error will be automatically reported to the developers to find a solution as soon as possible)"
|
||||||
|
extra={[
|
||||||
|
<Button type="primary" key="gomain" onClick={onClickGoMain}>
|
||||||
|
Go Main
|
||||||
|
</Button>,
|
||||||
|
<Button key="reload" onClick={onClickReload}>Reload</Button>,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<div className="desc">
|
<Paragraph>
|
||||||
<Paragraph>
|
<Text
|
||||||
<Text
|
strong
|
||||||
strong
|
style={{
|
||||||
style={{
|
fontSize: 16,
|
||||||
fontSize: 16,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
We catch the following errors:
|
||||||
We have detected the following errors:
|
</Text>
|
||||||
</Text>
|
<div className="errors">
|
||||||
</Paragraph>
|
{errors.map((error, index) => {
|
||||||
{getErrors()}
|
return <ErrorEntry key={index} error={error} />
|
||||||
</div>
|
})}
|
||||||
|
</div>
|
||||||
|
</Paragraph>
|
||||||
</Result>
|
</Result>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
16
packages/app/src/components/RenderError/index.less
Normal file
16
packages/app/src/components/RenderError/index.less
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
.errors {
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
.error {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: red;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.stack {
|
||||||
|
margin-left: 24px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import * as antd from "antd"
|
|
||||||
|
|
||||||
import "./index.less"
|
|
||||||
|
|
||||||
export default class Roles extends React.Component {
|
|
||||||
render() {
|
|
||||||
const roles = []
|
|
||||||
|
|
||||||
if (Array.isArray(this.props.roles)) {
|
|
||||||
this.props.roles.forEach((role) => {
|
|
||||||
roles.push(
|
|
||||||
<div key={role}>
|
|
||||||
<antd.Tag>{role}</antd.Tag>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className="roles_wrapper">{roles}</div>
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
.roles_wrapper{
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
@ -19,21 +19,23 @@ export default class SelectableList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickKey = (key) => {
|
selectAll = () => {
|
||||||
if (typeof this.props.selectionEnabled !== "undefined") {
|
if (this.props.items.length > 0) {
|
||||||
if (!Boolean(this.props.selectionEnabled)) {
|
this.setState({
|
||||||
return false
|
selectedKeys: [...this.props.items.map((item) => item.key ?? item.id ?? item._id)],
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectKey = (key) => {
|
||||||
let list = this.state.selectedKeys ?? []
|
let list = this.state.selectedKeys ?? []
|
||||||
|
list.push(key)
|
||||||
|
return this.setState({ selectedKeys: list })
|
||||||
|
}
|
||||||
|
|
||||||
if (!list.includes(key)) {
|
unselectKey = (key) => {
|
||||||
list.push(key)
|
let list = this.state.selectedKeys ?? []
|
||||||
} else {
|
list = list.filter((_key) => key !== _key)
|
||||||
list = list.filter((_key) => key !== _key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.setState({ selectedKeys: list })
|
return this.setState({ selectedKeys: list })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ export default class SelectableList extends React.Component {
|
|||||||
if (typeof this.props.onDone === "function") {
|
if (typeof this.props.onDone === "function") {
|
||||||
this.props.onDone(this.state.selectedKeys)
|
this.props.onDone(this.state.selectedKeys)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedKeys: [],
|
selectedKeys: [],
|
||||||
})
|
})
|
||||||
@ -57,6 +59,16 @@ export default class SelectableList extends React.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (typeof this.props.selectionEnabled !== "undefined") {
|
||||||
|
if (!Boolean(this.props.selectionEnabled) && this.state.selectedKeys.length > 0) {
|
||||||
|
this.setState({
|
||||||
|
selectedKeys: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
renderActions = () => {
|
renderActions = () => {
|
||||||
if (typeof this.props.renderActions !== "undefined" && !this.props.renderActions) {
|
if (typeof this.props.renderActions !== "undefined" && !this.props.renderActions) {
|
||||||
return false
|
return false
|
||||||
@ -65,7 +77,7 @@ export default class SelectableList extends React.Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderExtraActions = () => {
|
const renderProvidedActions = () => {
|
||||||
if (Array.isArray(this.props.actions)) {
|
if (Array.isArray(this.props.actions)) {
|
||||||
return this.props.actions.map((action) => {
|
return this.props.actions.map((action) => {
|
||||||
return (
|
return (
|
||||||
@ -113,45 +125,64 @@ export default class SelectableList extends React.Component {
|
|||||||
<ActionsBar style={{ borderRadius: "8px 8px 0 0", width: "fit-content" }}>
|
<ActionsBar style={{ borderRadius: "8px 8px 0 0", width: "fit-content" }}>
|
||||||
<div key="discard">
|
<div key="discard">
|
||||||
<Button
|
<Button
|
||||||
style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
|
shape="round"
|
||||||
shape="circle"
|
|
||||||
onClick={this.onDiscard}
|
onClick={this.onDiscard}
|
||||||
{...this.props.onDiscardProps}
|
{...this.props.onDiscardProps}
|
||||||
>
|
>
|
||||||
{this.props.onDiscardRender ?? <Icons.X style={{ margin: 0, padding: 0 }} />}
|
{this.props.onDiscardRender ?? <Icons.X />}
|
||||||
|
Discard
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div key="done">
|
{renderProvidedActions()}
|
||||||
<Button type="primary" onClick={this.onDone} {...this.props.onDoneProps}>
|
|
||||||
{this.props.onDoneRender ?? (
|
|
||||||
<>
|
|
||||||
<Icons.Check /> Done
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{renderExtraActions()}
|
|
||||||
</ActionsBar>
|
</ActionsBar>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const validSelectionMethods = ["onClick", "onDoubleClick"]
|
||||||
|
|
||||||
const renderMethod = (item) => {
|
const renderMethod = (item) => {
|
||||||
|
const selectionMethod = validSelectionMethods.includes(this.props.selectionMethod) ? this.props.selectionMethod : "onClick"
|
||||||
|
|
||||||
if (typeof this.props.renderItem === "function") {
|
if (typeof this.props.renderItem === "function") {
|
||||||
const _key = item.key ?? item.id ?? item._id
|
const _key = item.key ?? item.id ?? item._id
|
||||||
|
const list = this.state.selectedKeys
|
||||||
|
const isSelected = list.includes(_key)
|
||||||
|
|
||||||
|
let props = {
|
||||||
|
key: _key,
|
||||||
|
id: _key,
|
||||||
|
className: classnames("selectableList_item", this.props.itemClassName, {
|
||||||
|
selected: this.state.selectedKeys.includes(_key),
|
||||||
|
}),
|
||||||
|
[selectionMethod]: () => {
|
||||||
|
if (typeof this.props.selectionEnabled !== "undefined") {
|
||||||
|
if (!Boolean(this.props.selectionEnabled)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
this.unselectKey(_key)
|
||||||
|
} else {
|
||||||
|
this.selectKey(_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectionMethod == "onDoubleClick") {
|
||||||
|
props.onClick = () => {
|
||||||
|
if (list.length > 0) {
|
||||||
|
if (isSelected) {
|
||||||
|
this.unselectKey(_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div {...props}>
|
||||||
key={_key}
|
|
||||||
id={_key}
|
|
||||||
onClick={() => this.onClickKey(_key)}
|
|
||||||
className={classnames("selectableList_item", this.props.itemClassName, {
|
|
||||||
selection: this.state.selectionEnabled,
|
|
||||||
selected: this.state.selectedKeys.includes(_key),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{this.props.renderItem(item)}
|
{this.props.renderItem(item)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -178,8 +209,7 @@ export default class SelectableList extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={classnames("selectableList", { ["selectionEnabled"]: this.props.selectionEnabled })}>
|
||||||
{this.renderActions()}
|
|
||||||
<List
|
<List
|
||||||
{...listProps}
|
{...listProps}
|
||||||
dataSource={[
|
dataSource={[
|
||||||
@ -188,7 +218,8 @@ export default class SelectableList extends React.Component {
|
|||||||
]}
|
]}
|
||||||
renderItem={renderMethod}
|
renderItem={renderMethod}
|
||||||
/>
|
/>
|
||||||
|
{this.renderActions()}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,59 +1,65 @@
|
|||||||
@selectableList_item_borderColor_active: rgba(51,51,51,1);
|
@selectableList_item_borderColor_active: rgba(51, 51, 51, 1);
|
||||||
@selectableList_item_borderColor_normal: rgba(51,51,51,0.3);
|
@selectableList_item_borderColor_normal: rgba(51, 51, 51, 0.3);
|
||||||
|
|
||||||
.selectableList_item {
|
.selectableList {
|
||||||
user-select: none;
|
&.selectionEnabled {
|
||||||
width: 100%;
|
.selectableList_item {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
border: @selectableList_item_borderColor_normal 1px solid;
|
border: rgba(51, 51, 51, 0.3) 1px solid;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
transition: all 150ms ease-in-out;
|
h1 {
|
||||||
|
user-select: none;
|
||||||
margin-bottom: 12px;
|
}
|
||||||
padding: 10px;
|
h3 {
|
||||||
|
user-select: none;
|
||||||
> div {
|
}
|
||||||
margin: 7px;
|
}
|
||||||
}
|
.selectableList_item:hover {
|
||||||
|
box-shadow: 2px 2px 8px 0px rgba(51, 51, 51, 0.5);
|
||||||
&.selection{
|
border: @selectableList_item_borderColor_active 1px solid;
|
||||||
cursor: pointer;
|
}
|
||||||
background-color: #f5f5f5;
|
}
|
||||||
|
|
||||||
h1 {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
background-color: #dadada;
|
|
||||||
transform: translate(10px, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectableList_item:hover {
|
.selectableList_item {
|
||||||
// transform: translate(2px, 2px);
|
user-select: none;
|
||||||
box-shadow: 2px 2px 8px 0px rgba(51,51,51,0.5);
|
width: 100%;
|
||||||
border: @selectableList_item_borderColor_active 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottomActions_wrapper{
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 10;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
left: 0;
|
border: @selectableList_item_borderColor_normal 1px solid;
|
||||||
right: 0;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
transition: all 150ms ease-in-out;
|
||||||
|
|
||||||
border-radius: 0;
|
> div {
|
||||||
|
margin: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
display: flex;
|
&.selected {
|
||||||
flex-direction: row;
|
background-color: #f5f5f5;
|
||||||
align-items: center;
|
transform: translate(10px, 0);
|
||||||
justify-content: center;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottomActions_wrapper {
|
||||||
|
position: sticky;
|
||||||
|
z-index: 1000;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
37
packages/app/src/components/ServerStatus/index.jsx
Normal file
37
packages/app/src/components/ServerStatus/index.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React from "react"
|
||||||
|
import * as antd from "antd"
|
||||||
|
import { Icons } from "components/Icons"
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const [connected, setConnected] = React.useState(window.app.ws.connected ?? false)
|
||||||
|
|
||||||
|
window.app.eventBus.on("websocket_connected", (status) => {
|
||||||
|
setConnected(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.eventBus.on("websocket_disconnected", (status) => {
|
||||||
|
setConnected(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getColor = () => {
|
||||||
|
if (!connected) {
|
||||||
|
return "red"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "blue"
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<div key="http">
|
||||||
|
<Icons.Database /> {window.app?.api?.origin ?? "unavailable"}
|
||||||
|
</div>
|
||||||
|
<div key="websocket">
|
||||||
|
<Icons.Cpu /> {window.app?.ws?.io?.uri ?? "unavailable"}
|
||||||
|
</div>
|
||||||
|
<div key="health">
|
||||||
|
<Icons.Activity /> <antd.Tag color={getColor()}>
|
||||||
|
{connected ? "Connected" : "Disconnected"}
|
||||||
|
</antd.Tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/icons"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/icons"
|
||||||
|
import { SliderPicker } from "react-color"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { SketchPicker } from "react-color"
|
|
||||||
import { AboutApp } from ".."
|
|
||||||
import config from "config"
|
import config from "config"
|
||||||
|
|
||||||
|
import settingList from "schemas/settingsList.json"
|
||||||
|
import groupsDecorator from "schemas/settingsGroupsDecorator.json"
|
||||||
|
|
||||||
|
import { AboutApp } from ".."
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
const ItemTypes = {
|
const ItemTypes = {
|
||||||
@ -15,221 +19,165 @@ const ItemTypes = {
|
|||||||
Input: antd.Input,
|
Input: antd.Input,
|
||||||
InputNumber: antd.InputNumber,
|
InputNumber: antd.InputNumber,
|
||||||
Select: antd.Select,
|
Select: antd.Select,
|
||||||
ColorPicker: SketchPicker,
|
SliderColorPicker: SliderPicker,
|
||||||
}
|
}
|
||||||
|
|
||||||
import settingList from "schemas/settings.json"
|
export default class SettingsMenu extends React.Component {
|
||||||
import groupsDecorator from "schemas/settingsGroups.json"
|
|
||||||
import { Session } from "models"
|
|
||||||
|
|
||||||
export class SettingsMenu extends React.Component {
|
|
||||||
state = {
|
state = {
|
||||||
settings: window.app.configuration.settings.get() ?? {},
|
settings: window.app.configuration.settings.get() ?? {},
|
||||||
}
|
}
|
||||||
|
|
||||||
_set(key, value) {
|
handleEvent = (event, item, to) => {
|
||||||
this.setState({ settings: window.app.configuration.settings.change(key, value) })
|
const id = item.id
|
||||||
|
|
||||||
|
if (typeof id === "undefined") {
|
||||||
|
console.error("SettingsMenu: Cannot update, item has no id")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentValue = window.app.configuration.settings.get(id) ?? null
|
||||||
|
|
||||||
|
// by default we set the opposite value to the current value
|
||||||
|
if (typeof to === "undefined") {
|
||||||
|
to = !currentValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item.updateValueKey === "string") {
|
||||||
|
to = { [item.updateValueKey]: to }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof item.emitEvent === "string") {
|
||||||
|
window.app.eventBus.emit(item.emitEvent, { event, to })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item.noStorage) {
|
||||||
|
window.app.configuration.settings.change(id, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ settings: { ...this.state.settings, [id]: to } })
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(event, id, type) {
|
renderItem = (item) => {
|
||||||
if (typeof id === "undefined") {
|
if (!item.type) {
|
||||||
console.error(`No setting id provided!`)
|
console.error(`Item [${item.id}] has no an type!`)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
if (typeof type !== "string") {
|
if (typeof ItemTypes[item.type] === "undefined") {
|
||||||
console.error(`Invalid eventType data-type, expecting string!`)
|
console.error(`Item [${item.id}] has an invalid type: ${item.type}`)
|
||||||
return false
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = window.app.configuration.settings.get(id) ?? false
|
if (typeof item.props === "undefined") {
|
||||||
let to = !value
|
item.props = {}
|
||||||
|
}
|
||||||
|
|
||||||
switch (type.toLowerCase()) {
|
// fix handlers
|
||||||
case "button": {
|
switch (item.type.toLowerCase()) {
|
||||||
window.app.configuration.settings.events.emit("changeSetting", { event, id, value, to })
|
case "slidercolorpicker": {
|
||||||
|
item.props.onChange = (color) => {
|
||||||
|
item.props.color = color.hex
|
||||||
|
}
|
||||||
|
item.props.onChangeComplete = (color, event) => {
|
||||||
|
this.handleEvent(event, item, color.hex)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "switch": {
|
||||||
|
item.props.checked = this.state.settings[item.id]
|
||||||
|
item.props.onClick = (event) => this.handleEvent(event, item)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
this._set(id, to)
|
if (!item.props.children) {
|
||||||
|
item.props.children = item.title ?? item.id
|
||||||
|
}
|
||||||
|
item.props.value = this.state.settings[item.id]
|
||||||
|
item.props.onClick = (event) => this.handleEvent(event, item)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
generateMenu(data) {
|
return (
|
||||||
let items = {}
|
<div key={item.id} className="settingItem">
|
||||||
|
<div className="header">
|
||||||
const renderGroupItems = (group) => {
|
<div>
|
||||||
return items[group].map((item) => {
|
|
||||||
if (!item.type) {
|
|
||||||
console.error(`Item [${item.id}] has no an type!`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (typeof ItemTypes[item.type] === "undefined") {
|
|
||||||
console.error(`Item [${item.id}] has an invalid type: ${item.type}`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof item.props === "undefined") {
|
|
||||||
item.props = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix handlers
|
|
||||||
switch (item.type.toLowerCase()) {
|
|
||||||
case "colorpicker": {
|
|
||||||
item.props.onChange = (value) => {
|
|
||||||
item.props.color = value.hex
|
|
||||||
}
|
|
||||||
item.props.onChangeComplete = (color, event) => {
|
|
||||||
window.app.configuration.settings.events.emit("changeSetting", { id: item.id, event, value: color })
|
|
||||||
this._set(item.id, color.hex)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "switch": {
|
|
||||||
item.props.children = item.title ?? item.id
|
|
||||||
item.props.checked = this.state.settings[item.id]
|
|
||||||
item.props.onClick = (e) => this.handleEvent(e, item.id ?? "anon", item.type)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
item.props.children = item.title ?? item.id
|
|
||||||
item.props.value = this.state.settings[item.id]
|
|
||||||
item.props.onClick = (e) => this.handleEvent(e, item.id ?? "anon", item.type)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div key={item.id}>
|
|
||||||
<h5>
|
<h5>
|
||||||
{item.icon ? React.createElement(Icons[item.icon]) : null}
|
{item.icon ? React.createElement(Icons[item.icon]) : null}
|
||||||
{item.title ?? item.id}
|
{item.title ?? item.id}
|
||||||
</h5>
|
</h5>
|
||||||
{item.render ??
|
|
||||||
React.createElement(ItemTypes[item.type], {
|
|
||||||
...item.props,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
<div>
|
||||||
})
|
{item.experimental && <antd.Tag> Experimental </antd.Tag>}
|
||||||
}
|
|
||||||
|
|
||||||
const renderGroupDecorator = (group) => {
|
|
||||||
if (group === "none") {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const fromDecoratorIcon = groupsDecorator[group]?.icon
|
|
||||||
const fromDecoratorTitle = groupsDecorator[group]?.title
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
{fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null}{" "}
|
|
||||||
{fromDecoratorTitle ?? group}
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
|
||||||
data.forEach((item) => {
|
|
||||||
if (typeof item.group == "undefined") {
|
|
||||||
item.group = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!items[item.group]) {
|
|
||||||
items[item.group] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
items[item.group].push(item)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(items).map((group) => {
|
|
||||||
return (
|
|
||||||
<div key={group} style={{ marginBottom: "30px" }}>
|
|
||||||
{renderGroupDecorator(group)}
|
|
||||||
<div key={group} className="settings_groupItems">
|
|
||||||
{renderGroupItems(group)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
<div className="component">
|
||||||
})
|
{React.createElement(ItemTypes[item.type], item.props)}
|
||||||
}
|
|
||||||
|
|
||||||
renderAboutApp() {
|
|
||||||
const appConfig = config.app
|
|
||||||
const eviteNamespace = window.__evite
|
|
||||||
const isDevMode = eviteNamespace.env.NODE_ENV !== "production"
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="settings_about_app">
|
|
||||||
<div>{appConfig.siteName}</div>
|
|
||||||
<div>
|
|
||||||
<antd.Tag>
|
|
||||||
<Icons.Tag />v{eviteNamespace.projectVersion}
|
|
||||||
</antd.Tag>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<antd.Tag color={isDevMode ? "magenta" : "green"}>
|
|
||||||
{isDevMode ? <Icons.Triangle /> : <Icons.Box />}
|
|
||||||
{isDevMode ? "development" : "stable"}
|
|
||||||
</antd.Tag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLogout() {
|
renderGroup = (key, group) => {
|
||||||
if (window.app.isValidSession()) {
|
const fromDecoratorIcon = groupsDecorator[key]?.icon
|
||||||
return (
|
const fromDecoratorTitle = groupsDecorator[key]?.title
|
||||||
<div>
|
|
||||||
<antd.Button
|
|
||||||
onClick={() => {
|
|
||||||
Session.logout()
|
|
||||||
}}
|
|
||||||
type="danger"
|
|
||||||
>
|
|
||||||
Logout
|
|
||||||
</antd.Button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div></div>
|
return (
|
||||||
|
<div key={key} className="group">
|
||||||
|
<h1>
|
||||||
|
{fromDecoratorIcon ? React.createElement(Icons[fromDecoratorIcon]) : null}
|
||||||
|
{fromDecoratorTitle ?? key}
|
||||||
|
</h1>
|
||||||
|
<div className="content">
|
||||||
|
{group.map((item) => this.renderItem(item))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSettings = (data) => {
|
||||||
|
let groups = {}
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
if (!groups[item.group]) {
|
||||||
|
groups[item.group] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[item.group].push(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.keys(groups).map((groupKey) => {
|
||||||
|
return this.renderGroup(groupKey, groups[groupKey])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const isDevMode = window.__evite.env.NODE_ENV !== "production"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="settings">
|
||||||
{this.generateMenu(settingList)}
|
{this.generateSettings(settingList)}
|
||||||
<div className="settings_bottom_items">
|
<div className="footer">
|
||||||
{this.renderLogout()}
|
<div>
|
||||||
{this.renderAboutApp()}
|
<div>{config.app?.siteName}</div>
|
||||||
<antd.Button type="link" onClick={() => AboutApp.openModal()}>
|
<div>
|
||||||
About
|
<antd.Tag>
|
||||||
</antd.Button>
|
<Icons.Tag />v{window.__evite.projectVersion}
|
||||||
|
</antd.Tag>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<antd.Tag color={isDevMode ? "magenta" : "green"}>
|
||||||
|
{isDevMode ? <Icons.Triangle /> : <Icons.Box />}
|
||||||
|
{isDevMode ? "development" : "stable"}
|
||||||
|
</antd.Tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<antd.Button type="link" onClick={() => AboutApp.openModal()}>
|
||||||
|
About
|
||||||
|
</antd.Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const controller = {
|
|
||||||
open: (key) => {
|
|
||||||
// TODO: Scroll to content
|
|
||||||
window.app.DrawerController.open("settings", SettingsMenu, {
|
|
||||||
props: {
|
|
||||||
width: "45%",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
close: () => {
|
|
||||||
window.app.DrawerController.close("settings")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export default controller
|
|
@ -1,44 +1,81 @@
|
|||||||
.settings_groupItems{
|
.settings {
|
||||||
> div {
|
display: flex;
|
||||||
padding: 12px 30px;
|
flex-direction: column;
|
||||||
}
|
|
||||||
|
> div {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
> div {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingItem {
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
h5{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.component {
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
font-family: "Space Mono", monospace;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.ant-tag {
|
||||||
|
height: 18px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding: 0 7px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings_about_app{
|
|
||||||
font-family: 'Space Mono', monospace;
|
|
||||||
font-size: 10px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.ant-tag{
|
|
||||||
height: 18px;
|
|
||||||
line-height: 18px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
padding: 0 7px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.settings_bottom_items {
|
|
||||||
position: relative;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import * as antd from 'antd'
|
|
||||||
import { Icons } from 'components/Icons'
|
|
||||||
import { FormattedMessage } from 'react-intl'
|
|
||||||
|
|
||||||
export default class ButtonMenu extends React.Component {
|
|
||||||
handleClickMenu(id) {
|
|
||||||
const element = document.getElementById(id)
|
|
||||||
if (typeof (element) !== "undefined") {
|
|
||||||
try {
|
|
||||||
element.focus()
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof (this.props.onClick) !== "undefined") {
|
|
||||||
this.props.onClick(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMenus() {
|
|
||||||
return this.props?.menus?.map((e) => {
|
|
||||||
return <antd.Button onClick={() => this.handleClickMenu(e.id)} id={e.id ?? Math.random} key={e.id} className={window.classToStyle("indexMenuItem")}>
|
|
||||||
<div className="icon">{e.icon ? React.createElement(Icons[e.icon], { style: e.iconStyle ?? null }) : null}</div>
|
|
||||||
<div className="title"><FormattedMessage id={e.title} defaultMessage={e.title} /></div>
|
|
||||||
</antd.Button>
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className={window.classToStyle("indexMenu")} >
|
|
||||||
{this.renderMenus()}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -392,4 +392,4 @@ export default class FormGenerator extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,19 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
const Marker = ({ text }) => <div style={{ display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column", width: "auto", height: "auto", fontSize: "17px", color: "#333333" }}>
|
|
||||||
<div style={{ backgroundColor: "rgba(66, 117, 245, 0.4)", height: '35px', width: "35px", borderRadius: "24px" }} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
export default (props) => {
|
|
||||||
if (typeof(props.size) == "undefined") {
|
|
||||||
props.size = "420px"
|
|
||||||
}
|
|
||||||
return <div style={{ height: props.size, width: props.size }}>
|
|
||||||
<iframe
|
|
||||||
style={{ border: 0 }}
|
|
||||||
src={`https://maps.google.com/maps?q=${props.lat},${props.lng}&z=16&output=embed`}
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
}
|
|
@ -1,27 +1,17 @@
|
|||||||
import FormGenerator from './formGenerator'
|
export { default as FormGenerator } from "./FormGenerator"
|
||||||
import ButtonMenu from './buttonMenu'
|
export { default as Settings } from "./Settings"
|
||||||
import * as AboutApp from './AboutApp'
|
export { default as NotFound } from "./NotFound"
|
||||||
|
export { default as AppSearcher } from "./AppSearcher"
|
||||||
|
export { default as RenderError } from "./RenderError"
|
||||||
|
|
||||||
export { default as LoadingSpinner } from './LoadingSpinner'
|
export { default as Sessions } from "./Sessions"
|
||||||
export { default as Settings } from './Settings'
|
export { default as ActionsBar } from "./ActionsBar"
|
||||||
export { default as NotFound } from './notFound'
|
export { default as SelectableList } from "./SelectableList"
|
||||||
export { default as AppLoading } from './AppLoading'
|
export { default as ObjectInspector } from "./ObjectInspector"
|
||||||
export { default as AppSearcher } from './AppSearcher'
|
export { default as FabricCreator } from "./FabricCreator"
|
||||||
export { default as RenderError } from './RenderError'
|
export { default as ServerStatus } from "./ServerStatus"
|
||||||
|
export { default as ModifierTag } from "./ModifierTag"
|
||||||
export { default as ElementsList } from './ElementsList'
|
|
||||||
export { default as Sessions } from './Sessions'
|
|
||||||
export { default as Roles } from './Roles'
|
|
||||||
export { default as ActionsBar } from './ActionsBar'
|
|
||||||
export { default as SelectableList } from './SelectableList'
|
|
||||||
export { default as ObjectInspector } from './ObjectInspector'
|
|
||||||
export { default as FabricCreator } from './FabricCreator'
|
|
||||||
|
|
||||||
|
export * as AboutApp from "./AboutApp"
|
||||||
export * as QRReader from "./QRReader"
|
export * as QRReader from "./QRReader"
|
||||||
export * as Window from './RenderWindow'
|
export * as Window from "./RenderWindow"
|
||||||
|
|
||||||
export {
|
|
||||||
AboutApp,
|
|
||||||
ButtonMenu,
|
|
||||||
FormGenerator,
|
|
||||||
}
|
|
76
packages/app/src/components/modifierTag/index.jsx
Normal file
76
packages/app/src/components/modifierTag/index.jsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import React from "react"
|
||||||
|
import * as antd from "antd"
|
||||||
|
import { Icons, createIconRender } from "components/Icons"
|
||||||
|
|
||||||
|
export default (props) => {
|
||||||
|
const [loading, setLoading] = React.useState(false)
|
||||||
|
const [options, setOptions] = React.useState([])
|
||||||
|
const [value, setValue] = React.useState(null)
|
||||||
|
|
||||||
|
const onChangeProperties = async (update) => {
|
||||||
|
if (props.eventDisable) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
await props.onChangeProperties(update)
|
||||||
|
.then((data) => {
|
||||||
|
return setValue(update.join("-"))
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTagColor = () => {
|
||||||
|
if (props.colors) {
|
||||||
|
return props.colors[value]
|
||||||
|
}
|
||||||
|
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOptionsLoad = async (fn) => {
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
const result = await fn()
|
||||||
|
setOptions(result)
|
||||||
|
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDefaultValueLoad = async (fn) => {
|
||||||
|
const result = await fn()
|
||||||
|
setValue(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (typeof props.options === "function") {
|
||||||
|
handleOptionsLoad(props.options)
|
||||||
|
} else {
|
||||||
|
setOptions(props.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.defaultValue === "function") {
|
||||||
|
handleDefaultValueLoad(props.defaultValue)
|
||||||
|
} else {
|
||||||
|
setValue(props.defaultValue)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <antd.Cascader options={options} onChange={(update) => onChangeProperties(update)} >
|
||||||
|
<antd.Tag color={getTagColor()}>
|
||||||
|
{loading ? <Icons.LoadingOutlined spin /> :
|
||||||
|
<>
|
||||||
|
{Icons[props.icon] && createIconRender(props.icon)}
|
||||||
|
<h4>
|
||||||
|
{value}
|
||||||
|
</h4>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</antd.Tag>
|
||||||
|
</antd.Cascader>
|
||||||
|
}
|
@ -1,16 +1,12 @@
|
|||||||
import store from 'store'
|
import store from 'store'
|
||||||
import EventEmitter from "@foxify/events"
|
|
||||||
import { objectToArrayMap } from '@corenode/utils'
|
import { objectToArrayMap } from '@corenode/utils'
|
||||||
import handlers from 'core/handlers'
|
import defaultKeys from "schemas/defaultSettings.json"
|
||||||
|
|
||||||
const defaultKeys = import('schemas/defaultSettings.json')
|
|
||||||
|
|
||||||
class SettingsController {
|
class SettingsController {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.storeKey = "settings"
|
this.storeKey = "app_settings"
|
||||||
this.defaultSettings = defaultKeys
|
this.defaultSettings = defaultKeys
|
||||||
|
|
||||||
this.events = new EventEmitter()
|
|
||||||
this.settings = store.get(this.storeKey) ?? {}
|
this.settings = store.get(this.storeKey) ?? {}
|
||||||
|
|
||||||
objectToArrayMap(this.defaultSettings).forEach((entry) => {
|
objectToArrayMap(this.defaultSettings).forEach((entry) => {
|
||||||
@ -19,12 +15,6 @@ class SettingsController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.events.on('changeSetting', (payload) => {
|
|
||||||
if (typeof handlers[payload.id] === "function") {
|
|
||||||
handlers[payload.id](payload)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +37,7 @@ class SettingsController {
|
|||||||
let value = to ?? !this.settings[key] ?? true
|
let value = to ?? !this.settings[key] ?? true
|
||||||
|
|
||||||
this.set(key, value)
|
this.set(key, value)
|
||||||
this.events.emit("changeSetting", { key, value, to })
|
window.app.eventBus.emit("changeSetting", { key, value, to })
|
||||||
|
|
||||||
return this.settings
|
return this.settings
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import handlers from './handlers'
|
|
||||||
import { message, notification } from 'antd'
|
|
||||||
|
|
||||||
const events = {
|
|
||||||
invalidSidebarKey: (event) => {
|
|
||||||
console.error(`[invalidSidebarKey] >>`, event)
|
|
||||||
notification.error({
|
|
||||||
message: `An error seems to have occurred trying to open this.`,
|
|
||||||
description: "We will report this automatically"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default events
|
|
@ -1,7 +0,0 @@
|
|||||||
const handlers = {
|
|
||||||
"edit_sidebar": () => {
|
|
||||||
window.app.SidebarController.toogleEdit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handlers
|
|
@ -1,186 +0,0 @@
|
|||||||
import { cloneDeep } from 'lodash'
|
|
||||||
import store from 'store'
|
|
||||||
import { pathToRegexp } from 'path-to-regexp'
|
|
||||||
import config from 'config'
|
|
||||||
|
|
||||||
const languages = config.i18n ? config.i18n.languages.map(item => item.key) : []
|
|
||||||
const defaultLanguage = config.i18n ? config.i18n.defaultLanguage : 'en'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query objects that specify keys and values in an array where all values are objects.
|
|
||||||
* @param {array} array An array where all values are objects, like [{key:1},{key:2}].
|
|
||||||
* @param {string} key The key of the object that needs to be queried.
|
|
||||||
* @param {string} value The value of the object that needs to be queried.
|
|
||||||
* @return {object|undefined} Return frist object when query success.
|
|
||||||
*/
|
|
||||||
export function queryArray(array, key, value) {
|
|
||||||
if (!Array.isArray(array)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return array.find(_ => _[key] === value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert an array to a tree-structured array.
|
|
||||||
* @param {array} array The Array need to Converted.
|
|
||||||
* @param {string} id The alias of the unique ID of the object in the array.
|
|
||||||
* @param {string} parentId The alias of the parent ID of the object in the array.
|
|
||||||
* @param {string} children The alias of children of the object in the array.
|
|
||||||
* @return {array} Return a tree-structured array.
|
|
||||||
*/
|
|
||||||
export function arrayToTree(
|
|
||||||
array,
|
|
||||||
id = 'id',
|
|
||||||
parentId = 'pid',
|
|
||||||
children = 'children',
|
|
||||||
) {
|
|
||||||
const result = []
|
|
||||||
const hash = {}
|
|
||||||
const data = cloneDeep(array)
|
|
||||||
|
|
||||||
data.forEach((item, index) => {
|
|
||||||
hash[data[index][id]] = data[index];
|
|
||||||
})
|
|
||||||
|
|
||||||
data.forEach(item => {
|
|
||||||
const hashParent = hash[item[parentId]]
|
|
||||||
if (hashParent) {
|
|
||||||
!hashParent[children] && (hashParent[children] = [])
|
|
||||||
hashParent[children].push(item)
|
|
||||||
} else {
|
|
||||||
result.push(item)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In an array object, traverse all parent IDs based on the value of an object.
|
|
||||||
* @param {array} array The Array need to Converted.
|
|
||||||
* @param {string} current Specify the value of the object that needs to be queried.
|
|
||||||
* @param {string} parentId The alias of the parent ID of the object in the array.
|
|
||||||
* @param {string} id The alias of the unique ID of the object in the array.
|
|
||||||
* @return {array} Return a key array.
|
|
||||||
*/
|
|
||||||
export function queryPathKeys(array, current, parentId, id = 'id') {
|
|
||||||
const result = [current]
|
|
||||||
const hashMap = new Map()
|
|
||||||
array.forEach(item => hashMap.set(item[id], item))
|
|
||||||
|
|
||||||
const getPath = current => {
|
|
||||||
const currentParentId = hashMap.get(current)[parentId]
|
|
||||||
if (currentParentId) {
|
|
||||||
result.push(currentParentId)
|
|
||||||
getPath(currentParentId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPath(current)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query which layout should be used for the current path based on the configuration.
|
|
||||||
* @param {layouts} layouts Layout configuration.
|
|
||||||
* @param {pathname} pathname Path name to be queried.
|
|
||||||
* @return {string} Return frist object when query success.
|
|
||||||
*/
|
|
||||||
export function queryLayout(layouts, pathname) {
|
|
||||||
let result = 'public'
|
|
||||||
|
|
||||||
const isMatch = regepx => {
|
|
||||||
return regepx instanceof RegExp
|
|
||||||
? regepx.test(pathname)
|
|
||||||
: pathToRegexp(regepx).exec(pathname)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const item of layouts) {
|
|
||||||
let include = false
|
|
||||||
let exclude = false
|
|
||||||
if (item.include) {
|
|
||||||
for (const regepx of item.include) {
|
|
||||||
if (isMatch(regepx)) {
|
|
||||||
include = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (include && item.exclude) {
|
|
||||||
for (const regepx of item.exclude) {
|
|
||||||
if (isMatch(regepx)) {
|
|
||||||
exclude = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (include && !exclude) {
|
|
||||||
result = item.name
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLocale() {
|
|
||||||
return store.get('locale') || defaultLanguage
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setLocale(language) {
|
|
||||||
if (getLocale() !== language) {
|
|
||||||
store.set('locale', language)
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function queryIndexer(array, callback, params) {
|
|
||||||
if (!array) return false
|
|
||||||
if (typeof (pathMatchRegexp) == "undefined") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(array)) {
|
|
||||||
let opt = {
|
|
||||||
regex: /:id/gi
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params) {
|
|
||||||
opt = { ...opt, ...params }
|
|
||||||
}
|
|
||||||
|
|
||||||
array.forEach((e) => {
|
|
||||||
if (e.match != null && e.to != null) {
|
|
||||||
const pathMatch = pathMatchRegexp(e.match, window.location.pathname)
|
|
||||||
if (pathMatch != null) {
|
|
||||||
return callback(e.to.replace(opt.regex, pathMatch[1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateGUID(lenght = 6) {
|
|
||||||
let text = ""
|
|
||||||
const possibleChars = "abcdefghijklmnopqrstuvwxyz0123456789"
|
|
||||||
|
|
||||||
for (let i = 0; i < 6; i++)
|
|
||||||
text += possibleChars.charAt(Math.floor(Math.random() * possibleChars.length))
|
|
||||||
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateRandomId(length = 15) {
|
|
||||||
return Math.random().toString(36).substring(0, length)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
export function geteventBus() {
|
|
||||||
if (typeof window.app.eventBus !== "undefined") {
|
|
||||||
return window.app.eventBus
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
export { config, languages, defaultLanguage }
|
|
@ -1,98 +0,0 @@
|
|||||||
import { verbosity } from '@corenode/utils'
|
|
||||||
const inmutableKey = ["_params"]
|
|
||||||
|
|
||||||
export class Controller {
|
|
||||||
constructor(params) {
|
|
||||||
this.params = params
|
|
||||||
|
|
||||||
this.id = params.id
|
|
||||||
// this.scope = ["window"]
|
|
||||||
this.scopeWindow = params.scopeWindow ?? true
|
|
||||||
|
|
||||||
this.freezedKeys = []
|
|
||||||
this.lockController = params.locked ?? false
|
|
||||||
|
|
||||||
this.register({ _params: params })
|
|
||||||
}
|
|
||||||
|
|
||||||
_canDestroy(key) {
|
|
||||||
if (!inmutableKey.includes(key) && !this.freezedKeys.includes(key)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_initWindowScope() {
|
|
||||||
if (typeof (window.controllers) == "undefined") {
|
|
||||||
window.controllers = Object
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
register(controller) {
|
|
||||||
// this.scope.forEach((key, index) => {
|
|
||||||
|
|
||||||
// })
|
|
||||||
if (this.scopeWindow) {
|
|
||||||
if (typeof (window.controllers) == "undefined") {
|
|
||||||
this._initWindowScope()
|
|
||||||
}
|
|
||||||
|
|
||||||
window.controllers[this.id] = controller
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
freeze = {
|
|
||||||
_keys: () => {
|
|
||||||
return this.freezedKeys
|
|
||||||
},
|
|
||||||
isLock: (key) => {
|
|
||||||
if (key === "_self") {
|
|
||||||
return this.lockController
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._canDestroy(key)
|
|
||||||
},
|
|
||||||
lock: (key) => {
|
|
||||||
if (key === "_self") {
|
|
||||||
return this.lockController = true
|
|
||||||
}
|
|
||||||
this.freezedKeys.push(key)
|
|
||||||
},
|
|
||||||
unlock: (key) => {
|
|
||||||
if (key === "_self") {
|
|
||||||
return this.lockController = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const updated = this.freezedKeys.filter(function (value, index, arr) {
|
|
||||||
return value !== key
|
|
||||||
})
|
|
||||||
this.freezedKeys = updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add(key, method, options, events) {
|
|
||||||
this[key] = method
|
|
||||||
|
|
||||||
window.controllers[this.id][key] = method
|
|
||||||
|
|
||||||
if (options?.lock) {
|
|
||||||
this.freeze.lock(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(key) {
|
|
||||||
if (this._canDestroy(key)) {
|
|
||||||
return delete window.controllers[this.id][key]
|
|
||||||
}
|
|
||||||
verbosity.warn(`It is not possible to destroy this key because it is locked`)
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
if (!this.lockController) {
|
|
||||||
return delete window.controllers[this.id]
|
|
||||||
}
|
|
||||||
verbosity.warn(`It is not possible to destroy this controller because it is locked`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Controller
|
|
@ -1,157 +0,0 @@
|
|||||||
// Prototype for nodecore module
|
|
||||||
import { verbosity } from '@corenode/utils'
|
|
||||||
import store from 'store'
|
|
||||||
|
|
||||||
export class DJail {
|
|
||||||
constructor(params) {
|
|
||||||
this.storeKey = params.name
|
|
||||||
this.voidMutation = params.voidMutation ?? false
|
|
||||||
this.objectType = params.type ?? "object"
|
|
||||||
this.data = null
|
|
||||||
|
|
||||||
if (!this.storeKey) {
|
|
||||||
throw new Error(`Invalid or missing store name`)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.objectType) {
|
|
||||||
case "object": {
|
|
||||||
this.data = new Object()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
this.data = new Array()
|
|
||||||
this.data[0] = {}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
this.data = new Object()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_pull() {
|
|
||||||
const storaged = store.get(this.storeKey)
|
|
||||||
|
|
||||||
if (storaged) {
|
|
||||||
this.data = store.get(this.storeKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
|
|
||||||
_push(update) {
|
|
||||||
if (typeof update !== "undefined") {
|
|
||||||
switch (this.objectType) {
|
|
||||||
case "object": {
|
|
||||||
this.data = { ...this.data, ...update }
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
this.data = [...this.data, ...update]
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
store.set(this.storeKey, this.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
getValue(key) {
|
|
||||||
try {
|
|
||||||
return this.get(key)[key]
|
|
||||||
} catch (error) {
|
|
||||||
verbosity.error(error)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get(query) {
|
|
||||||
this._pull()
|
|
||||||
if (!query) {
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
|
|
||||||
let scope = []
|
|
||||||
let matched = {}
|
|
||||||
|
|
||||||
if (Array.isArray(query)) {
|
|
||||||
scope = query
|
|
||||||
} else {
|
|
||||||
scope.push(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.forEach((key) => {
|
|
||||||
switch (this.objectType) {
|
|
||||||
case "object": {
|
|
||||||
matched[key] = this.data[key]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
const adresses = this.data[0]
|
|
||||||
matched[key] = this.data[adresses[key]]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key, value) {
|
|
||||||
this._pull()
|
|
||||||
|
|
||||||
switch (this.objectType) {
|
|
||||||
case "object": {
|
|
||||||
if (typeof (value) == "undefined") {
|
|
||||||
if (!this.voidMutation) {
|
|
||||||
verbosity.warn(`voidMutation is enabled, no changes on key [${key}]`)
|
|
||||||
return settings
|
|
||||||
}
|
|
||||||
verbosity.warn(`voidMutation is not enabled, undefined values causes key removal`)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data[key] = value
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
this.data.push(value)
|
|
||||||
this.data[0][key] = (this.data.length - 1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._push()
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(key) {
|
|
||||||
switch (this.objectType) {
|
|
||||||
case "object": {
|
|
||||||
delete this.data[key]
|
|
||||||
this._push()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case "array": {
|
|
||||||
this.data.filter(item => item.key !== key)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DJail
|
|
@ -1,5 +0,0 @@
|
|||||||
import { Controller } from './controller'
|
|
||||||
import DJail from './djail'
|
|
||||||
|
|
||||||
export { default as settings } from './settings'
|
|
||||||
export { Controller, DJail }
|
|
@ -1,5 +0,0 @@
|
|||||||
import config from 'config'
|
|
||||||
import DJail from '../djail'
|
|
||||||
|
|
||||||
export const settings = new DJail({ name: config.app?.storage?.settings ?? "settings", voidMutation: true })
|
|
||||||
export default settings
|
|
@ -1,70 +0,0 @@
|
|||||||
import store from 'store'
|
|
||||||
import EventEmitter from "@foxify/events"
|
|
||||||
import { objectToArrayMap } from '@corenode/utils'
|
|
||||||
import handlers from 'core/handlers'
|
|
||||||
|
|
||||||
const defaultKeys = import('schemas/defaultSettings.json')
|
|
||||||
|
|
||||||
class SettingsController {
|
|
||||||
constructor() {
|
|
||||||
this.storeKey = "app_settings"
|
|
||||||
this.defaultSettings = defaultKeys
|
|
||||||
|
|
||||||
this.events = new EventEmitter()
|
|
||||||
this.settings = store.get(this.storeKey) ?? {}
|
|
||||||
|
|
||||||
objectToArrayMap(this.defaultSettings).forEach((entry) => {
|
|
||||||
if (typeof this.settings[entry.key] === "undefined") {
|
|
||||||
this.settings[entry.key] = entry.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.events.on('changeSetting', (payload) => {
|
|
||||||
if (typeof handlers[payload.id] === "function") {
|
|
||||||
handlers[payload.id](payload)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
_pull() {
|
|
||||||
this.settings = { ...this.settings, ...store.get(this.storeKey) }
|
|
||||||
}
|
|
||||||
|
|
||||||
_push(update) {
|
|
||||||
if (typeof update !== "undefined") {
|
|
||||||
this.settings = { ...this.settings, ...update }
|
|
||||||
}
|
|
||||||
store.set(this.storeKey, this.settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
is = (key, value) => {
|
|
||||||
return this.settings[key] === value ? true : false
|
|
||||||
}
|
|
||||||
|
|
||||||
change = (key, to) => {
|
|
||||||
let value = to ?? !this.settings[key] ?? true
|
|
||||||
|
|
||||||
this.set(key, value)
|
|
||||||
this.events.emit("changeSetting", { key, value, to })
|
|
||||||
|
|
||||||
return this.settings
|
|
||||||
}
|
|
||||||
|
|
||||||
set = (key, value) => {
|
|
||||||
this.settings[key] = value
|
|
||||||
store.set(this.storeKey, this.settings)
|
|
||||||
|
|
||||||
return this.settings
|
|
||||||
}
|
|
||||||
|
|
||||||
get = (key) => {
|
|
||||||
if (typeof key === "undefined") {
|
|
||||||
return this.settings
|
|
||||||
}
|
|
||||||
return this.settings[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SettingsController
|
|
@ -1,46 +0,0 @@
|
|||||||
import store from 'store'
|
|
||||||
import defaultKeys from 'schemas/defaultSidebar.json'
|
|
||||||
|
|
||||||
class SidebarController {
|
|
||||||
constructor() {
|
|
||||||
this.storeKey = "app_sidebar"
|
|
||||||
this.defaults = defaultKeys
|
|
||||||
|
|
||||||
this.data = store.get(this.storeKey) ?? this.defaults
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
_pull = () => {
|
|
||||||
this.data = [...this.data, ...store.get(this.storeKey)]
|
|
||||||
}
|
|
||||||
|
|
||||||
_push = (update) => {
|
|
||||||
if (typeof update !== "undefined") {
|
|
||||||
this.data = update
|
|
||||||
}
|
|
||||||
store.set(this.storeKey, this.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
set = (value) => {
|
|
||||||
this.data.push(value)
|
|
||||||
this._push()
|
|
||||||
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
|
|
||||||
all = () => {
|
|
||||||
let objs = []
|
|
||||||
|
|
||||||
this.data.forEach((entry) => {
|
|
||||||
objs.push(entry)
|
|
||||||
})
|
|
||||||
|
|
||||||
return objs
|
|
||||||
}
|
|
||||||
|
|
||||||
get = () => {
|
|
||||||
return this.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SidebarController
|
|
@ -1,15 +0,0 @@
|
|||||||
import { User } from "models"
|
|
||||||
|
|
||||||
export function hasPermissions() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function hasAdmin() {
|
|
||||||
const roles = await User.roles
|
|
||||||
|
|
||||||
if (!roles) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.isArray(roles) && roles.includes("admin")
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
return <div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import ReactJson from "react-json-view"
|
|
||||||
|
|
||||||
import "./index.less"
|
|
||||||
|
|
||||||
export default class EviteDebugger extends React.Component {
|
|
||||||
static bindMain = "all"
|
|
||||||
|
|
||||||
getClassname(classname) {
|
|
||||||
return `evite-debugger_${classname}`
|
|
||||||
}
|
|
||||||
|
|
||||||
getExtensions() {
|
|
||||||
const extensions = {}
|
|
||||||
|
|
||||||
Array.from(this.props.contexts.main.extensionsKeys).forEach((extension) => {
|
|
||||||
extensions[extension.key] = extension
|
|
||||||
})
|
|
||||||
|
|
||||||
return extensions
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { app, main } = this.props.contexts
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className={this.getClassname("content")}>
|
|
||||||
<div>
|
|
||||||
<h4>📦 Namespace</h4>
|
|
||||||
<div>
|
|
||||||
<ReactJson name="window.__evite" collapsed="true" src={window.__evite} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>📦 AppContext</h4>
|
|
||||||
<div>
|
|
||||||
<ReactJson name="app" collapsed="true" src={this.props.contexts.app} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>📦 MainContext</h4>
|
|
||||||
<div>
|
|
||||||
<ReactJson name="app" collapsed="true" src={this.props.contexts.main} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h4>🧰 Extensions</h4>
|
|
||||||
<div>
|
|
||||||
<ReactJson name="extensions" collapsed="true" src={getExtensions()} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
.evite-debugger_warning_bindingDisabled {
|
|
||||||
color: rgb(255, 166, 0);
|
|
||||||
padding: 10px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.evite-debugger_content{
|
|
||||||
> div {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
border: 0.3px solid #ccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 5px;
|
|
||||||
font-size: 12px;
|
|
||||||
color:aliceblue;
|
|
||||||
margin: 5px;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export { default as less } from './less'
|
|
||||||
export { default as api } from './api'
|
|
||||||
export { default as evite } from './evite'
|
|
||||||
export { default as workload } from './workload'
|
|
@ -1,11 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import less from "less"
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
console.log(less)
|
|
||||||
|
|
||||||
return <div>
|
|
||||||
<h4>Current Variables</h4>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import RJson from "react-json-view"
|
|
||||||
|
|
||||||
import "./index.less"
|
|
||||||
|
|
||||||
export default class WorkloadDebugger extends React.Component {
|
|
||||||
binding = window.app.debug.bindings["workload_list"]
|
|
||||||
|
|
||||||
addTestWorkload = () => {
|
|
||||||
this.binding.addWorkload({
|
|
||||||
_id: "test",
|
|
||||||
name: "Test Workload",
|
|
||||||
status: "funny",
|
|
||||||
})
|
|
||||||
this.forceUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTestWorkload = () => {
|
|
||||||
this.binding.deleteWorkload("test")
|
|
||||||
this.forceUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
this.binding = window.app.debug.bindings["workload_list"]
|
|
||||||
|
|
||||||
if (!this.binding) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>Workload binding not available</h2>
|
|
||||||
|
|
||||||
<button onClick={() => this.forceUpdate()}>reload</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="wrapper">
|
|
||||||
<div key="statement" className="section">
|
|
||||||
<h4>State</h4>
|
|
||||||
<div>
|
|
||||||
<RJson name="state.workloads" collapsed="true" src={this.binding.state.workloads} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div key="test_item" className="section">
|
|
||||||
<h4>Test item</h4>
|
|
||||||
<div>
|
|
||||||
<button onClick={() => this.addTestWorkload()}>Add</button>
|
|
||||||
<button onClick={() => this.removeTestWorkload()}>Delete All</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
.wrapper {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
> div {
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
import config from 'config'
|
import config from 'config'
|
||||||
import { Bridge } from "linebridge/client"
|
import { Bridge } from "linebridge/client"
|
||||||
import { Session } from "models"
|
import { Session } from "models"
|
||||||
|
import io from "socket.io-client"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
key: "apiBridge",
|
key: "apiBridge",
|
||||||
@ -9,8 +10,23 @@ export default {
|
|||||||
mutateContext: {
|
mutateContext: {
|
||||||
async initializeDefaultBridge() {
|
async initializeDefaultBridge() {
|
||||||
this.apiBridge = await this.createBridge()
|
this.apiBridge = await this.createBridge()
|
||||||
|
this.ws = io(config.ws.address, { transports: ["websocket"] })
|
||||||
|
|
||||||
window.app.apiBridge = this.apiBridge
|
this.ws.on("connect", (...context) => {
|
||||||
|
window.app.eventBus.emit("websocket_connected", ...context)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.ws.on("disconnect", (...context) => {
|
||||||
|
window.app.eventBus.emit("websocket_disconnected", ...context)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.ws.on("connect_error", (...context) => {
|
||||||
|
window.app.eventBus.emit("websocket_connection_error", ...context)
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app.ws = this.ws
|
||||||
|
window.app.api = this.apiBridge
|
||||||
|
window.app.request = this.apiBridge.endpoints
|
||||||
},
|
},
|
||||||
createBridge: async () => {
|
createBridge: async () => {
|
||||||
const getSessionContext = () => {
|
const getSessionContext = () => {
|
||||||
@ -27,7 +43,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bridge = new Bridge({
|
const bridge = new Bridge({
|
||||||
origin: config.api?.address,
|
origin: config.api.address,
|
||||||
onRequestContext: getSessionContext,
|
onRequestContext: getSessionContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -38,7 +54,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return bridge.endpoints
|
return bridge
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,129 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import { Window } from "components"
|
|
||||||
import { Skeleton, Tabs } from "antd"
|
|
||||||
|
|
||||||
class DebuggerUI extends React.Component {
|
|
||||||
state = {
|
|
||||||
loading: true,
|
|
||||||
error: null,
|
|
||||||
debuggers: null,
|
|
||||||
active: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
toogleLoading = (to = !this.state.loading ?? false) => {
|
|
||||||
this.setState({ loading: to })
|
|
||||||
}
|
|
||||||
|
|
||||||
loadDebuggers = async () => {
|
|
||||||
this.toogleLoading(true)
|
|
||||||
|
|
||||||
const debuggers = await import(`@/debug`)
|
|
||||||
let renders = {}
|
|
||||||
|
|
||||||
Object.keys(debuggers).forEach((key) => {
|
|
||||||
renders[key] = debuggers[key]
|
|
||||||
})
|
|
||||||
|
|
||||||
this.setState({ debuggers: renders }, () => {
|
|
||||||
this.toogleLoading(false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount = async () => {
|
|
||||||
await this.loadDebuggers()
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidCatch = (error, info) => {
|
|
||||||
this.setState({ error })
|
|
||||||
}
|
|
||||||
|
|
||||||
onChangeTab = (key) => {
|
|
||||||
this.setState({ active: key, error: null })
|
|
||||||
}
|
|
||||||
|
|
||||||
renderError = (key, error) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2>Debugger Error</h2>
|
|
||||||
<i>
|
|
||||||
<h4>
|
|
||||||
Catch on [<strong>{key}</strong>]
|
|
||||||
</h4>
|
|
||||||
</i>
|
|
||||||
`<code>{error.message}</code>`
|
|
||||||
<hr />
|
|
||||||
<code>{error.stack}</code>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTabs = () => {
|
|
||||||
return Object.keys(this.state.debuggers).map((key) => {
|
|
||||||
return <Tabs.TabPane tab={key} key={key} />
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
renderDebugger = (_debugger) => {
|
|
||||||
try {
|
|
||||||
return React.createElement(window.app.bindContexts(_debugger))
|
|
||||||
} catch (error) {
|
|
||||||
return this.renderError(key, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { loading, error } = this.state
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <Skeleton active />
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Tabs onChange={this.onChangeTab}>{this.renderTabs()}</Tabs>
|
|
||||||
{error && this.renderError(this.state.active, error)}
|
|
||||||
{!this.state.active ? (
|
|
||||||
<div> Select an debugger to start </div>
|
|
||||||
) : (
|
|
||||||
this.renderDebugger(this.state.debuggers[this.state.active])
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Debugger {
|
|
||||||
constructor(mainContext, params = {}) {
|
|
||||||
this.mainContext = mainContext
|
|
||||||
this.params = { ...params }
|
|
||||||
|
|
||||||
this.bindings = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
openWindow = () => {
|
|
||||||
new Window.DOMWindow({ id: "debugger", children: window.app.bindContexts(DebuggerUI) }).create()
|
|
||||||
}
|
|
||||||
|
|
||||||
bind = (id, binding) => {
|
|
||||||
this.bindings[id] = binding
|
|
||||||
|
|
||||||
return binding
|
|
||||||
}
|
|
||||||
|
|
||||||
unbind = (id) => {
|
|
||||||
delete this.bindings[id]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
key: "visualDebugger",
|
|
||||||
expose: [
|
|
||||||
{
|
|
||||||
initialization: [
|
|
||||||
async (app, main) => {
|
|
||||||
main.setToWindowContext("debug", new Debugger(main))
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
export * as Render from './render'
|
export * as Render from './render'
|
||||||
export * as Splash from './splash'
|
export * as Splash from './splash'
|
||||||
export * as Sound from './sound'
|
export * as Sound from './sound'
|
||||||
export { default as API } from './api'
|
export * as Theme from './theme'
|
||||||
export { default as Debug } from './debug'
|
export { default as API } from './api'
|
||||||
export { default as Theme } from './theme'
|
|
@ -1,5 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import loadable from "@loadable/component"
|
import loadable from "@loadable/component"
|
||||||
|
import resolve from "pages"
|
||||||
|
|
||||||
export const ConnectWithApp = (component) => {
|
export const ConnectWithApp = (component) => {
|
||||||
return window.app.bindContexts(component)
|
return window.app.bindContexts(component)
|
||||||
@ -15,9 +16,17 @@ export function GetRoutesMap() {
|
|||||||
export const LazyRouteRender = (props) => {
|
export const LazyRouteRender = (props) => {
|
||||||
const component = loadable(async () => {
|
const component = loadable(async () => {
|
||||||
const location = window.location
|
const location = window.location
|
||||||
const path = props.path ?? location.pathname
|
let path = props.path ?? location.pathname
|
||||||
|
|
||||||
let module = await import(`/src/pages/${path}`).catch(() => {
|
if (path.startsWith("/")) {
|
||||||
|
path = path.substring(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const src = resolve(path)
|
||||||
|
console.log(src)
|
||||||
|
|
||||||
|
let module = await import(src).catch((err) => {
|
||||||
|
console.error(err)
|
||||||
return props.staticRenders?.NotFound ?? import("./statics/404")
|
return props.staticRenders?.NotFound ?? import("./statics/404")
|
||||||
})
|
})
|
||||||
module = module.default || module
|
module = module.default || module
|
||||||
@ -53,7 +62,11 @@ export class RenderRouter extends React.Component {
|
|||||||
lastHistoryState = null
|
lastHistoryState = null
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
shouldComponentUpdate() {
|
||||||
return window.location.pathname !== this.lastPathname || this.lastHistoryState !== window.app.history.location.state
|
if (this.lastPathname !== window.location.pathname || this.lastHistoryState !== window.app.history.location.state) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -117,8 +130,9 @@ export const extension = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
main.history.setLocation = (to, state) => {
|
main.history.setLocation = (to, state) => {
|
||||||
if (typeof to !== "string") {
|
const lastLocation = main.history.lastLocation
|
||||||
console.warn(`Invalid location`)
|
|
||||||
|
if (typeof lastLocation !== "undefined" && lastLocation?.pathname === to && lastLocation?.state === state) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +142,7 @@ export const extension = {
|
|||||||
main.history.push({
|
main.history.push({
|
||||||
pathname: to,
|
pathname: to,
|
||||||
}, state)
|
}, state)
|
||||||
|
main.history.lastLocation = main.history.location
|
||||||
}, defaultTransitionDelay)
|
}, defaultTransitionDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,18 +10,20 @@ export class SoundEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSounds = async () => {
|
getSounds = async () => {
|
||||||
const soundPack = await import(`http://${window.location.host}/src/assets/sounds`)
|
const origin = process.env.NODE_ENV === "development" ? `${window.location.origin}/src/assets/sounds/index.js` : `${window.location.origin}/assets/sounds/index.js`
|
||||||
let sounds = soundPack.default
|
|
||||||
|
|
||||||
Object.keys(soundPack.default).forEach((key) => {
|
let soundPack = await import(origin)
|
||||||
const src = `http://${window.location.host}${sounds[key]}`
|
soundPack = soundPack.default || soundPack
|
||||||
|
|
||||||
sounds[key] = new Howl({
|
Object.keys(soundPack).forEach((key) => {
|
||||||
|
const src = soundPack[key]
|
||||||
|
|
||||||
|
soundPack[key] = new Howl({
|
||||||
src: [src]
|
src: [src]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return sounds
|
return soundPack
|
||||||
}
|
}
|
||||||
|
|
||||||
play = (name) => {
|
play = (name) => {
|
||||||
|
@ -32,18 +32,21 @@ export const extension = (params = {}) => {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
`
|
`
|
||||||
|
|
||||||
document.body.appendChild(splashElement)
|
const show = () => {
|
||||||
ReactDOM.render(<SplashComponent logo={params.logo} props={params.props} />, splashElement)
|
document.body.appendChild(splashElement)
|
||||||
|
ReactDOM.render(<SplashComponent logo={params.logo} props={params.props} />, splashElement)
|
||||||
|
}
|
||||||
|
|
||||||
const removeSplash = () => {
|
const removeSplash = () => {
|
||||||
splashElement.style.animation = `${params.preset ?? "fadeOut"} ${fadeOutVelocity ?? 1000}ms`
|
splashElement.style.animation = `${params.preset ?? "fadeOut"} ${fadeOutVelocity}ms`
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
splashElement.remove()
|
splashElement.remove()
|
||||||
}, fadeOutVelocity ?? 1000)
|
}, fadeOutVelocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
main.eventBus.on("initialization_done", removeSplash)
|
main.eventBus.on("splash_show", show)
|
||||||
|
main.eventBus.on("splash_close", removeSplash)
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
.splash_wrapper {
|
.splash_wrapper {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #f0f2f5;
|
|
||||||
|
background-color: rgba(240, 242, 245, 0.8);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 100;
|
z-index: 1000;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -13,7 +15,6 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.splash_logo {
|
.splash_logo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -28,7 +29,7 @@
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
max-height: 50%;
|
max-height: 50%;
|
||||||
filter: drop-shadow(14px 10px 10px rgba(70, 70, 70, 0.5));
|
filter: drop-shadow(14px 10px 10px rgba(128, 128, 128, 0.5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +1,168 @@
|
|||||||
import React from "react"
|
|
||||||
import config from "config"
|
import config from "config"
|
||||||
|
import store from "store"
|
||||||
|
import { ConfigProvider } from "antd"
|
||||||
|
|
||||||
const themeConfig = config.theme ?? {}
|
export class ThemeController {
|
||||||
|
|
||||||
//themeConfig["primary-color"]
|
|
||||||
|
|
||||||
const BaseThemeVars = {
|
|
||||||
"primary-color": "#32b7bb",
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThemeController {
|
|
||||||
constructor(params) {
|
constructor(params) {
|
||||||
this.params = { ...params }
|
this.params = { ...params }
|
||||||
this.vars = Object()
|
|
||||||
this.root = document.documentElement
|
|
||||||
|
|
||||||
// init
|
this.themeManifestStorageKey = "theme"
|
||||||
this.init()
|
this.modificationStorageKey = "themeModifications"
|
||||||
|
this.variantStorageKey = "themeVariation"
|
||||||
|
|
||||||
|
this.theme = null
|
||||||
|
|
||||||
|
this.mutation = null
|
||||||
|
this.currentVariant = null
|
||||||
|
|
||||||
|
this.init()
|
||||||
|
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
init = () => {
|
static get currentVariant() {
|
||||||
Object.keys(BaseThemeVars).forEach(key => {
|
return document.documentElement.style.getPropertyValue("--themeVariant")
|
||||||
this.updateVar(key, BaseThemeVars[key])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
updateVar = (key, value) => {
|
|
||||||
return this.root.style.setProperty(`--${key}`, value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generate = (payload = {}) => {
|
init = () => {
|
||||||
const { variables } = payload
|
let theme = this.getStoragedTheme()
|
||||||
|
const modifications = this.getStoragedModifications()
|
||||||
|
const variantKey = this.getStoragedVariant()
|
||||||
|
|
||||||
|
if (!theme) {
|
||||||
|
// load default theme
|
||||||
|
theme = this.getDefaultTheme()
|
||||||
|
} else {
|
||||||
|
// load URL and initialize theme
|
||||||
|
}
|
||||||
|
|
||||||
|
// set global theme
|
||||||
|
this.theme = theme
|
||||||
|
|
||||||
|
// override with static vars
|
||||||
|
if (theme.staticVars) {
|
||||||
|
this.update(theme.staticVars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// override theme with modifications
|
||||||
|
if (modifications) {
|
||||||
|
this.update(modifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply variation
|
||||||
|
this.applyVariant(variantKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
load = (payload) => {
|
getRootVariables = () => {
|
||||||
|
let attributes = document.documentElement.getAttribute("style").trim().split(";")
|
||||||
|
attributes = attributes.slice(0, (attributes.length - 1))
|
||||||
|
attributes = attributes.map((variable) => {
|
||||||
|
let [key, value] = variable.split(":")
|
||||||
|
key = key.split("--")[1]
|
||||||
|
|
||||||
}
|
return [key, value]
|
||||||
|
})
|
||||||
|
|
||||||
|
return Object.fromEntries(attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultTheme = () => {
|
||||||
|
// TODO: Use evite CONSTANTS_API
|
||||||
|
return config.defaultTheme
|
||||||
|
}
|
||||||
|
|
||||||
|
getStoragedTheme = () => {
|
||||||
|
return store.get(this.themeManifestStorageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
getStoragedModifications = () => {
|
||||||
|
return store.get(this.modificationStorageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
getStoragedVariant = () => {
|
||||||
|
return store.get(this.variantStorageKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
setVariant = (variationKey) => {
|
||||||
|
return store.set(this.variantStorageKey, variationKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
setModifications = (modifications) => {
|
||||||
|
return store.set(this.modificationStorageKey, modifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
resetDefault = () => {
|
||||||
|
store.remove(this.themeManifestStorageKey)
|
||||||
|
store.remove(this.modificationStorageKey)
|
||||||
|
|
||||||
|
return this.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
update = (update) => {
|
||||||
|
if (typeof update !== "object") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mutation = {
|
||||||
|
...this.theme.staticVars,
|
||||||
|
...this.mutation,
|
||||||
|
...update
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(this.mutation).forEach(key => {
|
||||||
|
document.documentElement.style.setProperty(`--${key}`, this.mutation[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
document.documentElement.className = `theme-${this.currentVariant}`
|
||||||
|
document.documentElement.style.setProperty(`--themeVariant`, this.currentVariant)
|
||||||
|
|
||||||
|
ConfigProvider.config({ theme: this.mutation })
|
||||||
|
}
|
||||||
|
|
||||||
|
applyVariant = (variant = (this.theme.defaultVariant ?? "light")) => {
|
||||||
|
const values = this.theme.variants[variant]
|
||||||
|
|
||||||
|
if (values) {
|
||||||
|
this.currentVariant = variant
|
||||||
|
this.update(values)
|
||||||
|
this.setVariant(variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export const extension = {
|
||||||
key: "theme",
|
key: "theme",
|
||||||
expose: [
|
expose: [
|
||||||
{
|
{
|
||||||
initialization: [
|
initialization: [
|
||||||
async (app, main) => {
|
async (app, main) => {
|
||||||
app.themeController = new ThemeController()
|
app.ThemeController = new ThemeController()
|
||||||
main.setToWindowContext("themeController", app.themeController)
|
|
||||||
|
main.eventBus.on("darkMode", (payload) => {
|
||||||
|
if (payload.to) {
|
||||||
|
app.ThemeController.applyVariant("dark")
|
||||||
|
} else {
|
||||||
|
app.ThemeController.applyVariant("light")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
main.eventBus.on("modifyTheme", (payload) => {
|
||||||
|
if (payload.to) {
|
||||||
|
app.ThemeController.update(payload.to)
|
||||||
|
app.ThemeController.setModifications(app.ThemeController.mutation)
|
||||||
|
} else {
|
||||||
|
app.ThemeController.update(payload)
|
||||||
|
app.ThemeController.setModifications(app.ThemeController.mutation)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
main.eventBus.on("resetTheme", () => {
|
||||||
|
app.ThemeController.resetDefault()
|
||||||
|
})
|
||||||
|
|
||||||
|
main.setToWindowContext("ThemeController", app.ThemeController)
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default extension
|
@ -2,6 +2,8 @@ import React from "react"
|
|||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import EventEmitter from "@foxify/events"
|
import EventEmitter from "@foxify/events"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
export class Drawer extends React.Component {
|
export class Drawer extends React.Component {
|
||||||
options = this.props.options ?? {}
|
options = this.props.options ?? {}
|
||||||
events = new EventEmitter()
|
events = new EventEmitter()
|
||||||
@ -82,14 +84,18 @@ export class Drawer extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<antd.Drawer {...drawerProps}>
|
<antd.Drawer className="drawer" {...drawerProps}>
|
||||||
<antd.PageHeader
|
<div className="header">
|
||||||
onBack={this.onClose}
|
<antd.PageHeader
|
||||||
title={this.props.title ?? "Close"}
|
onBack={this.onClose}
|
||||||
backIcon={this.props.backIcon}
|
title={this.props.title ?? "Close"}
|
||||||
subTitle={this.props.subtitle}
|
backIcon={this.props.backIcon}
|
||||||
/>
|
subTitle={this.props.subtitle}
|
||||||
<div style={{ padding: "10px 30px" }}>{React.createElement(this.props.children, componentProps)}</div>
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="body">
|
||||||
|
{React.createElement(this.props.children, componentProps)}
|
||||||
|
</div>
|
||||||
</antd.Drawer>
|
</antd.Drawer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
20
packages/app/src/layout/drawer/index.less
Normal file
20
packages/app/src/layout/drawer/index.less
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.drawer {
|
||||||
|
height: 100vh;
|
||||||
|
max-height: 100vh;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
position: relative;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.body {
|
||||||
|
padding: 10px 30px;
|
||||||
|
height: fit-content;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-content, .ant-drawer-wrapper-body, .ant-drawer-body{
|
||||||
|
height: 100vh;
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
|
import { Icons } from "components/Icons"
|
||||||
import { AppSearcher } from "components"
|
import { AppSearcher } from "components"
|
||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
|
|
||||||
@ -23,12 +24,19 @@ export default class Header extends React.Component {
|
|||||||
window.app["HeaderController"] = this.HeaderController
|
window.app["HeaderController"] = this.HeaderController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClickCreate = () => {
|
||||||
|
window.app.openFabric()
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<antd.Layout.Header className={classnames(`app_header`, { ["hidden"]: !this.state.visible })}>
|
<antd.Layout.Header className={classnames(`app_header`, { ["hidden"]: !this.state.visible })}>
|
||||||
<div>
|
<div>
|
||||||
<AppSearcher />
|
<AppSearcher />
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<antd.Button onClick={this.onClickCreate} type="primary" shape="circle" icon={<Icons.Plus style={{ margin: 0 }} />} style={{ display: "flex", alignItems: "center", justifyContent: "center" }} />
|
||||||
|
</div>
|
||||||
</antd.Layout.Header>
|
</antd.Layout.Header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
|
|
||||||
transition: all ease-in-out 150ms;
|
transition: all ease-in-out 150ms;
|
||||||
|
|
||||||
background: @app_header_background!important;
|
background: var(--background-color-primary)!important;
|
||||||
background-color: @app_header_background!important;
|
background-color: var(--background-color-primary)!important;
|
||||||
|
|
||||||
border-bottom: 1px @app_background_accent solid;
|
border-bottom: 1px var(--background-color-accent) solid;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Button } from "antd"
|
import { Button } from "antd"
|
||||||
import { ActionsBar } from "components"
|
import { ActionsBar } from "components"
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons, createIconRender } from "components/icons"
|
||||||
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"
|
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"
|
||||||
|
|
||||||
import Selector from "../selector"
|
import Selector from "../selector"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons, createIconRender } from "components/Icons"
|
||||||
import { LoadingSpinner, SelectableList } from "components"
|
import { SelectableList } from "components"
|
||||||
import { Button, List, Checkbox, InputNumber } from "antd"
|
import { List } from "antd"
|
||||||
|
|
||||||
import sidebarItems from "schemas/sidebar.json"
|
import sidebarItems from "schemas/sidebar.json"
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Icons, createIconRender } from "components/Icons"
|
import { Icons, createIconRender } from "components/icons"
|
||||||
import { Layout, Menu, Avatar } from "antd"
|
import { Layout, Menu, Avatar } from "antd"
|
||||||
|
|
||||||
import { Settings } from "components"
|
|
||||||
import { SidebarEditor } from "./components"
|
import { SidebarEditor } from "./components"
|
||||||
|
|
||||||
import config from "config"
|
import config from "config"
|
||||||
@ -16,7 +15,7 @@ const { Sider } = Layout
|
|||||||
|
|
||||||
const onClickHandlers = {
|
const onClickHandlers = {
|
||||||
settings: (event) => {
|
settings: (event) => {
|
||||||
Settings.open()
|
window.app.openSettings()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +47,7 @@ export default class Sidebar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.app["SidebarController"] = this.SidebarController
|
window.app["SidebarController"] = this.SidebarController
|
||||||
|
window.app.eventBus.on("edit_sidebar", () => this.toogleEditMode())
|
||||||
}
|
}
|
||||||
|
|
||||||
collapseDebounce = null
|
collapseDebounce = null
|
||||||
@ -214,9 +214,9 @@ export default class Sidebar extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toogleCollapse = (to) => {
|
toogleCollapse = (to) => {
|
||||||
if(window.app.configuration?.settings.is("collapseOnLooseFocus", true) && !this.state.editMode ){
|
if (window.app.configuration?.settings.is("collapseOnLooseFocus", true) && !this.state.editMode) {
|
||||||
this.setState({ collapsed: to ?? !this.state.collapsed })
|
this.setState({ collapsed: to ?? !this.state.collapsed })
|
||||||
}else {
|
} else {
|
||||||
this.setState({ collapsed: false })
|
this.setState({ collapsed: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,8 +232,8 @@ export default class Sidebar extends React.Component {
|
|||||||
|
|
||||||
handleMouseLeave = () => {
|
handleMouseLeave = () => {
|
||||||
if (!this.state.collapsed) {
|
if (!this.state.collapsed) {
|
||||||
this.collapseDebounce = setTimeout(() => {this.toogleCollapse(true)}, 500)
|
this.collapseDebounce = setTimeout(() => { this.toogleCollapse(true) }, 500)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderExtraItems = (position) => {
|
renderExtraItems = (position) => {
|
||||||
@ -261,7 +261,7 @@ export default class Sidebar extends React.Component {
|
|||||||
onMouseLeave={this.handleMouseLeave}
|
onMouseLeave={this.handleMouseLeave}
|
||||||
theme={this.props.theme}
|
theme={this.props.theme}
|
||||||
width={this.state.editMode ? 400 : 200}
|
width={this.state.editMode ? 400 : 200}
|
||||||
collapsed={this.state.editMode? false : this.state.collapsed}
|
collapsed={this.state.editMode ? false : this.state.collapsed}
|
||||||
onCollapse={() => this.props.onCollapse()}
|
onCollapse={() => this.props.onCollapse()}
|
||||||
className={classnames("sidebar", { ["edit_mode"]: this.state.editMode, ["hidden"]: !this.state.visible })}
|
className={classnames("sidebar", { ["edit_mode"]: this.state.editMode, ["hidden"]: !this.state.visible })}
|
||||||
>
|
>
|
||||||
@ -306,4 +306,4 @@ export default class Sidebar extends React.Component {
|
|||||||
</Sider>
|
</Sider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
// SIDEBAR
|
// SIDEBAR
|
||||||
.ant-layout-sider {
|
.ant-layout-sider {
|
||||||
background: @app_sidebar_backgroundColor!important;
|
background: var(--sidebar-background-color)!important;
|
||||||
background-color: @app_sidebar_backgroundColor!important;
|
background-color: var(--sidebar-background-color)!important;
|
||||||
|
|
||||||
border-radius: 0 @app_sidebar_borderRadius @app_sidebar_borderRadius 0;
|
border-radius: 0 @app_sidebar_borderRadius @app_sidebar_borderRadius 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid @app_sidebar_backgroundColor!important;
|
border: 1px solid var(--sidebar-background-color)!important;
|
||||||
|
|
||||||
|
transition: all 150ms ease-in-out;
|
||||||
|
|
||||||
&.hidden{
|
&.hidden{
|
||||||
flex: 0!important;
|
flex: 0!important;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
.sidedrawer {
|
.sidedrawer {
|
||||||
width: 30vw; // by default
|
width: 30vw; // by default
|
||||||
|
|
||||||
background-color: @app_sidebar_backgroundColor;
|
background-color: var(--sidedrawer-background-color);
|
||||||
border-radius: @app_sidebar_borderRadius 0 0 @app_sidebar_borderRadius;
|
border-radius: @app_sidebar_borderRadius 0 0 @app_sidebar_borderRadius;
|
||||||
|
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
export { default as Session } from './session'
|
export { default as Session } from './session'
|
||||||
export { default as User } from './user'
|
export { default as User } from './user'
|
||||||
|
export { default as SidebarController } from './sidebar'
|
||||||
|
export { default as SettingsController } from './settings'
|
@ -1,123 +0,0 @@
|
|||||||
import cookies from 'js-cookie'
|
|
||||||
import jwt_decode from "jwt-decode"
|
|
||||||
import config from 'config'
|
|
||||||
|
|
||||||
export default class Session {
|
|
||||||
static get bridge() {
|
|
||||||
return window.app?.apiBridge
|
|
||||||
}
|
|
||||||
|
|
||||||
static tokenKey = config.app?.storage?.token ?? "token"
|
|
||||||
|
|
||||||
static get token() {
|
|
||||||
return cookies.get(this.tokenKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
static set token(token) {
|
|
||||||
return cookies.set(this.tokenKey, token)
|
|
||||||
}
|
|
||||||
|
|
||||||
static get decodedToken() {
|
|
||||||
return this.token && jwt_decode(this.token)
|
|
||||||
}
|
|
||||||
|
|
||||||
//* BASIC HANDLERS
|
|
||||||
login = (payload, callback) => {
|
|
||||||
const body = {
|
|
||||||
username: window.btoa(payload.username),
|
|
||||||
password: window.btoa(payload.password),
|
|
||||||
allowRegenerate: payload.allowRegenerate
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.generateNewToken(body, (err, res) => {
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback(err, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!err || res.status === 200) {
|
|
||||||
let token = res.data
|
|
||||||
|
|
||||||
if (typeof token === 'object') {
|
|
||||||
token = token.token
|
|
||||||
}
|
|
||||||
|
|
||||||
Session.token = token
|
|
||||||
window.app.eventBus.emit("new_session")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
logout = async () => {
|
|
||||||
await this.destroyCurrentSession()
|
|
||||||
this.forgetLocalSession()
|
|
||||||
}
|
|
||||||
|
|
||||||
//* GENERATORS
|
|
||||||
generateNewToken = async (payload, callback) => {
|
|
||||||
const request = await Session.bridge.post.login(payload, undefined, {
|
|
||||||
parseData: false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (typeof callback === 'function') {
|
|
||||||
callback(request.error, request.response)
|
|
||||||
}
|
|
||||||
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
regenerateToken = async () => {
|
|
||||||
return await Session.bridge.post.regenerate()
|
|
||||||
}
|
|
||||||
|
|
||||||
//* GETTERS
|
|
||||||
getAllSessions = async () => {
|
|
||||||
return await Session.bridge.get.sessions()
|
|
||||||
}
|
|
||||||
|
|
||||||
getTokenInfo = async () => {
|
|
||||||
const session = Session.token
|
|
||||||
|
|
||||||
return await Session.bridge.post.validateSession({ session })
|
|
||||||
}
|
|
||||||
|
|
||||||
isCurrentTokenValid = async () => {
|
|
||||||
const health = await this.getTokenInfo()
|
|
||||||
|
|
||||||
return health.valid
|
|
||||||
}
|
|
||||||
|
|
||||||
forgetLocalSession = () => {
|
|
||||||
cookies.remove(this.tokenKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyAllSessions = async () => {
|
|
||||||
const session = Session.decodedToken
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await Session.bridge.delete.sessions({ user_id: session.user_id })
|
|
||||||
this.forgetLocalSession()
|
|
||||||
window.app.eventBus.emit("destroyed_session")
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyCurrentSession = async () => {
|
|
||||||
const token = Session.token
|
|
||||||
const session = Session.decodedToken
|
|
||||||
|
|
||||||
if (!session || !token) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await Session.bridge.delete.session({ user_id: session.user_id, token: token })
|
|
||||||
this.forgetLocalSession()
|
|
||||||
window.app.eventBus.emit("destroyed_session")
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
logout = this.destroyCurrentSession
|
|
||||||
}
|
|
@ -2,7 +2,7 @@ import Session from '../session'
|
|||||||
|
|
||||||
export default class User {
|
export default class User {
|
||||||
static get bridge() {
|
static get bridge() {
|
||||||
return window.app?.apiBridge
|
return window.app?.request
|
||||||
}
|
}
|
||||||
|
|
||||||
static get data() {
|
static get data() {
|
||||||
@ -36,4 +36,14 @@ export default class User {
|
|||||||
|
|
||||||
return request.response.data
|
return request.response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasAdmin = async () => {
|
||||||
|
const roles = await User.roles
|
||||||
|
|
||||||
|
if (!roles) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(roles) && roles.includes("admin")
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ import React from "react"
|
|||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
|
|
||||||
import { Roles } from "components"
|
|
||||||
import { AccountEditor, SessionsView, StatisticsView } from "./components"
|
import { AccountEditor, SessionsView, StatisticsView } from "./components"
|
||||||
|
|
||||||
import { Session } from "models"
|
import { Session } from "models"
|
||||||
@ -160,7 +159,9 @@ export default class Account extends React.Component {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<Roles roles={user.roles} />
|
<div key="roles">
|
||||||
|
|
||||||
|
</div>
|
||||||
{this.state.isSelf && this.renderSelfActions()}
|
{this.state.isSelf && this.renderSelfActions()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@200;300;400;500;700&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Alata&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Alata&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Poppins:300,300i,500,500i,700');
|
@import url('https://fonts.googleapis.com/css?family=Poppins:300,300i,500,500i,700');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Kulim+Park&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Kulim+Park&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');
|
||||||
@import url("https://fonts.googleapis.com/css?family=Manrope:300,400,500,600,700&display=swap&subset=latin-ext");
|
@import url("https://fonts.googleapis.com/css?family=Manrope:300,400,500,600,700&display=swap&subset=latin-ext");
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Varela+Round&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Space+Mono&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,300;0,400;1,300;1,400&display=swap');
|
@ -1,47 +1,10 @@
|
|||||||
@import "antd/dist/antd.less";
|
@import "antd/dist/antd.variable.less";
|
||||||
@import "theme/vars.less";
|
@import "theme/vars.less";
|
||||||
@import "theme/fonts.css";
|
@import "theme/fonts.css";
|
||||||
|
|
||||||
.ant-layout {
|
|
||||||
background: @app_layout_backgroundColor!important;
|
|
||||||
background-color: @app_layout_backgroundColor!important;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app_layout {
|
|
||||||
background: @app_layout_backgroundColor!important;
|
|
||||||
background-color: @app_layout_backgroundColor!important;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app_wrapper {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
|
|
||||||
margin: 10px 10px 30px 16px;
|
|
||||||
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: overlay;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
@ -77,11 +40,20 @@ html {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
background-color: @app_layout_backgroundColor!important;
|
background-color: var(--background-color-primary) !important;
|
||||||
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|
||||||
|
&.theme-dark {
|
||||||
|
@import "./variations/dark.less";
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
margin-right: 10px;
|
||||||
|
vertical-align: -0.125em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -89,7 +61,7 @@ body {
|
|||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
background-color: @app_layout_backgroundColor!important;
|
background-color: var(--background-color-primary) !important;
|
||||||
font-family: "Varela Round", sans-serif;
|
font-family: "Varela Round", sans-serif;
|
||||||
|
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
@ -110,7 +82,7 @@ body {
|
|||||||
|
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
background-color: @app_layout_backgroundColor!important;
|
background-color: var(--background-color-primary) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nprogress {
|
#nprogress {
|
||||||
@ -124,6 +96,35 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-layout,
|
||||||
|
.content_layout,
|
||||||
|
.app_layout {
|
||||||
|
background: var(--background-color-primary) !important;
|
||||||
|
background-color: var(--background-color-primary) !important;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 150ms ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout_page {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
|
||||||
|
margin: 10px 10px 30px 16px;
|
||||||
|
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
// @media (min-width: @screen-xs) {
|
// @media (min-width: @screen-xs) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
@ -144,47 +145,6 @@ body {
|
|||||||
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
.horizontal_actions_cascade {
|
|
||||||
> div:first-child {
|
|
||||||
transform: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
transform: translate(0, -10px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizontal_actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
padding: 20px 0;
|
|
||||||
|
|
||||||
border-bottom: #424242 solid 1px;
|
|
||||||
border-left: #424242 solid 1px;
|
|
||||||
border-right: #424242 solid 1px;
|
|
||||||
|
|
||||||
border-radius: 0 0 12px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component_bottom_centered {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-transverse-active {
|
.fade-transverse-active {
|
||||||
transition: all 250ms;
|
transition: all 250ms;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -214,12 +174,18 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app_crash {
|
.app_crash {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
@ -233,4 +199,17 @@ body {
|
|||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fixments
|
||||||
|
.ant-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-result-extra {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
78
packages/app/src/theme/variations/dark.less
Normal file
78
packages/app/src/theme/variations/dark.less
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
div {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: var(--header-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a, p {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
span:not(.ant-tag) {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg:not(.ant-tag *) {
|
||||||
|
color: var(--svg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
input, .ant-input-affix-wrapper, .ant-input {
|
||||||
|
color: var(--text-color)!important;
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:disabled{
|
||||||
|
background-color: var(--background_disabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODAL
|
||||||
|
.ant-modal-content {
|
||||||
|
background-color: var(--background-color-accent)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TABLE
|
||||||
|
tr {
|
||||||
|
background-color: var(--background-color-accent)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table, .ant-table-content, .ant-table-thead, .ant-table-cell {
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table-thead > tr > th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before {
|
||||||
|
background-color: var(--background-color-contrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MENU
|
||||||
|
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||||
|
background-color: var(--background-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BUTTONS
|
||||||
|
.ant-btn:not(.ant-btn-link) {
|
||||||
|
background-color: var(--button-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn span{
|
||||||
|
color: var(--button-text-color)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn svg {
|
||||||
|
color: var(--button-text-color)!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DRAWER
|
||||||
|
.ant-page-header span {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-drawer-content {
|
||||||
|
background: var(--background-color-primary);
|
||||||
|
background-color: var(--background-color-primary);
|
||||||
|
}
|
@ -1,13 +1,4 @@
|
|||||||
// COLORS
|
//* Now this only works as an fallback for unset dynamic theme values
|
||||||
@primary-color: #32b7bb;//#32b7bb; //var(--primary-color);
|
|
||||||
|
|
||||||
@app_background_primary: #ffffff;
|
|
||||||
@app_background_accent: #f0f2f5;
|
|
||||||
@app_background_contrast: #4b4b4b;
|
|
||||||
|
|
||||||
@app_layout_backgroundColor: @app_background_primary;
|
|
||||||
@app_sidebar_backgroundColor: @app_background_accent;
|
|
||||||
@app_header_background: @app_background_primary;
|
|
||||||
|
|
||||||
// borders & radius
|
// borders & radius
|
||||||
@app_sidebar_borderRadius: 18px;
|
@app_sidebar_borderRadius: 18px;
|
||||||
@ -19,11 +10,7 @@
|
|||||||
@app_menuItemIconSize: 30px;
|
@app_menuItemIconSize: 30px;
|
||||||
@app_menuItemTextSize: 12px;
|
@app_menuItemTextSize: 12px;
|
||||||
|
|
||||||
@app_menuItemSize: 100px;
|
// TRANSITIONS
|
||||||
@app_menuItemIconSize: 30px;
|
|
||||||
@app_menuItemTextSize: 12px;
|
|
||||||
|
|
||||||
// DEFAULT TRANSITIONS
|
|
||||||
@transition-ease-in: all 0.3s ease-out;
|
@transition-ease-in: all 0.3s ease-out;
|
||||||
@transition-ease-out: all 0.3s ease-out;
|
@transition-ease-out: all 0.3s ease-out;
|
||||||
@transition-ease-inout: all 150ms ease-in-out;
|
@transition-ease-inout: all 150ms ease-in-out;
|
Loading…
x
Reference in New Issue
Block a user