В этой статье я хотел бы поделиться одной быстро сделанной разработкой, к сожалению, на реальном проекте эта функциональность не понадобилась, но допускаю что у нее может быть применение.
Контекст следующий, с некоторых пор, разработчик Luxms предоставляют возможность доделывать и менять некоторые модули их системы, делать кастомные отображения переопределением react компонентов. Создавать свои дэши, можно подключать внешние библиотеки и т.д.
На сегодняшнюю дату (лето 2024) их документация хоть и содержит необходимый минимум, тем не менее имеет много пробелов и приходится разбираться на ходу. Возможно существует более «правильный» способ реализации, тем не менее описанный ниже, тоже имеет право на жизнь.
Проект bi-magic-resources
Данный пример запущен при помощи bi-magic-resources(BMR). Если кратко, то данная технология позволяет подключаться к серверу разработки с локальными файлами react приложения. А после успешной разработки запушить изменения на сервер.
Этот блок хорошо задокументирован, рекомендую ознакомиться. Проблем с запуском возникнуть не должно.
Так же можно посетить статью об первой настройке BMR проекта.
Базовые понятия
Для демонстрации я буду запускать компонент на главной странице. Переопределяя компонент Root. Пару заметок об этом можно посмотреть в документации.
Перед началом обозначим понятия, которые используются в luxms.
- Дэшлет — составная часть дэшборда, в одном дэшборде может быть много дэшлетов. Дэшлеты могут быть позиционированы в области дэшборда, как угодно. Задаются 4 координатами. x,y — точка начала координат (как на графике функций) h,w — высота и ширина соответственно. Дэшлет может иметь разные отображения, отображение задается параметром view class.
- Дэшборд — пространство, содержащие в себе дэшлеты. Представляет собой сетку из квадратов, где каждый квадрат имеет свою координату x,y. Имеет динамические размеры.
- Атлас — в ранних версиях атласов не было, но сейчас они представляют собой какой-то блок бизнес логики. Иначе говоря, атлас содержит в себе дэшборды.
- Группа атласов — в контексте статьи эта сущность не понадобится, но группа атласов соответственно содержит в себе атласы. Группа атласов может и не существовать, в этом случаи атласы попадают в группу по умолчанию «без группы».
Разработка компонентов
Дэшлет
Начнем с компонента дэшлета.
В папке ds_res создадим свою папку …\bi-magic-resources\src\ds_res\dashboard-componets
В ней же создадим файл src\ds_res\dashboard-componets\DashletCustom.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import React from 'react'; import { VizelFromCfg } from 'bi-internal/ui'; function DashletCustom(props) { const dashlet = props.dashlet; return ( <div className='dashlet_item' id = {dashlet.id} style={{ position: 'relative', gridColumnStart: (dashlet.config.frame.x === 0 || dashlet.config.frame.x === 1) ? dashlet.config.frame.x + 1 : dashlet.config.frame.x, gridColumnEnd: dashlet.config.frame.w + dashlet.config.frame.x, gridRowStart: dashlet.config.frame.h + dashlet.config.frame.y + 1, gridRowEnd: dashlet.config.frame.y + 1, margin: '10px' }}> <VizelFromCfg schema_name={props.schema_name} rawCfg={dashlet.config} view_class={dashlet.view_class} dashboardId={dashlet.dashboard_id} title={dashlet.title} key={dashlet.id} /> </div> ); }; export default DashletCustom; |
Для отображения дэшлета тут используется стандартный компонент VizelFromCfg (частично описан в документации). На вход подаются данные о схеме, классе отображения и настройках дэшлета. Все эти данные подгружаются из API (информация об этом будет в отдельном пункте)
Так же тут добавлена динамическая стилизация. Все дело в том, что позиционирование дэшлета в этом компоненте реализовано при помощи CSS селектора grid-template. Однако, есть различия. Если в luxms задаются точки начала, высота и ширина, то тут задаются точки начала и точки конца. Поэтому приходится по простым формулам преобразовывать одно в другое.
Дэшборд
Далее очередь дэшборда
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React from 'react'; import DashletCustom from './DashletCustom'; function DashboardCustom({dashlets, schema_name, dashlet_title}) { return ( <div > <h1>{ dashlet_title }</h1> <div className="wrapper"> {dashlets.reduce(function (filtered, dashlet) { filtered.push( <DashletCustom dashlet = {dashlet} schema_name = {schema_name} key={dashlet.id}/> ); return filtered; }, [])} </div> </div> ); }; export default DashboardCustom; |
Здесь список дэшлетов распаковывается в компонент DashletCustom. dashlets — это объект извлеченный из API. Единственное, стоит обратить внимание на имя класса wrapper блока div. Позже для этого класса будут описаны стили.
Компонент Root
Как было сказано ранее, переопределение этого компонента используется для демонстрации. В теории эти компоненты можно использовать для других дэшлетов. Что бы переопределить компонент главной страницы нужно создать следующий файл src\ds_res\Root.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import React, { useEffect, useState } from 'react'; import luxmsService from './API/luxmsService'; import DashboardCustom from './dashboard-componets/DashboardCustom'; import './styles/Root.css'; function Root(props) { const schema_name = 'ds_4' const dashletTitle = 'Заголовок дэша' const [dashlets, setDashlets] = useState([]); useEffect(() => { loadDashlet() }, []) async function loadDashlet() { const response = await luxmsService.getDashlets(schema_name, 1); setDashlets(response); } return <DashboardCustom id='dash_test' dashlets={dashlets} schema_name={schema_name} dashlet_title={dashletTitle} key={schema_name} /> export default Root; |
В данном примере имя схемы и заголовок захардкожены. Имя схемы можно посмотреть например в строке браузера или через API. На данной странице через хуки подгружаются данные из API и вызывается компонент DashboardCustom
Вызов API luxms
Есть как минимум 2 подхода вытащить данные из сервера Luxms.
- Воспользоваться сервисом, которые предоставляют разработчики(На данный момент не очень хорошо задокументирован)
- Сделать вызов API на прямую через Js библиотеку, например axios
В этом примере использовался второй подход.
В корневой папке ds_res создаем папку API, а в ней js файл src\ds_res\API\luxmsService.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import axios from 'axios'; export default class luxmsService{ static async getDatasets(){ const response = await axios.get('/api/db/adm.datasets') return response.data } static async getDashlets(schemaName, dboard_id){ const url = '/api/db/' + schemaName + '.dashlets/.order_by(srt,id).filter(dashboard_id='+dboard_id+')'; const response = await axios.get(url); return response.data } static async getDashboardList(schemaName){ const url = '/api/db/' + schemaName + '.dashboards/.order_by(srt,id)'; const response = await axios.get(url); return response.data } static async getGroupAtlas(){ const response = await axios.get('/api/db/adm.topics/.order_by(srt,title)'); return response.data } static async getMapAtlasDash(group_id){ let url = '/api/db/adm.topic_dataset_maps/'; if(group_id){ url = url + '.filter(topic_id='+ group_id +')'; } const response = await axios.get(url); return response.data } } |
В данном классе описаны эндпоинты для доступа к данным дэшлетов, дашбордов, атласов и групп атласов.
В текущем примере в компоненте Root используется getDashlets
ответ от сервера при вызове этого метода выглядит примерно следующим образом (чисто для примера)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
[ { "id": 1, "parent_id": null, "dashboard_id": 1, "view_class": "html", "title": "", "description": null, "frame": null, "layout": null, "length": null, "idx": 0, "srt": 2147483647, "config": { "frame": { "h": 5, "w": 9, "x": 0, "y": 0 }, "htmlText": "\u003Ch1 style=\"font-size: 100px\" \u003EHello World\u003C/h1\u003E", "dataSource": { } }, "updated": "2024-07-08T08:11:59.523305+00:00", "created": "2024-07-08T08:11:59.523305+00:00" } ] |
Добавление CSS стилей
Для того что бы кастомный дэшборд был похож на оригинальный, нужно задать ему стили. Для этой задачи, нам идеально подойдет grid-template. Ранее мы уже расставили координаты расположения дэшлетов в компоненте DashletCustom. Теперь нужно добавить оставшиеся стили.
Создаем папку со стилями src\ds_res\styles.
В ней создаем файл src\ds_res\styles\Root.css
Обратите внимание, что этот файл подключается в компоненте Root
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
* { box-sizing: border-box; } .wrapper { border: 2px solid black; border-radius: 5px; background-color: whitesmoke; display: grid; grid-template-columns: repeat(12, 1fr); grid-template-rows: repeat(12, 100px); overflow: auto; } .wrapper>div { border: 2px solid grey; border-radius: 5px; background-color: white; padding: 1em; color: #d9480f; } |
Здесь нужно внимательно посмотреть на селекторы grid-template-columns и grid-template-rows. Именно они задают размер всего поля дэшлета и размер «одной точки» на поле. От этого размера будет зависеть масштаб отображения.
Итоговая структура проекта
В итоге структура проекта со всеми добавленными папками и компонентами должна выглядеть примерно следующим образом. (Пара не нужных файлов заблюрены)
Результаты
Для демонстрации добавлю пару скринов.
Оригинал, содержит один HTML дэшлет
Кастомное отображение
Далее попробуем сделать более сложное расположение блоков
Идем на нашу страницу, обновляем ее
В итоге получаем примерно такое же отображение как и в оригинале. Это работает так же с остальными классами отображения и с более сложными схемами. Например есть такое понятие как «управляющий дэшлет». Когда интерактивность в одном дэшлете влияет на отображение другого. Например в одном ставятся фильтры, в другом отображается таблица по этим фильтрам. Такая схема так же работает в кастомном отображении.
Что можно улучшить/доработать
Так как далее эта разработка не понадобилась, есть пару мыслей по улучшению. Можно подогнать размеры более точно. Добавить стилей для более органичного отображения. Можно было бы сделать динамическими размеры дэшборда, сейчас они фиксированные, по координатам есть возможность вычислить наибольшие отдаленные точки.
Так же есть возможность подгружать все дэшборды с проекта и открывать их по клику кнопки или иными способами, в прототипе использовались дополнительные компоненты для вкладок(tabs), но в статье они убраны что бы не загромождать демонстрацию. Подгружать данные об всех Атласах, Дэшбордах и т.д. можно методами класса luxmsService (был описан выше)
Область применения до конца не определенная, но например этим можно воспользоваться что бы отображать что-то в переопределённых компонентах пользовательского интерфейса сразу, без необходимости переходить во внутрь сущностей Атласа. В общем здесь имеется свобода в кастомизации, от чего Luxms выглядит привлекательно для разработки.