Files
2026-08-08 16:55:22 +08:00

1129 lines
56 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "adminive/status.hpp"
#include "adminive/view_json.hpp"
#include <algorithm>
#include <cstdint>
#include <map>
#include <stdexcept>
#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);
}
inline std::string escape_amis_string_literal(std::string_view value) {
std::string result;
result.reserve(value.size());
for(const char character : value) {
if(character == '\\' || character == '\'') {
result.push_back('\\');
}
if(character == '\n') {
result += "\\n";
} else if(character == '\r') {
result += "\\r";
} else {
result.push_back(character);
}
}
return result;
}
inline std::string resolve_visible_on(std::string expression, std::string_view prefix) {
constexpr std::string_view marker{"$self"};
std::size_t position{};
while((position = expression.find(marker, position)) != std::string::npos) {
const bool dotted = position + marker.size() < expression.size() && expression[position + marker.size()] == '.';
std::string replacement(prefix);
if(!replacement.empty() && dotted) {
replacement.push_back('.');
}
const std::size_t count = marker.size() + (dotted ? 1 : 0);
expression.replace(position, count, replacement);
position += replacement.size();
}
return expression;
}
template <Json_Type Json>
const Json& field_presentation(const Json& field) {
return Json_Adapter<Json>::at(field, "presentation");
}
template <Json_Type Json>
std::string field_label(const Json& field) {
return json_get<Json, std::string>(Json_Adapter<Json>::at(field_presentation<Json>(field), "label"));
}
template <Json_Type Json>
void apply_visible_on(Json& control, const Json& field, std::string_view prefix) {
const auto& presentation = field_presentation<Json>(field);
if(Json_Adapter<Json>::contains(presentation, "visible_on")) {
json_set(control, "visibleOn", resolve_visible_on(json_get<Json, std::string>(Json_Adapter<Json>::at(presentation, "visible_on")), prefix));
}
}
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_form_body(const Json& descriptor, std::string_view permission, std::string_view prefix = {});
template <Json_Type Json, Described_Type T>
Json make_typed_amis_form_body(const Json& descriptor, std::string_view permission, std::string_view prefix = {});
template <Json_Type Json, class T>
requires Polymorphic_Type<T, Json>
Json make_typed_amis_polymorphic_control(const Json& field, std::string_view permission, std::string_view prefix);
template <Json_Type Json>
Json make_amis_polymorphic_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 auto& metadata = Json_Adapter<Json>::at(field, "polymorphic");
const std::string discriminator = json_get<Json, std::string>(Json_Adapter<Json>::at(metadata, "discriminator"));
Json selector = json_object<Json>();
json_set(selector, "type", "select");
json_set(selector, "name", make_field_name(full_name, discriminator));
json_set(selector, "label", Json_Adapter<Json>::at(metadata, "discriminator_label"));
json_set(selector, "options", Json_Adapter<Json>::at(metadata, "options"));
json_set(selector, "required", true);
Json body = json_array<Json>();
json_append(body, std::move(selector));
const auto& variants = Json_Adapter<Json>::at(metadata, "variants");
for(std::size_t index = 0; index < Json_Adapter<Json>::size(variants); ++index) {
const auto& variant = Json_Adapter<Json>::at(variants, index);
const std::string value = json_get<Json, std::string>(Json_Adapter<Json>::at(variant, "value"));
Json group = json_object<Json>();
json_set(group, "type", "container");
json_set(group, "visibleOn", "${" + make_field_name(full_name, discriminator) + " == '" + escape_amis_string_literal(value) + "'}");
json_set(group, "body", make_amis_form_body<Json>(Json_Adapter<Json>::at(variant, "descriptor"), permission, full_name));
json_append(body, std::move(group));
}
Json result = json_object<Json>();
json_set(result, "type", "fieldset");
json_set(result, "title", Json_Adapter<Json>::at(field_presentation<Json>(field), "label"));
json_set(result, "body", std::move(body));
json_set(result, "collapsable", true);
json_set(result, "collapsed", false);
if(Json_Adapter<Json>::contains(field_presentation<Json>(field), "description")) {
json_set(result, "description", Json_Adapter<Json>::at(field_presentation<Json>(field), "description"));
}
apply_visible_on(result, field, prefix);
return result;
}
template <Json_Type Json, class T>
requires Polymorphic_Type<T, Json>
Json make_typed_amis_polymorphic_control(const Json& field, std::string_view permission, std::string_view prefix) {
using Adapter = Polymorphic_Adapter<std::remove_cvref_t<T>, Json>;
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 auto& metadata = Json_Adapter<Json>::at(field, "polymorphic");
const std::string discriminator(Adapter::discriminator());
Json selector = json_object<Json>();
json_set(selector, "type", "select");
json_set(selector, "name", make_field_name(full_name, discriminator));
json_set(selector, "label", Json_Adapter<Json>::at(metadata, "discriminator_label"));
json_set(selector, "options", Json_Adapter<Json>::at(metadata, "options"));
json_set(selector, "required", true);
Json body = json_array<Json>();
json_append(body, std::move(selector));
std::apply([&](const auto&... variant) {
([&] {
using Variant = typename std::remove_cvref_t<decltype(variant)>::type;
const Json descriptor = to_descriptor_json<Json, Variant>();
Json group = json_object<Json>();
json_set(group, "type", "container");
json_set(group, "visibleOn", "${" + make_field_name(full_name, discriminator) + " == '" + escape_amis_string_literal(variant.value) + "'}");
json_set(group, "body", make_typed_amis_form_body<Json, Object_Model_Type<Variant>>(descriptor, permission, full_name));
json_append(body, std::move(group));
}(), ...);
}, Adapter::variants());
Json result = json_object<Json>();
json_set(result, "type", "fieldset");
json_set(result, "title", Json_Adapter<Json>::at(field_presentation<Json>(field), "label"));
json_set(result, "body", std::move(body));
json_set(result, "collapsable", true);
json_set(result, "collapsed", false);
if(Json_Adapter<Json>::contains(field_presentation<Json>(field), "description")) {
json_set(result, "description", Json_Adapter<Json>::at(field_presentation<Json>(field), "description"));
}
apply_visible_on(result, field, prefix);
return result;
}
inline std::string_view amis_control_type(std::string_view control, std::string_view value_type) {
if(control == "text") {
return "input-text";
}
if(control == "multiline_text") {
return "textarea";
}
if(control == "number") {
return "input-number";
}
if(control == "boolean") {
return "switch";
}
if(control == "select") {
return "select";
}
if(control == "date") {
return "input-date";
}
if(control == "color") {
return "input-color";
}
if(control != "automatic") {
throw std::invalid_argument("unknown field control: " + std::string(control));
}
if(value_type == "boolean") {
return "switch";
}
if(value_type == "integer" || value_type == "number") {
return "input-number";
}
if(value_type == "enum") {
return "select";
}
if(value_type == "string") {
return "input-text";
}
return {};
}
template <Json_Type Json>
Json make_default_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 descriptor = json_object<Json>();
json_set(descriptor, "fields", Json_Adapter<Json>::at(field, "children"));
Json control = json_object<Json>();
json_set(control, "type", "fieldset");
json_set(control, "title", Json_Adapter<Json>::at(field_presentation<Json>(field), "label"));
json_set(control, "body", make_amis_form_body<Json>(descriptor, permission, full_name));
json_set(control, "collapsable", true);
json_set(control, "collapsed", false);
if(Json_Adapter<Json>::contains(field_presentation<Json>(field), "description")) {
json_set(control, "description", Json_Adapter<Json>::at(field_presentation<Json>(field), "description"));
}
apply_visible_on(control, field, prefix);
return control;
}
if(value_type == "polymorphic" && Json_Adapter<Json>::contains(field, "polymorphic")) {
return make_amis_polymorphic_control<Json>(field, permission, prefix);
}
Json control = json_object<Json>();
json_set(control, "name", full_name);
json_set(control, "label", Json_Adapter<Json>::at(field_presentation<Json>(field), "label"));
if(Json_Adapter<Json>::contains(field_presentation<Json>(field), "description")) {
json_set(control, "description", Json_Adapter<Json>::at(field_presentation<Json>(field), "description"));
}
apply_visible_on(control, field, prefix);
if(!json_get<Json, bool>(Json_Adapter<Json>::at(field, permission))) {
json_set(control, "type", "static");
return control;
}
const auto control_type = amis_control_type(json_get<Json, std::string>(Json_Adapter<Json>::at(field_presentation<Json>(field), "control")), value_type);
if(control_type.empty()) {
throw std::invalid_argument("field '" + full_name + "' requires an explicit field control or Control_Adapter");
}
json_set(control, "type", std::string(control_type));
const std::string resolved_control_type = json_get<Json, std::string>(Json_Adapter<Json>::at(control, "type"));
if(Json_Adapter<Json>::contains(field, "nullable") && json_get<Json, bool>(Json_Adapter<Json>::at(field, "nullable"))) {
json_set(control, "clearable", true);
}
if(resolved_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(resolved_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_presentation<Json>(field), "options")) {
json_set(control, "options", Json_Adapter<Json>::at(field_presentation<Json>(field), "options"));
}
return control;
}
template <Json_Type Json>
Json make_amis_control(const Json& field, std::string_view permission, std::string_view prefix = {}) {
return make_default_amis_control<Json>(field, permission, prefix);
}
template <Json_Type Json, class T>
Json make_typed_amis_control(const Json& field, std::string_view permission, std::string_view prefix = {}) {
using Storage = std::remove_cvref_t<T>;
using Value = Adapted_Value_Type<Storage, Json>;
using Control_Value = Optional_Unwrapped_Type<Value>;
const Control_Context context{permission, prefix};
if constexpr(Control_Adapter_With_Control<Storage, Json>) {
return Control_Adapter<Storage, Json>::make_control(field, context);
} else if constexpr(!std::same_as<Storage, Value> && Control_Adapter_With_Control<Value, Json>) {
return Control_Adapter<Value, Json>::make_control(field, context);
} else if constexpr(!std::same_as<Value, Control_Value> && Control_Adapter_With_Control<Control_Value, Json>) {
return Control_Adapter<Control_Value, Json>::make_control(field, context);
} else if constexpr(Polymorphic_Type<Control_Value, Json>) {
return make_typed_amis_polymorphic_control<Json, Control_Value>(field, permission, prefix);
} else if constexpr(Described_Type<Control_Value> || Snapshot_Adapted_Object<Control_Value>) {
using Model = Object_Model_Type<Control_Value>;
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 Json descriptor = to_descriptor_json<Json, Control_Value>();
Json control = json_object<Json>();
json_set(control, "type", "fieldset");
json_set(control, "title", Json_Adapter<Json>::at(field_presentation<Json>(field), "label"));
json_set(control, "body", make_typed_amis_form_body<Json, Model>(descriptor, permission, full_name));
json_set(control, "collapsable", true);
json_set(control, "collapsed", false);
if(Json_Adapter<Json>::contains(field_presentation<Json>(field), "description")) {
json_set(control, "description", Json_Adapter<Json>::at(field_presentation<Json>(field), "description"));
}
apply_visible_on(control, field, prefix);
return control;
} else {
return make_default_amis_control<Json>(field, permission, prefix);
}
}
template <Json_Type Json>
Json make_amis_form_body(const Json& descriptor, std::string_view permission, std::string_view prefix) {
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);
json_append(body, make_default_amis_control<Json>(field, permission, prefix));
}
return body;
}
template <std::size_t Index, Json_Type Json, class Descriptor>
void append_typed_amis_form_fields(Json& body, const Json& fields, const Descriptor& descriptor, std::string_view permission, std::string_view prefix) {
using Fields = std::remove_cvref_t<decltype(descriptor.fields())>;
if constexpr(Index < std::tuple_size_v<Fields>) {
const auto& item = std::get<Index>(descriptor.fields());
const auto& field = Json_Adapter<Json>::at(fields, Index);
using Field = std::remove_cvref_t<decltype(item)>;
json_append(body, make_typed_amis_control<Json, typename Field::member_type>(field, permission, prefix));
append_typed_amis_form_fields<Index + 1>(body, fields, descriptor, permission, prefix);
}
}
template <Json_Type Json, Described_Type T>
Json make_typed_amis_form_body(const Json& descriptor, std::string_view permission, std::string_view prefix) {
Json body = json_array<Json>();
const auto& fields = Json_Adapter<Json>::at(descriptor, "fields");
const auto object_descriptor = describe<T>();
append_typed_amis_form_fields<0>(body, fields, object_descriptor, permission, prefix);
return body;
}
template <Json_Type Json>
Json make_default_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_presentation<Json>(field), "label"));
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_presentation<Json>(field), "options")) {
json_set(column, "type", "mapping");
Json map = json_object<Json>();
const auto& options = Json_Adapter<Json>::at(field_presentation<Json>(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, class T>
Json make_typed_amis_column(const Json& field) {
using Storage = std::remove_cvref_t<T>;
using Value = Adapted_Value_Type<Storage, Json>;
using Control_Value = Optional_Unwrapped_Type<Value>;
if constexpr(Control_Adapter_With_Column<Storage, Json>) {
return Control_Adapter<Storage, Json>::make_column(field);
} else if constexpr(!std::same_as<Storage, Value> && Control_Adapter_With_Column<Value, Json>) {
return Control_Adapter<Value, Json>::make_column(field);
} else if constexpr(!std::same_as<Value, Control_Value> && Control_Adapter_With_Column<Control_Value, Json>) {
return Control_Adapter<Control_Value, Json>::make_column(field);
} else {
return make_default_amis_column<Json>(field);
}
}
template <std::size_t Index = 0, Json_Type Json, class Descriptor>
Json make_typed_amis_control_by_name(const Json& descriptor_json, const Descriptor& descriptor, std::string_view field_name, std::string_view permission, std::string_view prefix) {
using Fields = std::remove_cvref_t<decltype(descriptor.fields())>;
if constexpr(Index < std::tuple_size_v<Fields>) {
const auto& item = std::get<Index>(descriptor.fields());
if(item.name() == field_name) {
using Field = std::remove_cvref_t<decltype(item)>;
return make_typed_amis_control<Json, typename Field::member_type>(Json_Adapter<Json>::at(Json_Adapter<Json>::at(descriptor_json, "fields"), Index), permission, prefix);
}
return make_typed_amis_control_by_name<Index + 1>(descriptor_json, descriptor, field_name, permission, prefix);
} else {
throw std::invalid_argument("view field does not exist: " + std::string(field_name));
}
}
template <std::size_t Index = 0, Json_Type Json, class Descriptor>
Json make_typed_amis_column_by_name(const Json& descriptor_json, const Descriptor& descriptor, std::string_view field_name) {
using Fields = std::remove_cvref_t<decltype(descriptor.fields())>;
if constexpr(Index < std::tuple_size_v<Fields>) {
const auto& item = std::get<Index>(descriptor.fields());
if(item.name() == field_name) {
using Field = std::remove_cvref_t<decltype(item)>;
return make_typed_amis_column<Json, typename Field::member_type>(Json_Adapter<Json>::at(Json_Adapter<Json>::at(descriptor_json, "fields"), Index));
}
return make_typed_amis_column_by_name<Index + 1>(descriptor_json, descriptor, field_name);
} else {
throw std::invalid_argument("table column field does not exist: " + std::string(field_name));
}
}
inline std::string_view form_permission(Form_Mode mode) noexcept {
switch(mode) {
case Form_Mode::display:
return "readable";
case Form_Mode::edit:
return "editable";
case Form_Mode::create:
return "creatable";
}
return "readable";
}
inline std::size_t amis_grid_width(std::size_t index, std::size_t count) noexcept {
if(count == 0) {
return 12;
}
if(count >= 12) {
return 1;
}
const std::size_t width = 12 / count;
return width + (index % count < 12 % count ? 1 : 0);
}
inline void collect_amis_submit_fields(const View_Node& node, std::vector<std::string>& fields, bool& all_fields_selected) {
if(all_fields_selected) {
return;
}
if(node.kind == View_Node_Kind::all_fields) {
all_fields_selected = true;
fields.clear();
return;
}
if(node.kind == View_Node_Kind::field && std::find(fields.begin(), fields.end(), node.field) == fields.end()) {
fields.push_back(node.field);
}
for(const auto& child : node.children) {
collect_amis_submit_fields(child, fields, all_fields_selected);
}
}
template <Json_Type Json>
Json make_amis_submit_data(const Json& descriptor, const View_Node& body, std::string_view permission) {
std::vector<std::string> selected_fields;
bool all_fields_selected{};
collect_amis_submit_fields(body, selected_fields, all_fields_selected);
Json result = json_object<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 bool selected = all_fields_selected || std::find(selected_fields.begin(), selected_fields.end(), name) != selected_fields.end();
if(selected && json_get<Json, bool>(Json_Adapter<Json>::at(field, permission))) {
json_set(result, name, "${" + name + "}");
}
}
return result;
}
template <Json_Type Json>
void apply_view_node_options(Json& result, const View_Node& node, std::string_view prefix) {
if(!node.description.empty()) {
json_set(result, "description", node.description);
}
if(!node.visible_on.empty()) {
json_set(result, "visibleOn", resolve_visible_on(node.visible_on, prefix));
}
}
template <Json_Type Json, Described_Type T>
Json make_amis_view_body(const View_Node& node, const Json& descriptor_json, std::string_view permission, std::string_view prefix);
template <Json_Type Json, Described_Type T>
Json make_amis_view_node(const View_Node& node, const Json& descriptor_json, std::string_view permission, std::string_view prefix) {
const auto descriptor = describe<T>();
if(node.kind == View_Node_Kind::field) {
return make_typed_amis_control_by_name(descriptor_json, descriptor, node.field, permission, prefix);
}
if(node.kind == View_Node_Kind::frontend) {
Json result = json_object<Json>();
json_set(result, "type", "container");
json_set(result, "body", make_amis_view_body<Json, T>(View_Node{View_Node_Kind::vertical, {}, {}, {}, {}, 0, false, false, node.children}, descriptor_json, permission, prefix));
json_set(result, "className", "adminive-frontend-placement");
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::list) {
Json body = json_array<Json>();
for(const auto& child : node.children) {
Json item = json_object<Json>();
json_set(item, "type", "container");
json_set(item, "body", make_amis_view_body<Json, T>(child, descriptor_json, permission, prefix));
Json style = json_object<Json>();
json_set(style, "padding", "10px 0");
json_set(style, "borderBottom", "1px solid #e5e7eb");
json_set(item, "style", std::move(style));
json_append(body, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "type", "container");
json_set(result, "body", std::move(body));
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::group) {
Json result = json_object<Json>();
json_set(result, "type", "fieldset");
json_set(result, "title", node.title);
json_set(result, "body", make_amis_view_body<Json, T>(View_Node{View_Node_Kind::vertical, {}, {}, {}, {}, 0, false, false, node.children}, descriptor_json, permission, prefix));
if(node.collapsible) {
json_set(result, "collapsable", true);
json_set(result, "collapsed", node.collapsed);
}
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::card) {
Json result = json_object<Json>();
json_set(result, "type", "card");
if(!node.title.empty()) {
Json header = json_object<Json>();
json_set(header, "title", node.title);
json_set(result, "header", std::move(header));
}
json_set(result, "body", make_amis_view_body<Json, T>(View_Node{View_Node_Kind::vertical, {}, {}, {}, {}, 0, false, false, node.children}, descriptor_json, permission, prefix));
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::horizontal || node.kind == View_Node_Kind::grid) {
Json columns = json_array<Json>();
const std::size_t count = node.kind == View_Node_Kind::grid && node.columns != 0 ? node.columns : std::max<std::size_t>(1, node.children.size());
for(std::size_t index = 0; index < node.children.size(); ++index) {
Json column = json_object<Json>();
json_set(column, "md", amis_grid_width(index, count));
json_set(column, "body", make_amis_view_body<Json, T>(node.children[index], descriptor_json, permission, prefix));
json_append(columns, std::move(column));
}
Json result = json_object<Json>();
json_set(result, "type", "grid");
json_set(result, "columns", std::move(columns));
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::flow) {
Json items = json_array<Json>();
for(const auto& child : node.children) {
Json item = json_object<Json>();
json_set(item, "type", "container");
json_set(item, "body", make_amis_view_body<Json, T>(child, descriptor_json, permission, prefix));
Json item_style = json_object<Json>();
json_set(item_style, "flex", "1 1 280px");
json_set(item, "style", std::move(item_style));
json_append(items, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "type", "flex");
json_set(result, "direction", "row");
json_set(result, "items", std::move(items));
Json style = json_object<Json>();
json_set(style, "flexWrap", "wrap");
json_set(style, "gap", "12px");
json_set(result, "style", std::move(style));
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::tabs) {
Json tab_items = json_array<Json>();
for(const auto& child : node.children) {
Json item = json_object<Json>();
json_set(item, "title", child.title);
json_set(item, "body", make_amis_view_body<Json, T>(View_Node{View_Node_Kind::vertical, {}, {}, {}, {}, 0, false, false, child.children}, descriptor_json, permission, prefix));
if(!child.visible_on.empty()) {
json_set(item, "visibleOn", resolve_visible_on(child.visible_on, prefix));
}
json_append(tab_items, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "type", "tabs");
json_set(result, "tabs", std::move(tab_items));
apply_view_node_options(result, node, prefix);
return result;
}
if(node.kind == View_Node_Kind::tab) {
throw std::invalid_argument("tab view can only be used inside tabs");
}
Json result = json_object<Json>();
json_set(result, "type", "container");
json_set(result, "body", make_amis_view_body<Json, T>(View_Node{View_Node_Kind::vertical, {}, {}, {}, {}, 0, false, false, node.children}, descriptor_json, permission, prefix));
apply_view_node_options(result, node, prefix);
return result;
}
template <Json_Type Json, Described_Type T>
void append_amis_view_node(Json& body, const View_Node& node, const Json& descriptor_json, std::string_view permission, std::string_view prefix) {
if(node.kind == View_Node_Kind::all_fields) {
Json fields = make_typed_amis_form_body<Json, T>(descriptor_json, permission, prefix);
for(std::size_t index = 0; index < Json_Adapter<Json>::size(fields); ++index) {
json_append(body, Json_Adapter<Json>::at(fields, index));
}
return;
}
if(node.kind == View_Node_Kind::vertical) {
for(const auto& child : node.children) {
append_amis_view_node<Json, T>(body, child, descriptor_json, permission, prefix);
}
return;
}
json_append(body, make_amis_view_node<Json, T>(node, descriptor_json, permission, prefix));
}
template <Json_Type Json, Described_Type T>
Json make_amis_view_body(const View_Node& node, const Json& descriptor_json, std::string_view permission, std::string_view prefix) {
Json result = json_array<Json>();
append_amis_view_node<Json, T>(result, node, descriptor_json, permission, prefix);
return result;
}
template <Json_Type Json>
void apply_composition_node_options(Json& result, const Composition_Node& node) {
if(!node.description.empty()) {
json_set(result, "description", node.description);
}
if(!node.visible_on.empty()) {
json_set(result, "visibleOn", node.visible_on);
}
}
template <Json_Type Json>
Json make_amis_composition_body(const Composition_Node& node, const std::map<std::string, Json>& slots);
template <Json_Type Json>
Json make_amis_composition_node(const Composition_Node& node, const std::map<std::string, Json>& slots) {
if(node.kind == Composition_Node_Kind::slot) {
const auto iterator = slots.find(node.slot);
if(iterator == slots.end()) {
throw std::invalid_argument("composition slot is not provided: " + node.slot);
}
return iterator->second;
}
if(node.kind == Composition_Node_Kind::heading) {
Json result = json_object<Json>();
json_set(result, "type", "tpl");
std::string html = "<h3 style=\"margin:0";
if(!node.accent_color.empty()) {
html += ";color:" + node.accent_color;
}
html += "\">" + node.title + "</h3>";
json_set(result, "tpl", std::move(html));
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::frontend) {
Json result = json_object<Json>();
json_set(result, "type", "container");
json_set(result, "body", make_amis_composition_body<Json>(Composition_Node{Composition_Node_Kind::vertical, {}, {}, {}, {}, {}, 0, false, false, node.children}, slots));
json_set(result, "className", "adminive-frontend-placement");
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::list) {
Json body = json_array<Json>();
for(const auto& child : node.children) {
Json item = json_object<Json>();
json_set(item, "type", "container");
json_set(item, "body", make_amis_composition_body<Json>(child, slots));
Json style = json_object<Json>();
json_set(style, "padding", "10px 0");
json_set(style, "borderBottom", "1px solid #e5e7eb");
json_set(item, "style", std::move(style));
json_append(body, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "type", "container");
json_set(result, "body", std::move(body));
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::group) {
Json result = json_object<Json>();
json_set(result, "type", "panel");
json_set(result, "title", node.title);
json_set(result, "body", make_amis_composition_body<Json>(Composition_Node{Composition_Node_Kind::vertical, {}, {}, {}, {}, {}, 0, false, false, node.children}, slots));
if(node.collapsible) {
json_set(result, "collapsable", true);
json_set(result, "collapsed", node.collapsed);
}
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::card) {
Json result = json_object<Json>();
json_set(result, "type", "card");
if(!node.title.empty()) {
Json header = json_object<Json>();
json_set(header, "title", node.title);
json_set(result, "header", std::move(header));
}
json_set(result, "body", make_amis_composition_body<Json>(Composition_Node{Composition_Node_Kind::vertical, {}, {}, {}, {}, {}, 0, false, false, node.children}, slots));
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::horizontal || node.kind == Composition_Node_Kind::grid) {
Json columns = json_array<Json>();
const std::size_t count = node.kind == Composition_Node_Kind::grid && node.columns != 0 ? node.columns : std::max<std::size_t>(1, node.children.size());
for(std::size_t index = 0; index < node.children.size(); ++index) {
Json column = json_object<Json>();
json_set(column, "md", amis_grid_width(index, count));
json_set(column, "body", make_amis_composition_body<Json>(node.children[index], slots));
json_append(columns, std::move(column));
}
Json result = json_object<Json>();
json_set(result, "type", "grid");
json_set(result, "columns", std::move(columns));
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::flow) {
Json items = json_array<Json>();
for(const auto& child : node.children) {
Json item = json_object<Json>();
json_set(item, "type", "container");
json_set(item, "body", make_amis_composition_body<Json>(child, slots));
Json item_style = json_object<Json>();
json_set(item_style, "flex", "1 1 280px");
json_set(item, "style", std::move(item_style));
json_append(items, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "type", "flex");
json_set(result, "direction", "row");
json_set(result, "items", std::move(items));
Json style = json_object<Json>();
json_set(style, "flexWrap", "wrap");
json_set(style, "gap", "12px");
json_set(result, "style", std::move(style));
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::tabs) {
Json tab_items = json_array<Json>();
for(const auto& child : node.children) {
Json item = json_object<Json>();
json_set(item, "title", child.title);
json_set(item, "body", make_amis_composition_body<Json>(Composition_Node{Composition_Node_Kind::vertical, {}, {}, {}, {}, {}, 0, false, false, child.children}, slots));
if(!child.visible_on.empty()) {
json_set(item, "visibleOn", child.visible_on);
}
json_append(tab_items, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "type", "tabs");
json_set(result, "tabs", std::move(tab_items));
apply_composition_node_options(result, node);
return result;
}
if(node.kind == Composition_Node_Kind::tab) {
throw std::invalid_argument("composition tab can only be used inside tabs");
}
Json result = json_object<Json>();
json_set(result, "type", "container");
json_set(result, "body", make_amis_composition_body<Json>(Composition_Node{Composition_Node_Kind::vertical, {}, {}, {}, {}, {}, 0, false, false, node.children}, slots));
apply_composition_node_options(result, node);
return result;
}
template <Json_Type Json>
void append_amis_composition_node(Json& body, const Composition_Node& node, const std::map<std::string, Json>& slots) {
if(node.kind == Composition_Node_Kind::vertical) {
for(const auto& child : node.children) {
append_amis_composition_node(body, child, slots);
}
return;
}
if(node.kind == Composition_Node_Kind::slot) {
const auto iterator = slots.find(node.slot);
if(iterator == slots.end()) {
throw std::invalid_argument("composition slot is not provided: " + node.slot);
}
if(Json_Adapter<Json>::is_array(iterator->second)) {
for(std::size_t index = 0; index < Json_Adapter<Json>::size(iterator->second); ++index) {
json_append(body, Json_Adapter<Json>::at(iterator->second, index));
}
} else {
json_append(body, iterator->second);
}
return;
}
json_append(body, make_amis_composition_node<Json>(node, slots));
}
template <Json_Type Json>
Json make_amis_composition_body(const Composition_Node& node, const std::map<std::string, Json>& slots) {
Json result = json_array<Json>();
append_amis_composition_node(result, node, slots);
return result;
}
template <Json_Type Json>
Json to_amis_composition_schema(const Composition_View& view, const std::map<std::string, Json>& slots) {
validate_composition_view(view);
Json result = json_object<Json>();
json_set(result, "type", view.title.empty() ? "container" : "panel");
if(!view.title.empty()) {
json_set(result, "title", view.title);
}
json_set(result, "body", make_amis_composition_body<Json>(view.body, slots));
return result;
}
template <Json_Type Json>
Json make_amis_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_status_service(const Json& descriptor, std::string status_api, std::uint64_t interval = 2000) {
Json panel = json_object<Json>();
json_set(panel, "type", "panel");
json_set(panel, "title", Json_Adapter<Json>::at(descriptor, "label"));
json_set(panel, "body", make_amis_status_body<Json>(descriptor));
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", json_get<Json, std::string>(Json_Adapter<Json>::at(descriptor, "name")) + "_status");
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", std::move(panel));
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_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, class T>
requires Described_Type<std::remove_cvref_t<T>> || Snapshot_Adapted_Object<T>
Json to_amis_form_schema(const T& value, const Form_View& view, std::string submit_api = {}) {
using Model = Object_Model_Type<T>;
validate_form_view<Model>(view);
const Json descriptor = to_descriptor_json<Json, T>();
Json result = json_object<Json>();
json_set(result, "type", "form");
json_set(result, "title", view.title);
json_set(result, "data", to_frontend_json<Json>(value));
json_set(result, "body", make_amis_view_body<Json, Model>(view.body, descriptor, form_permission(view.mode), {}));
if(view.mode != Form_Mode::display) {
json_set(result, "actions", make_amis_form_actions<Json>(view.submit_label));
json_set(result, "affixFooter", view.affix_footer);
if(!submit_api.empty()) {
Json api = json_object<Json>();
json_set(api, "method", "post");
json_set(api, "url", std::move(submit_api));
json_set(api, "data", make_amis_submit_data<Json>(descriptor, view.body, form_permission(view.mode)));
json_set(result, "api", std::move(api));
}
}
return result;
}
template <Json_Type Json, Managed_Value_Type T>
Json to_amis_form_schema(const T& value, const Form_View& view, std::string submit_api = {}) {
return value.read([&](const auto& item) {
return adminive::to_amis_form_schema<Json>(item, view, std::move(submit_api));
});
}
template <Json_Type Json, Managed_Field_Type T>
Json to_amis_form_schema(const T& value, const Form_View& view, std::string submit_api = {}) {
return value.read([&](const auto& item) {
return adminive::to_amis_form_schema<Json>(item, view, std::move(submit_api));
});
}
inline std::string collection_field_label(const Collection_View& view, std::string_view field) {
for(const auto& column : view.table.columns) {
if(column.field == field) {
return column.label.empty() ? make_label(field) : column.label;
}
}
return make_label(field);
}
template <Json_Type Json>
Json make_amis_semantic_collection_item(const Collection_View& view) {
Json body = json_array<Json>();
for(const auto& field : view.item.fields) {
Json line = json_object<Json>();
json_set(line, "type", "tpl");
json_set(line, "tpl", "<span class=\"text-muted\">" + collection_field_label(view, field) + "</span><strong>${" + field + "}</strong>");
json_append(body, std::move(line));
}
if(!view.item.accent_field.empty()) {
Json accent = json_object<Json>();
json_set(accent, "type", "tpl");
json_set(accent, "tpl", "<div style=\"height:6px;border-radius:999px;background:${" + view.item.accent_field + "};margin-top:10px\"></div>");
json_append(body, std::move(accent));
}
if(view.mode == Collection_Mode::list) {
Json result = json_object<Json>();
json_set(result, "title", "${" + view.item.title_field + "}");
if(!view.item.subtitle_field.empty()) {
json_set(result, "subTitle", "${" + view.item.subtitle_field + "}");
}
json_set(result, "body", std::move(body));
return result;
}
Json header = json_object<Json>();
json_set(header, "title", "${" + view.item.title_field + "}");
if(!view.item.subtitle_field.empty()) {
json_set(header, "subTitle", "${" + view.item.subtitle_field + "}");
}
Json result = json_object<Json>();
json_set(result, "header", std::move(header));
json_set(result, "body", std::move(body));
return result;
}
template <Json_Type Json, class T>
requires Described_Type<Object_Model_Type<T>>
Json make_amis_collection_schema(std::string base_api, const Table_View& view, std::string mode, const Json* item, std::size_t cards_columns, const Json* item_status_descriptor, std::uint64_t item_status_interval, const Json* overview_status_descriptor, std::string overview_status_api, std::uint64_t overview_status_interval) {
using Model = Object_Model_Type<T>;
validate_table_view<Model>(view);
const Json descriptor = to_descriptor_json<Json, T>();
const auto object_descriptor = describe<Model>();
std::string crud_id = view.name + "_crud";
if(mode != "table") {
crud_id += "_" + mode;
}
Json create_api = json_object<Json>();
json_set(create_api, "method", "post");
json_set(create_api, "url", base_api);
json_set(create_api, "data", make_amis_submit_data<Json>(descriptor, view.create_body, "creatable"));
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_view_body<Json, Model>(view.create_body, descriptor, "creatable", {}));
json_set(create_form, "actions", make_amis_form_actions<Json>(view.create_label));
Json edit_api = json_object<Json>();
json_set(edit_api, "method", "put");
json_set(edit_api, "url", base_api + "/${id}");
json_set(edit_api, "data", make_amis_submit_data<Json>(descriptor, view.edit_body, "editable"));
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_view_body<Json, Model>(view.edit_body, descriptor, "editable", {}));
json_set(edit_form, "actions", make_amis_form_actions<Json>(view.confirm_label));
Json create_dialog = json_object<Json>();
json_set(create_dialog, "title", view.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", view.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", view.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", view.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", view.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", view.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 toolbar = json_array<Json>();
if(mode == "table") {
Json toggler = json_object<Json>();
json_set(toggler, "type", "columns-toggler");
json_set(toggler, "draggable", view.column_reorderable);
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));
if(mode == "table") {
Json columns = json_array<Json>();
Json id_column = json_object<Json>();
json_set(id_column, "name", "id");
json_set(id_column, "label", view.id_label);
json_set(id_column, "sortable", true);
json_append(columns, std::move(id_column));
for(const auto& definition : view.columns) {
Json column_json = make_typed_amis_column_by_name(descriptor, object_descriptor, definition.field);
if(!definition.label.empty()) {
json_set(column_json, "label", definition.label);
}
json_set(column_json, "sortable", definition.sortable);
if(definition.searchable) {
json_set(column_json, "searchable", true);
}
if(definition.filterable) {
json_set(column_json, "filterable", true);
}
if(definition.fixed == Table_Fixed::left) {
json_set(column_json, "fixed", "left");
} else if(definition.fixed == Table_Fixed::right) {
json_set(column_json, "fixed", "right");
}
json_append(columns, std::move(column_json));
}
if(item_status_descriptor) {
json_append(columns, make_amis_object_status_column<Json>(*item_status_descriptor, base_api + "/${id}/status", item_status_interval, view.view_status_label));
}
Json operation = json_object<Json>();
json_set(operation, "type", "operation");
json_set(operation, "label", view.actions_label);
json_set(operation, "buttons", buttons);
json_append(columns, std::move(operation));
json_set(crud, "columns", std::move(columns));
} else {
Json item_schema = *item;
if(!Json_Adapter<Json>::contains(item_schema, "actions")) {
json_set(item_schema, "actions", buttons);
}
json_set(crud, "mode", mode);
json_set(crud, mode == "list" ? "listItem" : "card", std::move(item_schema));
if(mode == "cards") {
json_set(crud, "columnsCount", cards_columns);
}
}
if(!view.default_order_by.empty()) {
Json params = json_object<Json>();
json_set(params, "orderBy", view.default_order_by);
json_set(params, "orderDir", view.default_order_dir);
json_set(crud, "defaultParams", std::move(params));
}
if(view.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 page_body = json_array<Json>();
if(overview_status_descriptor && !overview_status_api.empty()) {
json_append(page_body, make_amis_status_service<Json>(*overview_status_descriptor, std::move(overview_status_api), overview_status_interval));
}
json_append(page_body, std::move(crud));
Json result = json_object<Json>();
json_set(result, "type", "page");
json_set(result, "title", view.title);
json_set(result, "body", std::move(page_body));
return result;
}
template <Json_Type Json, class T>
requires Described_Type<Object_Model_Type<T>>
Json to_amis_table_schema(std::string base_api, const Table_View& view, const Json* item_status_descriptor = nullptr, std::uint64_t item_status_interval = 2000, const Json* overview_status_descriptor = nullptr, std::string overview_status_api = {}, std::uint64_t overview_status_interval = 2000) {
return make_amis_collection_schema<Json, T>(std::move(base_api), view, "table", nullptr, 0, item_status_descriptor, item_status_interval, overview_status_descriptor, std::move(overview_status_api), overview_status_interval);
}
template <Json_Type Json, class T>
requires Described_Type<Object_Model_Type<T>>
Json to_amis_list_schema(std::string base_api, const Table_View& view, Json list_item, const Json* item_status_descriptor = nullptr, std::uint64_t item_status_interval = 2000, const Json* overview_status_descriptor = nullptr, std::string overview_status_api = {}, std::uint64_t overview_status_interval = 2000) {
return make_amis_collection_schema<Json, T>(std::move(base_api), view, "list", &list_item, 0, item_status_descriptor, item_status_interval, overview_status_descriptor, std::move(overview_status_api), overview_status_interval);
}
template <Json_Type Json, class T>
requires Described_Type<Object_Model_Type<T>>
Json to_amis_cards_schema(std::string base_api, const Table_View& view, Json card, std::size_t columns_count = 3, const Json* item_status_descriptor = nullptr, std::uint64_t item_status_interval = 2000, const Json* overview_status_descriptor = nullptr, std::string overview_status_api = {}, std::uint64_t overview_status_interval = 2000) {
return make_amis_collection_schema<Json, T>(std::move(base_api), view, "cards", &card, columns_count, item_status_descriptor, item_status_interval, overview_status_descriptor, std::move(overview_status_api), overview_status_interval);
}
template <Json_Type Json, class T>
requires Described_Type<Object_Model_Type<T>>
Json to_amis_collection_schema(std::string base_api, const Collection_View& view, const Json* item_status_descriptor = nullptr, std::uint64_t item_status_interval = 2000, const Json* overview_status_descriptor = nullptr, std::string overview_status_api = {}, std::uint64_t overview_status_interval = 2000) {
using Model = Object_Model_Type<T>;
validate_collection_view<Model>(view);
if(view.mode == Collection_Mode::table) {
return make_amis_collection_schema<Json, T>(std::move(base_api), view.table, "table", nullptr, 0, item_status_descriptor, item_status_interval, overview_status_descriptor, std::move(overview_status_api), overview_status_interval);
}
Json item = make_amis_semantic_collection_item<Json>(view);
return make_amis_collection_schema<Json, T>(std::move(base_api), view.table, std::string(collection_mode_name(view.mode)), &item, view.cards_columns, item_status_descriptor, item_status_interval, overview_status_descriptor, std::move(overview_status_api), overview_status_interval);
}
}