parent
46ea2de206
commit
02cb4ab656
|
|
@ -476,6 +476,26 @@
|
|||
<nodeVersion>${nodeVersion}</nodeVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm install ccm-admin-dashboard</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>install</arguments>
|
||||
<workingDirectory>src/ccm-admin-dashboard</workingDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>build ccm-admin-dashboard</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run build</arguments>
|
||||
<workingDirectory>src/ccm-admin-dashboard</workingDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm install ccm-admin-systeminformation</id>
|
||||
<goals>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# ccm-admin-dashboard
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
presets: ["@vue/cli-plugin-babel/preset"]
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"name": "ccm-admin-dashboard",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build --dest ../../target/generated-resources/_admin/dashboard",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^4.5.2",
|
||||
"bootstrap-vue": "^2.16.0",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^2.6.11",
|
||||
"vue-class-component": "^7.2.3",
|
||||
"vue-property-decorator": "^8.4.2",
|
||||
"vue-router": "^3.2.0",
|
||||
"vuex": "^3.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.0",
|
||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"@vue/eslint-config-prettier": "^6.0.0",
|
||||
"@vue/eslint-config-typescript": "^5.0.2",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"node-sass": "^4.12.0",
|
||||
"prettier": "^1.19.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"typescript": "~3.9.3",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"vue": {
|
||||
"filenameHashing": false,
|
||||
"publicPath": "./"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended",
|
||||
"@vue/typescript/recommended",
|
||||
"@vue/prettier",
|
||||
"@vue/prettier/@typescript-eslint"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": false
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<b-card-group deck>
|
||||
<b-card v-for="app in apps" :key="app.slug" :title="app.label">
|
||||
<b-card-text>
|
||||
{{ app.description }}
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
</b-card-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss"></style>
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||
import { AdminApp } from "./admin-ui";
|
||||
|
||||
@Component
|
||||
export default class App extends Vue {
|
||||
get apps(): AdminApp[] {
|
||||
const appElem: Element | null = document.querySelector("#app");
|
||||
if (appElem === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const appsValue: string | null = appElem.getAttribute("data-apps");
|
||||
if (appsValue === null) {
|
||||
return [];
|
||||
} else {
|
||||
return JSON.parse(appsValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export interface AdminApp {
|
||||
slug: String,
|
||||
label: string,
|
||||
description: string,
|
||||
order: number,
|
||||
jsFilesUrls: string[],
|
||||
cssFilesUrls: string[],
|
||||
iconName: string | undefined,
|
||||
symbolUrl: string | undefined,
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import Vue from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import store from "./store";
|
||||
|
||||
import {BootstrapVue, IconsPlugin} from "bootstrap-vue";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
Vue.use(BootstrapVue);
|
||||
Vue.use(IconsPlugin);
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount("#app");
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import Vue from "vue";
|
||||
import VueRouter, { RouteConfig } from "vue-router";
|
||||
import Home from "../views/Home.vue";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const routes: Array<RouteConfig> = [
|
||||
// {
|
||||
// path: "/",
|
||||
// name: "Home",
|
||||
// component: Home
|
||||
// },
|
||||
// {
|
||||
// path: "/about",
|
||||
// name: "About",
|
||||
// // route level code-splitting
|
||||
// // this generates a separate chunk (about.[hash].js) for this route
|
||||
// // which is lazy-loaded when the route is visited.
|
||||
// component: () =>
|
||||
// import(/* webpackChunkName: "about" */ "../views/About.vue")
|
||||
// }
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: "hash",
|
||||
routes
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import Vue, { VNode } from "vue";
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
declare module "*.vue" {
|
||||
import Vue from "vue";
|
||||
export default Vue;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {},
|
||||
mutations: {},
|
||||
actions: {},
|
||||
modules: {}
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<div class="home">
|
||||
<img alt="Vue logo" src="../assets/logo.png" />
|
||||
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Vue } from "vue-property-decorator";
|
||||
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
})
|
||||
export default class Home extends Vue {}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
|
@ -14,16 +14,12 @@ const routes: Array<RouteConfig> = [
|
|||
{
|
||||
path: "/about",
|
||||
name: "About",
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (about.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: About
|
||||
}
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: "hash",
|
||||
base: process.env.BASE_URL,
|
||||
routes
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import freemarker.template.TemplateException;
|
|||
import freemarker.template.TemplateExceptionHandler;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.libreccm.api.JsonArrayCollector;
|
||||
import org.libreccm.security.Shiro;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -39,6 +40,8 @@ import java.util.Map;
|
|||
import javax.annotation.PostConstruct;
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObjectBuilder;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
|
|
@ -61,7 +64,7 @@ import javax.ws.rs.core.UriInfo;
|
|||
public class AdminUi {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(AdminUi.class);
|
||||
|
||||
|
||||
@Inject
|
||||
private AdminUiApps adminUiApps;
|
||||
|
||||
|
|
@ -160,6 +163,15 @@ public class AdminUi {
|
|||
|
||||
final Map<String, Object> data = new HashMap<>();
|
||||
data.put("adminUiApps", adminUiApps.getAdminUiApps());
|
||||
data.put(
|
||||
"adminUiAppsJson",
|
||||
adminUiApps
|
||||
.getAdminUiApps()
|
||||
.stream()
|
||||
.map(AdminUiApp::buildJson)
|
||||
.map(JsonObjectBuilder::build)
|
||||
.collect(new JsonArrayCollector())
|
||||
);
|
||||
data.put("activeApp", app);
|
||||
data.put("currentUser", shiro.getUser().get());
|
||||
data.put("contextPath", servletContext.getContextPath());
|
||||
|
|
|
|||
|
|
@ -18,8 +18,14 @@
|
|||
*/
|
||||
package org.libreccm.ui.admin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonObjectBuilder;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
|
|
@ -87,5 +93,28 @@ public interface AdminUiApp {
|
|||
* {@link Optional} if the icon should be used.
|
||||
*/
|
||||
Optional<String> getSymbolUrl();
|
||||
|
||||
default JsonObjectBuilder buildJson() {
|
||||
return Json
|
||||
.createObjectBuilder()
|
||||
.add("slug", getSlug())
|
||||
.add("label", getLabel())
|
||||
.add("description", getDescription())
|
||||
.add("order", getOrder())
|
||||
.add(
|
||||
"jsFilesUrls",
|
||||
Json.createArrayBuilder(Arrays.asList(getJsFilesUrls()))
|
||||
)
|
||||
.add(
|
||||
"cssFilesurls",
|
||||
Json.createArrayBuilder(Arrays.asList(getCssFilesUrls()))
|
||||
)
|
||||
.add("iconName", getIconName().orElse(null))
|
||||
.add("symbolUrl", getSymbolUrl().orElse(null));
|
||||
}
|
||||
|
||||
default JsonObject toJson() {
|
||||
return buildJson().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class DashboardApp implements AdminUiApp {
|
|||
@PostConstruct
|
||||
private void init() {
|
||||
adminBundle = ResourceBundle.getBundle(
|
||||
"org.libreccm.ui.admin",
|
||||
"org.libreccm.ui.admin",
|
||||
globalizationHelper.getNegotiatedLocale()
|
||||
);
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ public class DashboardApp implements AdminUiApp {
|
|||
public String getSlug() {
|
||||
return "dashboard";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return adminBundle.getString("dashboard.label");
|
||||
|
|
@ -69,12 +69,18 @@ public class DashboardApp implements AdminUiApp {
|
|||
|
||||
@Override
|
||||
public String[] getJsFilesUrls() {
|
||||
return new String[]{};
|
||||
return new String[]{
|
||||
"/_admin/systeminformation/js/chunk-vendors.js",
|
||||
"/_admin/systeminformation/js/app.js"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCssFilesUrls() {
|
||||
return new String[]{};
|
||||
return new String[]{
|
||||
"/_admin/systeminformation/css/chunk-vendors.css",
|
||||
"/_admin/systeminformation/css/app.css"
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<title>LibreCCM - ${activeApp.label}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="app" data-apps="${adminUiAppsJson}"></div>
|
||||
<#list activeApp.jsFilesUrls as jsFile>
|
||||
<script src="${contextPath}${jsFile}"></script>
|
||||
</#list>
|
||||
|
|
|
|||
Loading…
Reference in New Issue