Files
Adminive/backend/library/include/adminive/amis.hpp
T
2026-08-07 23:44:35 +08:00

608 lines
32 KiB
C++

#pragma once
#include "adminive/status.hpp"
#include <algorithm>
#include <cstdint>
#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>
void apply_visible_on(Json& control, const Json& field, std::string_view prefix) {
if(Json_Adapter<Json>::contains(field, "visible_on")) {
json_set(control, "visibleOn", resolve_visible_on(json_get<Json, std::string>(Json_Adapter<Json>::at(field, "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, "label"));
json_set(result, "body", std::move(body));
json_set(result, "collapsable", true);
json_set(result, "collapsed", false);
if(Json_Adapter<Json>::contains(field, "description")) {
json_set(result, "description", Json_Adapter<Json>::at(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, "label"));
json_set(result, "body", std::move(body));
json_set(result, "collapsable", true);
json_set(result, "collapsed", false);
if(Json_Adapter<Json>::contains(field, "description")) {
json_set(result, "description", Json_Adapter<Json>::at(field, "description"));
}
apply_visible_on(result, field, prefix);
return result;
}
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, "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, "description")) {
json_set(control, "description", Json_Adapter<Json>::at(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, "label"));
if(Json_Adapter<Json>::contains(field, "description")) {
json_set(control, "description", Json_Adapter<Json>::at(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;
}
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 if(value_type == "string") {
json_set(control, "type", "input-text");
} else {
throw std::invalid_argument("field '" + full_name + "' requires an explicit widget or Control_Adapter");
}
const std::string 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(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_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, "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, "description")) {
json_set(control, "description", Json_Adapter<Json>::at(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);
if(json_get<Json, bool>(Json_Adapter<Json>::at(field, "visible"))) {
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);
if(json_get<Json, bool>(Json_Adapter<Json>::at(field, "visible"))) {
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, "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, 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 <Json_Type Json>
struct Amis_Ordered_Column {
int order{};
Json column;
};
template <std::size_t Index, Json_Type Json, class Descriptor>
void append_typed_amis_columns(std::vector<Amis_Ordered_Column<Json>>& columns, const Json& fields, const Descriptor& descriptor) {
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)>;
using Member = typename Field::member_type;
using Storage = std::remove_cvref_t<Member>;
using Value = Adapted_Value_Type<Storage, Json>;
using Control_Value = Optional_Unwrapped_Type<Value>;
constexpr bool custom_column = Control_Adapter_With_Column<Storage, Json> || (!std::same_as<Storage, Value> && Control_Adapter_With_Column<Value, Json>) || (!std::same_as<Value, Control_Value> && Control_Adapter_With_Column<Control_Value, Json>);
const std::string value_type = json_get<Json, std::string>(Json_Adapter<Json>::at(field, "value_type"));
const bool structural = value_type == "object" || value_type == "array" || value_type == "map" || value_type == "polymorphic";
if(json_get<Json, bool>(Json_Adapter<Json>::at(field, "readable")) && json_get<Json, bool>(Json_Adapter<Json>::at(field, "list_visible")) && (!structural || custom_column)) {
columns.push_back(Amis_Ordered_Column<Json>{json_get<Json, int>(Json_Adapter<Json>::at(field, "order")), make_typed_amis_column<Json, Member>(field)});
}
append_typed_amis_columns<Index + 1>(columns, fields, descriptor);
}
}
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, std::string submit_api = {}, std::string submit_label = "Apply") {
using Model = Object_Model_Type<T>;
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_typed_amis_form_body<Json, Model>(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, Managed_Value_Type T>
Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::string submit_label = "Apply") {
return value.read([&](const auto& item) {
return adminive::to_amis_form_schema<Json>(item, std::move(submit_api), std::move(submit_label));
});
}
template <Json_Type Json, Managed_Field_Type T>
Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::string submit_label = "Apply") {
return value.read([&](const auto& item) {
return adminive::to_amis_form_schema<Json>(item, std::move(submit_api), std::move(submit_label));
});
}
template <Json_Type Json, class T>
requires Described_Type<Object_Model_Type<T>>
Json to_amis_crud_schema(std::string base_api, const Json* status_descriptor = nullptr, std::uint64_t status_interval = 2000) {
using Model = Object_Model_Type<T>;
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<Amis_Ordered_Column<Json>> ordered_columns;
const auto& fields = Json_Adapter<Json>::at(descriptor, "fields");
const auto object_descriptor = describe<Model>();
append_typed_amis_columns<0>(ordered_columns, fields, object_descriptor);
std::stable_sort(ordered_columns.begin(), ordered_columns.end(), [](const Amis_Ordered_Column<Json>& left, const Amis_Ordered_Column<Json>& right) {
return left.order < right.order;
});
for(auto& column : ordered_columns) {
json_append(columns, std::move(column.column));
}
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_typed_amis_form_body<Json, Model>(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_typed_amis_form_body<Json, Model>(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, class T>
requires Described_Type<Object_Model_Type<T>>
Json to_amis_crud_status_schema(std::string base_api, std::string status_api, const Json& status_descriptor, std::uint64_t status_interval = 2000, 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>(status_descriptor, std::move(status_api), status_interval));
json_append(body, std::move(crud));
json_set(result, "body", std::move(body));
return result;
}
}