From 215ccbf64cf97a472b1b8cd4d5a061eedc566b76 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Tue, 25 May 2021 20:24:10 -0400 Subject: [PATCH] Add panels as extension type --- app/src/lang/set-language.ts | 3 + app/src/lang/translations/en-US.yaml | 4 + app/src/main.ts | 3 +- app/src/modules/insights/index.ts | 11 +++ app/src/modules/insights/routes/dashboard.vue | 37 ++++++++- .../insights/routes/panel-configuration.vue | 80 +++++++++++++++++++ app/src/panels/define.ts | 13 +++ app/src/panels/index.ts | 17 ++++ app/src/panels/metric/index.ts | 13 +++ app/src/panels/metric/metric.vue | 3 + app/src/panels/register.ts | 47 +++++++++++ app/src/panels/types.ts | 17 ++++ 12 files changed, 244 insertions(+), 4 deletions(-) create mode 100644 app/src/modules/insights/routes/panel-configuration.vue create mode 100644 app/src/panels/define.ts create mode 100644 app/src/panels/index.ts create mode 100644 app/src/panels/metric/index.ts create mode 100644 app/src/panels/metric/metric.vue create mode 100644 app/src/panels/register.ts create mode 100644 app/src/panels/types.ts diff --git a/app/src/lang/set-language.ts b/app/src/lang/set-language.ts index dd7e3e9925..83978b57b6 100644 --- a/app/src/lang/set-language.ts +++ b/app/src/lang/set-language.ts @@ -1,5 +1,6 @@ import { getDisplays } from '@/displays'; import { getInterfaces } from '@/interfaces'; +import { getPanels } from '@/panels'; import { getLayouts } from '@/layouts'; import { getModules } from '@/modules'; import { useCollectionsStore, useFieldsStore } from '@/stores'; @@ -10,6 +11,7 @@ import { i18n, Language, loadedLanguages } from './index'; const { modules, modulesRaw } = getModules(); const { layouts, layoutsRaw } = getLayouts(); const { interfaces, interfacesRaw } = getInterfaces(); +const { panels, panelsRaw } = getPanels(); const { displays, displaysRaw } = getDisplays(); export async function setLanguage(lang: Language): Promise { @@ -33,6 +35,7 @@ export async function setLanguage(lang: Language): Promise { modules.value = translate(modulesRaw.value); layouts.value = translate(layoutsRaw.value); interfaces.value = translate(interfacesRaw.value); + panels.value = translate(panelsRaw.value); displays.value = translate(displaysRaw.value); collectionsStore.translateCollections(); diff --git a/app/src/lang/translations/en-US.yaml b/app/src/lang/translations/en-US.yaml index 00e23fa75a..9507aa2688 100644 --- a/app/src/lang/translations/en-US.yaml +++ b/app/src/lang/translations/en-US.yaml @@ -1176,3 +1176,7 @@ layouts: calendar: Calendar start_date_field: Start Date Field end_date_field: End Date Field +panels: + metric: + name: Metric + description: Show a single value based on a query diff --git a/app/src/main.ts b/app/src/main.ts index 801205f0fa..f8bfff6c9e 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -50,13 +50,14 @@ import './views/register'; import { registerInterfaces } from './interfaces/register'; import { loadModules } from './modules/register'; +import { registerPanels } from './panels/register'; import { registerLayouts } from './layouts/register'; import { registerDisplays } from './displays/register'; import App from './app.vue'; async function init() { - await Promise.all([registerInterfaces(), registerDisplays(), registerLayouts(), loadModules()]); + await Promise.all([registerInterfaces(), registerPanels(), registerDisplays(), registerLayouts(), loadModules()]); Vue.config.productionTip = false; diff --git a/app/src/modules/insights/index.ts b/app/src/modules/insights/index.ts index 4c6c1849ba..ee39edaa8f 100644 --- a/app/src/modules/insights/index.ts +++ b/app/src/modules/insights/index.ts @@ -1,6 +1,7 @@ import { defineModule } from '@/modules/define'; import InsightsOverview from './routes/overview.vue'; import InsightsDashboard from './routes/dashboard.vue'; +import InsightsPanelConfiguration from './routes/panel-configuration.vue'; export default defineModule({ id: 'insights', @@ -17,6 +18,16 @@ export default defineModule({ path: '/:primaryKey', component: InsightsDashboard, props: true, + children: [ + { + name: 'panel-detail', + path: ':panelKey', + props: true, + components: { + detail: InsightsPanelConfiguration, + }, + }, + ], }, ], order: 30, diff --git a/app/src/modules/insights/routes/dashboard.vue b/app/src/modules/insights/routes/dashboard.vue index fc257e5b28..d3f592c77f 100644 --- a/app/src/modules/insights/routes/dashboard.vue +++ b/app/src/modules/insights/routes/dashboard.vue @@ -8,6 +8,10 @@ -
+
+ +
+ + @@ -44,7 +52,13 @@ export default defineComponent({ insightsStore.state.dashboards.find((dashboard) => dashboard.id === props.primaryKey) ); - return { currentDashboard, editMode }; + const stagedPanels = ref([]); + + const panels = computed(() => { + return currentDashboard.value?.panels || []; + }); + + return { currentDashboard, editMode, panels }; }, }); @@ -61,6 +75,10 @@ export default defineComponent({ overflow: visible; } +.workspace > * { + z-index: 2; +} + .workspace::before { position: absolute; top: -4px; @@ -68,7 +86,7 @@ export default defineComponent({ display: block; width: calc(100% + 8px); height: calc(100% + 8px); - background-image: radial-gradient(#efefef 25%, transparent 25%); + background-image: radial-gradient(#efefef 20%, transparent 20%); background-position: -6px -6px; background-size: 20px 20px; opacity: 0; @@ -80,4 +98,17 @@ export default defineComponent({ .workspace.editing::before { opacity: 1; } + +/* .dummy { + --pos-x: 5; + --pos-y: 5; + --width: 5; + --height: 5; + + display: block; + grid-row: var(--pos-y) / span var(--height); + grid-column: var(--pos-x) / span var(--width); + background-color: var(--primary); + border-radius: var(--border-radius); +} */ diff --git a/app/src/modules/insights/routes/panel-configuration.vue b/app/src/modules/insights/routes/panel-configuration.vue new file mode 100644 index 0000000000..063e6380d1 --- /dev/null +++ b/app/src/modules/insights/routes/panel-configuration.vue @@ -0,0 +1,80 @@ + + + + + + + diff --git a/app/src/panels/define.ts b/app/src/panels/define.ts new file mode 100644 index 0000000000..1d89258f20 --- /dev/null +++ b/app/src/panels/define.ts @@ -0,0 +1,13 @@ +import { PanelConfig, PanelDefineParam } from './types'; + +export function definePanel(config: PanelDefineParam): PanelConfig { + let options: PanelConfig; + + if (typeof config === 'function') { + options = config(); + } else { + options = config; + } + + return options; +} diff --git a/app/src/panels/index.ts b/app/src/panels/index.ts new file mode 100644 index 0000000000..4698bebbce --- /dev/null +++ b/app/src/panels/index.ts @@ -0,0 +1,17 @@ +import { ref, Ref } from '@vue/composition-api'; +import { PanelConfig } from './types'; + +let panelsRaw: Ref; +let panels: Ref; + +export function getPanels(): Record> { + if (!panelsRaw) { + panelsRaw = ref([]); + } + + if (!panels) { + panels = ref([]); + } + + return { panels, panelsRaw }; +} diff --git a/app/src/panels/metric/index.ts b/app/src/panels/metric/index.ts new file mode 100644 index 0000000000..61153d4e9b --- /dev/null +++ b/app/src/panels/metric/index.ts @@ -0,0 +1,13 @@ +import { definePanel } from '../define'; +import PanelMetric from './metric.vue'; + +export default definePanel({ + id: 'metric', + name: '$t:panels.metric.name', + description: '$t:panels.metric.description', + icon: 'functions', + component: PanelMetric, + options: [], + minWidth: 4, + minHeight: 4, +}); diff --git a/app/src/panels/metric/metric.vue b/app/src/panels/metric/metric.vue new file mode 100644 index 0000000000..e55754ea29 --- /dev/null +++ b/app/src/panels/metric/metric.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/panels/register.ts b/app/src/panels/register.ts new file mode 100644 index 0000000000..626d00658a --- /dev/null +++ b/app/src/panels/register.ts @@ -0,0 +1,47 @@ +// import api from '@/api'; +// import { getRootPath } from '@/utils/get-root-path'; +import registerComponent from '@/utils/register-component/'; +// import asyncPool from 'tiny-async-pool'; +import { Component } from 'vue'; +import { getPanels } from './index'; +import { PanelConfig } from './types'; + +const { panelsRaw } = getPanels(); + +export async function registerPanels(): Promise { + const context = require.context('.', true, /^.*index\.ts$/); + + const modules = context + .keys() + .map((key) => context(key)) + .map((mod) => mod.default) + .filter((m) => m); + + // try { + // const customResponse = await api.get('/extensions/panels/'); + // const panels: string[] = customResponse.data.data || []; + + // await asyncPool(5, panels, async (panelName) => { + // try { + // const result = await import( + // /* webpackIgnore: true */ getRootPath() + `extensions/panels/${panelName}/index.js` + // ); + // modules.push(result.default); + // } catch (err) { + // console.warn(`Couldn't load custom panel "${panelName}":`, err); + // } + // }); + // } catch { + // console.warn(`Couldn't load custom panels`); + // } + + panelsRaw.value = modules; + + panelsRaw.value.forEach((panel: PanelConfig) => { + registerComponent('panel-' + panel.id, panel.component); + + if (typeof panel.options !== 'function' && Array.isArray(panel.options) === false) { + registerComponent(`panel-options-${panel.id}`, panel.options as Component); + } + }); +} diff --git a/app/src/panels/types.ts b/app/src/panels/types.ts new file mode 100644 index 0000000000..2ff84849fd --- /dev/null +++ b/app/src/panels/types.ts @@ -0,0 +1,17 @@ +import { Field } from '@/types'; +import { AsyncComponent, Component } from 'vue'; +import VueI18n from 'vue-i18n'; + +export interface PanelConfig { + id: string; + name: string; + icon: string; + description?: string | VueI18n.TranslateResult; + component: Component | AsyncComponent; + options: DeepPartial[] | Component | AsyncComponent; + minWidth: number; + minHeight: number; +} + +export type PanelDefineParam = PanelDefineParamGeneric; +export type PanelDefineParamGeneric = T | (() => T);