380 lines
19 KiB
C++
380 lines
19 KiB
C++
#pragma once
|
|
#include "adminive/status.hpp"
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
namespace adminive {
|
|
inline std::string make_field_name(std::string_view prefix, std::string_view name) {
|
|
if(prefix.empty()) {
|
|
return std::string(name);
|
|
}
|
|
return std::string(prefix) + "." + std::string(name);
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_form_actions(std::string submit_label = "Apply") {
|
|
Json reset = json_object<Json>();
|
|
json_set(reset, "type", "reset");
|
|
json_set(reset, "label", "Reset");
|
|
Json submit = json_object<Json>();
|
|
json_set(submit, "type", "submit");
|
|
json_set(submit, "label", std::move(submit_label));
|
|
json_set(submit, "level", "primary");
|
|
Json result = json_array<Json>();
|
|
json_append(result, std::move(reset));
|
|
json_append(result, std::move(submit));
|
|
return result;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_control(const Json& field, std::string_view permission, std::string_view prefix = {}) {
|
|
const std::string name = json_get<Json, std::string>(Json_Adapter<Json>::at(field, "name"));
|
|
const std::string full_name = make_field_name(prefix, name);
|
|
const std::string value_type = json_get<Json, std::string>(Json_Adapter<Json>::at(field, "value_type"));
|
|
if(value_type == "object" && Json_Adapter<Json>::contains(field, "children")) {
|
|
Json body = json_array<Json>();
|
|
const auto& children = Json_Adapter<Json>::at(field, "children");
|
|
for(std::size_t index = 0; index < Json_Adapter<Json>::size(children); ++index) {
|
|
const auto& child = Json_Adapter<Json>::at(children, index);
|
|
if(json_get<Json, bool>(Json_Adapter<Json>::at(child, "visible"))) {
|
|
json_append(body, make_amis_control<Json>(child, permission, full_name));
|
|
}
|
|
}
|
|
Json control = json_object<Json>();
|
|
json_set(control, "type", "fieldset");
|
|
json_set(control, "title", Json_Adapter<Json>::at(field, "label"));
|
|
json_set(control, "body", std::move(body));
|
|
json_set(control, "collapsable", true);
|
|
json_set(control, "collapsed", false);
|
|
if(Json_Adapter<Json>::contains(field, "description")) {
|
|
json_set(control, "description", Json_Adapter<Json>::at(field, "description"));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "visible_on")) {
|
|
json_set(control, "visibleOn", Json_Adapter<Json>::at(field, "visible_on"));
|
|
}
|
|
return control;
|
|
}
|
|
Json control = json_object<Json>();
|
|
json_set(control, "name", full_name);
|
|
json_set(control, "label", Json_Adapter<Json>::at(field, "label"));
|
|
if(Json_Adapter<Json>::contains(field, "description")) {
|
|
json_set(control, "description", Json_Adapter<Json>::at(field, "description"));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "visible_on")) {
|
|
json_set(control, "visibleOn", Json_Adapter<Json>::at(field, "visible_on"));
|
|
}
|
|
if(!json_get<Json, bool>(Json_Adapter<Json>::at(field, permission))) {
|
|
json_set(control, "type", "static");
|
|
return control;
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "widget")) {
|
|
json_set(control, "type", Json_Adapter<Json>::at(field, "widget"));
|
|
} else if(value_type == "boolean") {
|
|
json_set(control, "type", "switch");
|
|
} else if(value_type == "integer" || value_type == "number") {
|
|
json_set(control, "type", "input-number");
|
|
} else if(value_type == "enum") {
|
|
json_set(control, "type", "select");
|
|
} else {
|
|
json_set(control, "type", "input-text");
|
|
}
|
|
const std::string control_type = json_get<Json, std::string>(Json_Adapter<Json>::at(control, "type"));
|
|
if(control_type == "input-date") {
|
|
json_set(control, "valueFormat", "YYYY-MM-DD");
|
|
json_set(control, "displayFormat", "YYYY-MM-DD");
|
|
json_set(control, "clearable", true);
|
|
} else if(control_type == "input-color") {
|
|
json_set(control, "clearable", true);
|
|
}
|
|
if(json_get<Json, bool>(Json_Adapter<Json>::at(field, "required"))) {
|
|
json_set(control, "required", true);
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "minimum")) {
|
|
json_set(control, "min", Json_Adapter<Json>::at(field, "minimum"));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "maximum")) {
|
|
json_set(control, "max", Json_Adapter<Json>::at(field, "maximum"));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "multiple_of")) {
|
|
json_set(control, "step", Json_Adapter<Json>::at(field, "multiple_of"));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "validation_message")) {
|
|
Json errors = json_object<Json>();
|
|
json_set(errors, "isInt", Json_Adapter<Json>::at(field, "validation_message"));
|
|
json_set(control, "validationErrors", std::move(errors));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "options")) {
|
|
json_set(control, "options", Json_Adapter<Json>::at(field, "options"));
|
|
}
|
|
return control;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_form_body(const Json& descriptor, std::string_view permission) {
|
|
Json body = json_array<Json>();
|
|
const auto& fields = Json_Adapter<Json>::at(descriptor, "fields");
|
|
for(std::size_t index = 0; index < Json_Adapter<Json>::size(fields); ++index) {
|
|
const auto& field = Json_Adapter<Json>::at(fields, index);
|
|
if(json_get<Json, bool>(Json_Adapter<Json>::at(field, "visible"))) {
|
|
json_append(body, make_amis_control<Json>(field, permission));
|
|
}
|
|
}
|
|
return body;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_column(const Json& field) {
|
|
Json column = json_object<Json>();
|
|
json_set(column, "name", Json_Adapter<Json>::at(field, "name"));
|
|
json_set(column, "label", Json_Adapter<Json>::at(field, "list_label"));
|
|
if(Json_Adapter<Json>::contains(field, "order")) {
|
|
json_set(column, "order", Json_Adapter<Json>::at(field, "order"));
|
|
}
|
|
if(Json_Adapter<Json>::contains(field, "sortable")) {
|
|
json_set(column, "sortable", Json_Adapter<Json>::at(field, "sortable"));
|
|
}
|
|
const std::string value_type = json_get<Json, std::string>(Json_Adapter<Json>::at(field, "value_type"));
|
|
if(value_type == "boolean") {
|
|
json_set(column, "type", "status");
|
|
} else if(value_type == "enum" && Json_Adapter<Json>::contains(field, "options")) {
|
|
json_set(column, "type", "mapping");
|
|
Json map = json_object<Json>();
|
|
const auto& options = Json_Adapter<Json>::at(field, "options");
|
|
for(std::size_t index = 0; index < Json_Adapter<Json>::size(options); ++index) {
|
|
const auto& option = Json_Adapter<Json>::at(options, index);
|
|
json_set(map, json_get<Json, std::string>(Json_Adapter<Json>::at(option, "value")), Json_Adapter<Json>::at(option, "label"));
|
|
}
|
|
json_set(column, "map", std::move(map));
|
|
}
|
|
return column;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_status_service(std::string status_api) {
|
|
Json view = json_object<Json>();
|
|
json_set(view, "type", "tpl");
|
|
json_set(view, "tpl", R"(<div class="adminive-status-grid"><div><strong>Service</strong><span>${service_state}</span></div><div><strong>Server time</strong><span>${server_time}</span></div><div><strong>Uptime</strong><span>${uptime_seconds} s</span></div><div><strong>Polling sequence</strong><span>${poll_sequence}</span></div><div><strong>Radio states</strong><span>${radio_state_count}</span></div><div><strong>Listen port</strong><span>${listen_port}</span></div></div>)");
|
|
Json panel = json_object<Json>();
|
|
json_set(panel, "type", "panel");
|
|
json_set(panel, "title", "Backend Status");
|
|
json_set(panel, "body", std::move(view));
|
|
Json api = json_object<Json>();
|
|
json_set(api, "method", "get");
|
|
json_set(api, "url", std::move(status_api));
|
|
Json result = json_object<Json>();
|
|
json_set(result, "type", "service");
|
|
json_set(result, "name", "backend_status");
|
|
json_set(result, "api", std::move(api));
|
|
json_set(result, "initFetch", true);
|
|
json_set(result, "interval", 2000);
|
|
json_set(result, "silentPolling", true);
|
|
json_set(result, "showErrorMsg", true);
|
|
json_set(result, "body", std::move(panel));
|
|
return result;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_object_status_body(const Json& descriptor) {
|
|
Json body = json_array<Json>();
|
|
const auto& fields = Json_Adapter<Json>::at(descriptor, "fields");
|
|
for(std::size_t index = 0; index < Json_Adapter<Json>::size(fields); ++index) {
|
|
const auto& field = Json_Adapter<Json>::at(fields, index);
|
|
const std::string name = json_get<Json, std::string>(Json_Adapter<Json>::at(field, "name"));
|
|
const std::string label = json_get<Json, std::string>(Json_Adapter<Json>::at(field, "label"));
|
|
const bool color = json_get<Json, bool>(Json_Adapter<Json>::at(field, "color"));
|
|
std::string tpl = "<div class=\"adminive-object-status-row\"><span class=\"adminive-object-status-label\">" + label + "</span><strong";
|
|
if(color) {
|
|
tpl += " style=\"color: ${" + name + ".color}\"";
|
|
}
|
|
tpl += ">${" + name + ".value}</strong></div>";
|
|
Json item = json_object<Json>();
|
|
json_set(item, "type", "tpl");
|
|
json_set(item, "tpl", std::move(tpl));
|
|
json_append(body, std::move(item));
|
|
}
|
|
Json result = json_object<Json>();
|
|
json_set(result, "type", "container");
|
|
json_set(result, "className", "adminive-object-status");
|
|
json_set(result, "body", std::move(body));
|
|
return result;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_object_status_service(const Json& descriptor, std::string status_api, std::uint64_t interval) {
|
|
Json api = json_object<Json>();
|
|
json_set(api, "method", "get");
|
|
json_set(api, "url", std::move(status_api));
|
|
Json result = json_object<Json>();
|
|
json_set(result, "type", "service");
|
|
json_set(result, "api", std::move(api));
|
|
json_set(result, "initFetch", true);
|
|
json_set(result, "interval", interval);
|
|
json_set(result, "silentPolling", true);
|
|
json_set(result, "showErrorMsg", true);
|
|
json_set(result, "body", make_amis_object_status_body<Json>(descriptor));
|
|
return result;
|
|
}
|
|
template <Json_Type Json>
|
|
Json make_amis_object_status_column(const Json& descriptor, std::string status_api, std::uint64_t interval, std::string action_label) {
|
|
Json pop_over = json_object<Json>();
|
|
json_set(pop_over, "trigger", "click");
|
|
json_set(pop_over, "position", "left-top");
|
|
json_set(pop_over, "showIcon", false);
|
|
json_set(pop_over, "body", make_amis_object_status_service<Json>(descriptor, std::move(status_api), interval));
|
|
Json result = json_object<Json>();
|
|
json_set(result, "type", "tpl");
|
|
json_set(result, "label", Json_Adapter<Json>::at(descriptor, "label"));
|
|
json_set(result, "tpl", "<span class=\"text-primary\">" + std::move(action_label) + "</span>");
|
|
json_set(result, "popOver", std::move(pop_over));
|
|
return result;
|
|
}
|
|
template <Json_Type Json, Described_Type T>
|
|
Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::string submit_label = "Apply") {
|
|
const Json descriptor = to_descriptor_json<Json, T>();
|
|
Json result = json_object<Json>();
|
|
json_set(result, "type", "form");
|
|
json_set(result, "title", Json_Adapter<Json>::at(descriptor, "label"));
|
|
json_set(result, "data", to_frontend_json<Json>(value));
|
|
json_set(result, "body", make_amis_form_body<Json>(descriptor, "editable"));
|
|
json_set(result, "actions", make_amis_form_actions<Json>(std::move(submit_label)));
|
|
json_set(result, "affixFooter", true);
|
|
if(!submit_api.empty()) {
|
|
Json api = json_object<Json>();
|
|
json_set(api, "method", "post");
|
|
json_set(api, "url", std::move(submit_api));
|
|
json_set(result, "api", std::move(api));
|
|
}
|
|
return result;
|
|
}
|
|
template <Json_Type Json, Described_Type T>
|
|
Json to_amis_crud_schema(std::string base_api, const Json* status_descriptor = nullptr, std::uint64_t status_interval = 2000) {
|
|
const Json descriptor = to_descriptor_json<Json, T>();
|
|
const Json& list = Json_Adapter<Json>::at(descriptor, "list");
|
|
const std::string crud_id = json_get<Json, std::string>(Json_Adapter<Json>::at(descriptor, "name")) + "_crud";
|
|
Json columns = json_array<Json>();
|
|
Json id_column = json_object<Json>();
|
|
json_set(id_column, "name", "id");
|
|
json_set(id_column, "label", Json_Adapter<Json>::at(list, "id_label"));
|
|
json_set(id_column, "sortable", true);
|
|
json_append(columns, std::move(id_column));
|
|
std::vector<Json> list_fields;
|
|
const auto& fields = Json_Adapter<Json>::at(descriptor, "fields");
|
|
for(std::size_t index = 0; index < Json_Adapter<Json>::size(fields); ++index) {
|
|
const auto& field = Json_Adapter<Json>::at(fields, index);
|
|
if(json_get<Json, bool>(Json_Adapter<Json>::at(field, "readable")) && json_get<Json, bool>(Json_Adapter<Json>::at(field, "list_visible")) && json_get<Json, std::string>(Json_Adapter<Json>::at(field, "value_type")) != "object") {
|
|
list_fields.push_back(field);
|
|
}
|
|
}
|
|
std::stable_sort(list_fields.begin(), list_fields.end(), [](const Json& left, const Json& right) {
|
|
return json_get<Json, int>(Json_Adapter<Json>::at(left, "order")) < json_get<Json, int>(Json_Adapter<Json>::at(right, "order"));
|
|
});
|
|
for(const auto& field : list_fields) {
|
|
json_append(columns, make_amis_column<Json>(field));
|
|
}
|
|
if(status_descriptor) {
|
|
json_append(columns, make_amis_object_status_column<Json>(*status_descriptor, base_api + "/${id}/status", status_interval, json_get<Json, std::string>(Json_Adapter<Json>::at(list, "view_status_label"))));
|
|
}
|
|
const std::string create_label = json_get<Json, std::string>(Json_Adapter<Json>::at(list, "create_label"));
|
|
const std::string edit_label = json_get<Json, std::string>(Json_Adapter<Json>::at(list, "edit_label"));
|
|
Json create_api = json_object<Json>();
|
|
json_set(create_api, "method", "post");
|
|
json_set(create_api, "url", base_api);
|
|
Json create_form = json_object<Json>();
|
|
json_set(create_form, "type", "form");
|
|
json_set(create_form, "api", std::move(create_api));
|
|
json_set(create_form, "reload", crud_id);
|
|
json_set(create_form, "body", make_amis_form_body<Json>(descriptor, "creatable"));
|
|
json_set(create_form, "actions", make_amis_form_actions<Json>(create_label));
|
|
Json edit_api = json_object<Json>();
|
|
json_set(edit_api, "method", "put");
|
|
json_set(edit_api, "url", base_api + "/${id}");
|
|
Json edit_form = json_object<Json>();
|
|
json_set(edit_form, "type", "form");
|
|
json_set(edit_form, "api", std::move(edit_api));
|
|
json_set(edit_form, "reload", crud_id);
|
|
json_set(edit_form, "body", make_amis_form_body<Json>(descriptor, "editable"));
|
|
json_set(edit_form, "actions", make_amis_form_actions<Json>(json_get<Json, std::string>(Json_Adapter<Json>::at(list, "confirm_label"))));
|
|
Json create_dialog = json_object<Json>();
|
|
json_set(create_dialog, "title", create_label + " " + json_get<Json, std::string>(Json_Adapter<Json>::at(descriptor, "label")));
|
|
json_set(create_dialog, "body", std::move(create_form));
|
|
Json create_button = json_object<Json>();
|
|
json_set(create_button, "type", "button");
|
|
json_set(create_button, "label", create_label);
|
|
json_set(create_button, "level", "primary");
|
|
json_set(create_button, "actionType", "dialog");
|
|
json_set(create_button, "dialog", std::move(create_dialog));
|
|
Json edit_dialog = json_object<Json>();
|
|
json_set(edit_dialog, "title", edit_label + " " + json_get<Json, std::string>(Json_Adapter<Json>::at(descriptor, "label")));
|
|
json_set(edit_dialog, "body", std::move(edit_form));
|
|
Json edit_button = json_object<Json>();
|
|
json_set(edit_button, "type", "button");
|
|
json_set(edit_button, "label", edit_label);
|
|
json_set(edit_button, "level", "link");
|
|
json_set(edit_button, "actionType", "dialog");
|
|
json_set(edit_button, "dialog", std::move(edit_dialog));
|
|
Json delete_api = json_object<Json>();
|
|
json_set(delete_api, "method", "delete");
|
|
json_set(delete_api, "url", base_api + "/${id}");
|
|
Json delete_button = json_object<Json>();
|
|
json_set(delete_button, "type", "button");
|
|
json_set(delete_button, "label", Json_Adapter<Json>::at(list, "delete_label"));
|
|
json_set(delete_button, "level", "link");
|
|
json_set(delete_button, "className", "text-danger");
|
|
json_set(delete_button, "actionType", "ajax");
|
|
json_set(delete_button, "confirmText", Json_Adapter<Json>::at(list, "delete_confirm"));
|
|
json_set(delete_button, "api", std::move(delete_api));
|
|
json_set(delete_button, "reload", crud_id);
|
|
Json buttons = json_array<Json>();
|
|
json_append(buttons, std::move(edit_button));
|
|
json_append(buttons, std::move(delete_button));
|
|
Json operation = json_object<Json>();
|
|
json_set(operation, "type", "operation");
|
|
json_set(operation, "label", Json_Adapter<Json>::at(list, "actions_label"));
|
|
json_set(operation, "buttons", std::move(buttons));
|
|
json_append(columns, std::move(operation));
|
|
Json toggler = json_object<Json>();
|
|
json_set(toggler, "type", "columns-toggler");
|
|
json_set(toggler, "draggable", Json_Adapter<Json>::at(list, "column_reorderable"));
|
|
Json toolbar = json_array<Json>();
|
|
json_append(toolbar, std::move(toggler));
|
|
json_append(toolbar, "reload");
|
|
json_append(toolbar, std::move(create_button));
|
|
Json crud_api = json_object<Json>();
|
|
json_set(crud_api, "method", "get");
|
|
json_set(crud_api, "url", base_api);
|
|
Json crud = json_object<Json>();
|
|
json_set(crud, "type", "crud");
|
|
json_set(crud, "id", crud_id);
|
|
json_set(crud, "api", std::move(crud_api));
|
|
json_set(crud, "syncLocation", false);
|
|
json_set(crud, "primaryField", "id");
|
|
json_set(crud, "headerToolbar", std::move(toolbar));
|
|
json_set(crud, "columns", std::move(columns));
|
|
const std::string default_order_by = json_get<Json, std::string>(Json_Adapter<Json>::at(list, "default_order_by"));
|
|
if(!default_order_by.empty()) {
|
|
Json params = json_object<Json>();
|
|
json_set(params, "orderBy", default_order_by);
|
|
json_set(params, "orderDir", Json_Adapter<Json>::at(list, "default_order_dir"));
|
|
json_set(crud, "defaultParams", std::move(params));
|
|
}
|
|
if(json_get<Json, bool>(Json_Adapter<Json>::at(list, "user_reorderable"))) {
|
|
json_set(crud, "draggable", true);
|
|
Json order_api = json_object<Json>();
|
|
json_set(order_api, "method", "post");
|
|
json_set(order_api, "url", base_api + "/order");
|
|
json_set(crud, "saveOrderApi", std::move(order_api));
|
|
}
|
|
Json result = json_object<Json>();
|
|
json_set(result, "type", "page");
|
|
json_set(result, "title", Json_Adapter<Json>::at(list, "management_label"));
|
|
json_set(result, "body", std::move(crud));
|
|
return result;
|
|
}
|
|
template <Json_Type Json, Described_Type T>
|
|
Json to_amis_crud_status_schema(std::string base_api, std::string status_api, const Json* object_status_descriptor = nullptr, std::uint64_t object_status_interval = 2000) {
|
|
Json result = to_amis_crud_schema<Json, T>(std::move(base_api), object_status_descriptor, object_status_interval);
|
|
Json crud = std::move(Json_Adapter<Json>::at(result, "body"));
|
|
Json body = json_array<Json>();
|
|
json_append(body, make_amis_status_service<Json>(std::move(status_api)));
|
|
json_append(body, std::move(crud));
|
|
json_set(result, "body", std::move(body));
|
|
return result;
|
|
}
|
|
}
|