A hackable universal dashboard framework for your desktop task automation and visualise tooling.
-
Node >= v8.16.0
If we get
fsevents.watch is not a function
error, try to change node version greater than v8.16.0.
To get started developing, set up the nteract monorepo.
- Fork this repo
- Clone your fork or this repo
git clone https://github.com/etamity/dashpad
cd
to the directory where weclone
d it
yarn install
yarn start
Your built version will be under build
folder.
yarn build
Your bundled version will be under dist
folder.
yarn pack:mac
or
yarn pack:win
The application config file is in /Users/${username}/documents/dashpad/db/db.json
, we can change the window size and port etc.
{
"config": {
"port": 9999,
"uiport": 8888,
"host": "localhost",
"window": {
"width": 1308,
"height": 1024,
"show": false
},
...
}
}
If we wish to use dashpad
command, run npm global install and link it inside your project root folder.
npm i -g & npm link
and then we can run dashpad
in the terminal to open the dashpad.
The default work workspace directory will be under /Users/${username}/documents/dashpad
Create new folder in /Users/${username}/documents/dashpad/packages/${your_module}
, and open terminal in your module folder,
npm init
to create package.json
and create an index.js
file in the folder, with below code:
module.exports = function(params) {
Dashpad.showToast({ message: 'hello form module' });
};
Dashpad
is a global object that we can access dashpad api here.
Create _dash/config.yml
file, and copy the menu items as below:
---
navs:
- name: My First Module
icon: icon-speedometer
isOpen: true
goto: ui.yml # Your entry ui file
badge:
variant: info
text: NEW
# This settings will display in Dashpad settings in UI
# settings:
# username: Joey
# token: someToken
# configFile: !import someConfigFile.json
Now restart dashpad, we will see the new side menu My First Module
.
Create _dash/ui.yaml
file and fill the ui schema:
---
Tabs:
activeTab: 0
Card:
label: The first Tab
Header:
title: A registration form
Form:
Button_notification:
label: Notification
onClick: >
(e) => {
Dashpad.showNotification({
title: 'Notification title',
message: 'Notification message'
});
console.log('e', e, 'this', this);
Dashpad.run('index.js', {someParams: 'test'}); // calling backend index.js
}
and now go back to dashpad and click side menu My First Module
, we will see new we ui has been created.
if click Notification
button, we will recevie a notification.
Dashpad also allow us to create ui with react code base, but the main entry file must be .mdx
, please check out mdxjs.
Example of the config.yml
:
---
navs:
- name: My First Module
icon: icon-speedometer
isOpen: true
goto: index.mdx # Your entry ui file
badge:
variant: info
text: NEW
example of the index.mdx
file:
import ExampleComponent from './example.js';
# Hello, *world*!
Below is an example of JSX embedded in Markdown. <br /> **Try and change
the background color!**
<ExampleComponent />
or
<div>
use jsx sytex directly
</div>
example of the example.js
file:
import React from 'react';
export default () => (<div style={{ padding: '20px', backgroundColor: 'tomato' }}>
<h3>This is JSX Component</h3>
</div>);
If we want to use redux, we just need to use Block
component to wrap up your custom component.
example of index.mdx
file:
import { Button } from 'reactstrap';
<Block name="example" defaultState={{
name: 'init state'
}}
>
{(state)=> {
return <Button onClick={()=> {
state.set({name: 'state changed'});
// Or access Dashpad api here
}}>Start {state.name}</Button>
}
}
</Block>
Dashpad api available in global scope, we can access Dashpad
anypoint in the mdx
or jsx
file
If we want to split the js code from yml file into a external js file, we can just create a js file with exact same name as yaml file, and write logic or function in that js file, Dashpad will preload the code for yaml file usage.
e.g
If _dash/ui.yaml
file exist already then create another js file call _dash/ui.js
, so that we can write js code in the file.
ui.js
const onTabsMount = () => {
console.log('Tab Mounted');
}
const onTabsChanged = () => {
console.log('Tab Changed');
}
console.log('This code is loaded');
and then we can call onTabsMount
in component events, such as onClick
, onMount
, onWillMount
etc.
---
Tabs:
activeTab: 0
onMount: >
(e) => {
onTabsMount();
}
onChange: >
(e) => {
onTabsChanged();
}
Card:
label: The first Tab
Header:
title: A registration form
Now check the console, will find message 'This code is loaded'
is there, and then is 'Tab Mounted'
, if change the tab will see the output 'Tab Changed'
.
You can split yaml file into sub component files, but have to named the file start with @
, e.g @filename.yml
, this is for recognising the file as a component, so when we update the component file, it will only re-render main entry file.
e.g
Tabs:
Slot_1: !import components/@Card_dashpad.yml
Slot_2: !import components/@Card_test.yml
Slot
is a warpper component which doesn't doing anything on appearance, it's useful to organise structure or import sepreated file components.
You can access this
ref in any events, such as onClick
, onMount
, onWillMount
etc.
Container:
Collapse:
isOpen: true
List:
items:
- Some example text!
- another example text!
Button:
label: Get Context
position: left
brand: linkedin
onClick: >
(e) => {
console.log(this);
const brand = this.get('brand').value();
const isOpenProp = this.sibling('Collapse.isOpen');
const isOpen = isOpenProp.value();
const newButtonLabel = (isOpen ? 'isOpen' : 'isClosed')
this.get('label').set(newButtonLabel+ ' : ' + brand);
isOpenProp.set(!isOpen);
}
this.props
refer to the event target itself, we can direct acces target's props object, keypath, type, name etc.
this.get(key)
refer to the event target itself, we can access target's props object; this.get(key).value()
will return the value of the prop key.
this.set(value)
refer to the event target itself, we can set target's props value directly; We also can do chain keypath lookup: this.parent(key).sibling(key2).set(value)
.
this.sibling(key)
refer to the event target sibling props object, we can set sibling's props value directly; this.sibling(key).value()
will return the value of the sibling prop key.
this.parent(key)
refer to the event target parent props object, we can get parent's props value directly; this.parent(key).value()
will return the value of the parent prop key.
this.ref()
refer to the event target instance, we can access target's instance, currently only Webview component has the value return from ref()
.
The yaml ui schema is a tree structure file, when dashpad parse the ui, it alwasy will look for the key name of the object, the format will look like Type_name
. E.g. Tabs_mytabs
Dashpad will parse Tabs_mytabs
as Tabs
component and the component name is Tabs_mytabs
.
Important : Type_name
the Type
must start with capital character, so Dashpad will recognise it is a component Type, otherwise will parse it as an attribute.
Alert:
color: warning
Html:
content: >
<h4 className="alert-heading">Well done!</h4>
<p>
Aww yeah, we successfully read this important alert message. This example text is going
to run a bit longer so that we can see how spacing within an alert works with this kind
of content.
</p>
<hr />
<p className="mb-0">
Whenever we need to, be sure to use margin utilities to keep things nice and tidy.
</p>
Container:
Card:
Text:
position: center
content: this is a container
Tabs:
activeTab: 0
Card_1:
label: tab1
Card_2:
label: tab2
Card:
label: tab1
Header:
title: card title
Button_1:
label: left button
position: left
Button_2:
label: right button
position: right
Form:
Username:
type: text
value: joey
Collapse:
isOpen: false
Card:
title: card title
Button_control:
label: button to control collapse
onClick: >
(e) => {
Dashpad.setVars('isOpen', !isOpen);
}
Form:
Username:
type: text
label: Username
tooltip: Choose your username
value: First name
prepend:
- type: button
icon: icon-drop
color: warning
append:
- type: text
icon: icon-drop
label: hajajaj
color: success
Password:
type: password
label: Password
tooltip: Please input the password
One row with equally width colums
Row:
fluid: 'true'
Username:
type: text
value: joey
label: Username
tooltip: Please input your username
Password:
type: password
value: 1234
label: Password
tooltip: Please input your password
Also we can specify col
props in child components
Row:
fluid: 'true'
Username:
col:
md: 8 # Bootstrap 12 colums in one row
lg: 4
type: text
value: joey
label: Username
tooltip: Please input your username
Password:
col:
md: 4 # Bootstrap 12 colums in one row
lg: 8
type: password
value: 1234
label: Password
tooltip: Please input your password
Button:
brand: twitter
label: Run Script
color: success
icon: icon-drop
disabled: false
outline: false
size: md
className: ''
onClick: >
console.log('Button clicked')
Input component can specific type as ['text', 'password', 'number', 'email', 'textarea','datetime', 'time', 'date']
Input:
type: date # ['text', 'password', 'number', 'email', 'textarea','datetime', 'time', 'date']
label: Birthday
tooltip: Please Input Your Birthday
Field component can specific type as Input
, Button
, RADIO
, SELECT
, CHECKBOX
, SWITCH
Form:
Field_input:
type: text
label: Text Field
Field_button:
type: button
label: Button Field
Field_radio:
type: radio
title: Radio Field
options:
- Male
- Female
Field_checkbox:
type: checkbox
title: Checkbox Field
options:
- Foot Ball
- Piano
- Games
- Movies
Field_select:
type: select
title: Select Field
options:
- London
- York
- Manchester
- Liverpool
Field_switch:
type: switch
label: Switch Field
checked: true
color: danger
Hobbies:
type: checkbox
title: Foot Ball
options:
- Foot Ball
- Piano
- Games
- Movies
Gender:
type: radio
title: Gender
inline: true
options:
- Male
- Female
Link component will open the link in browser
Link:
icon: icon-links
link: http://somelink.com/
label: Open the link
color: danger
City:
type: select
title: City
options:
- London
- York
- Manchester
- Liverpool
RememberMe:
type: switch
label: Remember Me
color: primary
checked: true
Badge:
icon: icon-settingd
color: primary
Markdown:
content: >
<h2>Full example</h2>
``` jsx
console.log('this is test Markdown');
```
List:
items:
- list item 1
- list item 2
- list item 3
- title: list item 4
description: this is list item 4 content
- Text:
context: just text
Table:
labels: ['id', 'name', 'email', 'color']
dataset:
- ['1', 'joey', '[email protected]', 'red']
- ['2', 'joe', '[email protected]', 'blue']
- ['3', 'jack', '[email protected]', 'green']
- ['3', 'joseph', '[email protected]', 'black']
Html:
content: >
<h1>You can put html in here<h1>
Table:
labels: ['id', 'name', 'email', 'color']
dataset:
- ['1', 'joey', '[email protected]', 'red']
- ['2', 'joe', '[email protected]', 'blue']
- ['3', 'jack', '[email protected]', 'green']
- ['3', 'joseph', '[email protected]', 'black']
Buttongroup:
type: dropdown # ['group', 'toolbar', 'dropdown', 'wrap']
label: Menu
className: d-flex
childClassName: m-1
onClick: >
(e) => {
console.log('test group', this.e.target);
}
items:
- label: Button 1
- label: Button 2
- label: Button 3
Progress:
color: primary
value: 40
max: 100
Text:
tag: h3 #['h1', 'h2', ... , 'h6']
content: this is the typograph component
position: center # ['left', 'center', 'right']
color: info # ['primary', 'info', 'warning', 'danger', 'white', 'black']
weight: bold # ['ligth', 'normal', 'bold']
transform: uppercase # ['uppercase', 'lowercase', 'capitalise']
Code:
width: '100%'
theme: solarized_dark #['javascript', 'java', 'python', 'xml', 'ruby', 'sass', 'markdown', 'mysql', 'json', 'html', 'handlebars', 'golang', 'csharp', 'elixir', 'typescript', 'css', 'sh', 'yaml', 'sql', 'jsx', 'css']
mode: markdown #['github', 'solarized_dark', 'terminal']
value: const js = console.log;
wrapEnabled: true
readOnly: true
You can get variables value from $vars
properties by keyPath,
e.g.
Dashpad.getVars(keyPath);
If we predefined key default value in the yaml file header under $vars
key:
---
$vars:
username: Joey
header: This is Header
progress: 0
Then we can get the value simple pass they username
key to getVars, it will return variables username
value.
Dashpad.getVars('username'); // return 'joey'
You can set variable value to all components where it refers to this variable,
e.g.
Dashpad.setVars(keyPath, value);
If we predefined key default value in the yaml file header under $vars
key:
---
$vars:
username: Joey
progress: 0
And we can set the props refers ${username}
and ${progress}
to these variable keys:
---
Username:
type: text
label: Username
tooltip: Please input your username
value: ${username} # set the refers
Progress:
value: ${progress} # set the refers
max: 1000
animated: true
Then we can set the value simple pass they username
key to getVars, it will set Username.value
and Progress.value
props value.
Dashpad.setVars('username', 'joseph');
// or
Dashpad.setVars({ keyPath: 'username', value: 'joseph' });
Or we can set mutiple value in one goal:
Dashpad.setVars([
{ keyPath: 'username', value: 'Hello!!!!' },
{ keyPath: 'username', value: 'this will be set at same time!!!!' },
]);
You can get current state by keyPath,
e.g.
Dashpad.getVarsState(keyPath);
If we predefined key path in the yaml file header under $vars
key:
---
$vars:
username: Tabs_1.Card_1.Row.Username.value
header: Tabs_1.Card_1.Header.title
Then we can get the value simple pass they username
key to getVarsState, it will return Tabs_1.Card_1.Row.Username.value
state value.
Dashpad.getVarsState('username');
You can get current state by keyPath,
e.g.
Dashpad.setVarsState(keyPath, value);
If we predefined key path in the yaml file header under $vars
key:
---
$vars:
username: Tabs_1.Card_1.Row.Username.value
header: Tabs_1.Card_1.Header.title
Then we can set the value simple pass they username
key to getVars, it will set Tabs_1.Card_1.Row.Username.value
state value.
Dashpad.setVarsState('username', 'joseph');
// or
Dashpad.setVarsState({ keyPath: 'username', value: 'joseph' });
Or we can set mutiple value in one goal:
Dashpad.setVarsState([
{ keyPath: 'username', value: 'Hello!!!!' },
{ keyPath: 'username', value: 'this will be set at same time!!!!' },
]);
You can have your own scope settings variable by access Dashpad.settings
e.g.
Dashpad.settings.get(); // get all settings under your package name
Dashpad.settings.get(keyPath); // get the keyPath settings under your package name
Dashpad.settings.set(keyPath, value); // set the variables under your package name
Dashpad.settings.push(keyPath, value); // push an element into an array under your package name
Dashpad.settings.delete(keyPath); // delete the variables under your package name
Dashpad.settings.value(); // get all Dashpad settings
If we have a node package name sample_module
, and we can set your settings variable as below:
Dashpad.settings.set('myToken', 'token_data'); // set the variables under your package name
Dashpad will save token_data
value under settings.sample_module.myToken
. You will see the settings in the dashboard settings view.
You can get current state by keyPath,
e.g.
Dashpad.getState(keyPath);
Dashpad.getState('Tabs_1.Card_1.Header.title');
You can set current state by keyPath and value,
e.g.
Dashpad.setState(keyPath, value);
Dashpad.setState('Tabs_1.Card_1.Header.title', 'Hello!!!!');
// or
Dashpad.setState({ keyPath: 'Tabs_1.Card_1.Header.title', value: 'Hello!!!!' });
Or we can set mutiple value in one goal:
Dashpad.setState([
{ keyPath: 'Tabs_1.Card_1.Header.title', value: 'Hello!!!!' },
{
keyPath: 'Tabs_1.Card_1.Header.Button_1.label',
value: 'this will be set at same time!!!!',
},
]);
e.g.
Dashpad.showNotification({titile, message});
Dashpad.showNotification({
titile: 'Hello world',
message: 'This is notification',
});
e.g.
Dashpad.showToast({message, options});
const options = {
type: 'info' // 'info' | 'success' | 'warning' | 'error' | 'default'
}
Dashpad.showToast({
message: 'This is toast',
options
});
e.g.
Dashpad.showModal({title, message, onConfirm});
Dashpad.showModal({
title: 'show a modal'
message: 'This is modal'
});
load Json file and return json object
e.g.
Dashpad.loadJson(jsonfile);
const json = Dashpad.loadJson('somejson.json');
Run node scirpt from yml file
e.g.
Dashpad.run(nodeScript, parameters);
Dashpad.run('index.js', { obj: 'hey, node can get this parameter!' });
Once your task is finished, call Dashpad.exit();
to exit the process in the node.
e.g.
Dashpad.exit();
The reason to have js VM library is to avoid global scope poisioning. All yml (frontend) script will be running in a closure function.
E.g
// VM filter the global object in frontend
(function anonymous(window..., Dashpad
) {
"use strict";
commonFunc('just loaded!!');
return function VMScope() {
return {
run: function run(code) {
eval(code);
},
runEvent: function runEvent(code, e) {
eval(code);
}
};
}()
})
So all global objects are being filtered, window
object will be undefined
.
Dashpad has integrated official github rest sdk @octokit/rest, we can generate auth token on github setting, and set it up on Dashpad settings page, then get the Github object by
const { Github } = Dashpad.platform;
Github.search({q, ...});
By default if we didn't set up the auth token, the Github object will be empty.
If we set up the endpoint under settings.platform.Jenkins.endpoint
in Dashpad, it will connect to that jenkins endpoint by default, then we can use the Jenkins
object to call jenkins api.
Example:
const { Jenkins } = Dashpad.platform;
Jenkins.build('some_job', function(err, data) {})
For connecting to different endpoint, we can use JenkinsConnect
function to construct a new object to use it.
const { JenkinsConnect } = Dashpad.platform;
const devtoolJenkins = JenkinsConnect('someurl.to.jenkins');
The jenkins api originally from node-jenkins-api
Here is list of available api.
Jenkins.build('job-in-jenkins', (optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.build_with_params('job-in-jenkins', (optional){depth: 1, <param>:<value>, token: 'jenkins-token',...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.stop_build('job-in-jenkins', 'build-number', (optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.console_output('job-in-jenkins', 'buildname', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.build_info('job-in-jenkins', 'build-number', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.last_build_info('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.last_completed_build_info('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.all_builds('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.test_result('job-in-jenkins', 'build-number', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
// Jenkins.last_build_report('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
// if (err){ return console.log(err); }
// console.log(data)
// });
Jenkins.delete_build('job-in-jenkins', 'build-number', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.all_jobs((optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.get_config_xml('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.update_config('job-to-update'
,function(config) {
// function which takes the config.xml, and returns
// the new config xml for the new job
return config.replace('development','feature-branch');
}
,(optional){token: 'jenkins-token', ...}
,function(err, data) {
// if no error, job was copied
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.update_job('job-to-update', xmlConfigString, (optional){token: 'jenkins-token', ...}, function(err, data) {
// if no error, job was copied
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.job_info('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.create_job('job-in-jenkins', xmlConfigString, (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.copy_job('job-to-copy'
,'new-job-title'
,function(config) {
// function which takes the config.xml, and returns
// the new config xml for the new job
return config.replace('development','feature-branch');
}
,(optional){token: 'jenkins-token', ...}
,function(err, data) {
// if no error, job was copied
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.delete_job('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.enable_job('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.disable_job('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.last_success('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.last_result('job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.queue((optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.queue_item('queue-item-number', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.cancel_item('queue-item-number', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.computers((optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.all_views((optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.create_view('new-view-name', (optional)viewMode = 'hudson.model.ListView', (optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.create_view('view-name', (optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
var viewConfig = {
name: "view-in-jenkins",
"description": "This is the view-in-jenkins View",
"statusFilter": "",
"job-in-jenkins": true,
"useincluderegex": true,
"includeRegex": "prefix.*",
"columns": [{"stapler-class": "hudson.views.StatusColumn", "$class": "hudson.views.StatusColumn"}, {"stapler-class": "hudson.views.WeatherColumn", "$class": "hudson.views.WeatherColumn"}, {"stapler-class": "hudson.views.JobColumn", "$class": "hudson.views.JobColumn"}, {"stapler-class": "hudson.views.LastSuccessColumn", "$class": "hudson.views.LastSuccessColumn"}, {"stapler-class": "hudson.views.LastFailureColumn", "$class": "hudson.views.LastFailureColumn"}, {"stapler-class": "hudson.views.LastDurationColumn", "$class": "hudson.views.LastDurationColumn"}, {"stapler-class": "hudson.views.BuildButtonColumn", "$class": "hudson.views.BuildButtonColumn"}]
};
Jenkins.update_view('view-in-jenkins', viewConfig, (optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.delete_view('view-in-jenkins', (optional){token: 'jenkins-token', ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.add_job_to_view('view-in-jenkins', 'job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.remove_job_from_view('view-in-jenkins', 'job-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.all_jobs_in_view('view-in-jenkins', (optional) {depth: 1, <param>:<value>, ...}, function(err, data) {
if (err){ return console.log(err); }
console.log(data)
});
Jenkins.all_installed_plugins((optional){token: 'jenkins-token', ...}, function(err, data){
if (err){ return console.log(err); }
console.log(data)
})
// var plugin = '[email protected]';
var plugin = 'copyartifact@current';
Jenkins.install_plugin(plugin, (optional){token: 'jenkins-token', ...}, function(err, data){
if (err){ return console.log(err, data); }
console.log(data)
});
NOTE: It will report successful even if the plugin is already installed. NOTE: Prevent Cross Site Request Forgery exploits need be disabled in Configure Global Security.
To create new plugin, go to packages/frontend/dashpad/plugins
and create new folder with NewPlguin
style, and then create and index.js file as entry point.
export * from './NewPlguin';
Also we can copy the template __SamplePlugin
folder, we can find it under plugins
folder to start your new plugin.
import React, { Component } from 'react';
export class NewPlguin extends Component {
static Config() {
return {
name: 'New Plguin'
SideMenus, // Side menu items
TopRightIcons, // Top right icon buttons
SubRoutes: [],
TopMenus, // Top menu (on left side)
Aside, // Aside panel (if any)
};
}
render() {
return (
<div className="animated fadeIn">
<Container />
</div>
);
}
}
configs/Menu.js
There are two main menu, Top
menu and Side menu
Add MenuItem to Side
menu
const SideMenuItems = [
{
name: 'Dashboard',
url: '/dashboard',
icon: 'icon-speedometer',
},
{
name: 'Frontend Tools',
url: '/frontendtools',
icon: 'icon-wrench',
badge: {
variant: 'info',
text: 'NEW',
},
},
];
Add menu item to SideMenu
menu
export class NewPlugin extends Component {
static get Config() {
return {
SideMenu: SideMenuItems,
};
}
}
Each SideMenu
menu item also can have submenu, and we can assign it to children
array in the menu item.
SideMenu Add sub menu into menu item
const SideMenuItems = [
{
name: 'Dashboard',
url: '/dashboard',
icon: 'icon-speedometer'
children: [
{
name: 'Colors',
url: '/dashboard/page1',
icon: 'icon-drop',
badge: {
variant: 'info',
text: 'NEW',
}
},
{
name: 'Typography',
url: '/dashboard/page2',
icon: 'icon-pencil',
},
],
},
{
name: 'Frontend Tools',
url: '/frontendtools',
icon: 'icon-wrench',
badge: {
variant: 'info',
text: 'NEW'
}
}
];
Once we push your code, add dashpad
topic on the github descirption area, your plugin will show up on the Dashpad main page, it will be nice to add README.md to tell a bit more about your plugin, let people know what it does.
When we created new plugin, it will automatically create new route for we.
For example when we created a new plugin under packages/frontend/dashpad/plugins/NewPlugin
, and export the plugin in the packages/frontend/dashpad/plugins/index.js
file:
export * from './NewPlugin';
it will generate a new route /newplugin
.
Go to src/sass
folder to change style, it's using Bootstrap 4, reactstrap and Core UI
All exports to renderer thread are under packages/frontend/dashpad/libs/Remote.js
which can be use in frontend. The reason to import it from frontend is for live reloading node code just by refreshing frontend page.
packages/electron/main.js
are electron entry file.
All node backend operation function are under packages/core
folder.
While running the application, two ports will be using. (8888, and 9999)