#pragma once #include "adminive/composition.hpp" #include "adminive/json.hpp" #include "adminive/view.hpp" #include #include namespace adminive { inline constexpr std::string_view view_protocol = "adminive.view"; inline constexpr int view_protocol_version = 1; inline constexpr std::string_view composition_manifest_protocol = "adminive.composition-manifest"; inline constexpr int composition_manifest_protocol_version = 1; inline std::string_view composition_node_kind_name(Composition_Node_Kind value) noexcept { switch(value) { case Composition_Node_Kind::slot: return "slot"; case Composition_Node_Kind::heading: return "heading"; case Composition_Node_Kind::frontend: return "frontend"; case Composition_Node_Kind::vertical: return "vertical"; case Composition_Node_Kind::horizontal: return "horizontal"; case Composition_Node_Kind::flow: return "flow"; case Composition_Node_Kind::grid: return "grid"; case Composition_Node_Kind::list: return "list"; case Composition_Node_Kind::group: return "group"; case Composition_Node_Kind::card: return "card"; case Composition_Node_Kind::tabs: return "tabs"; case Composition_Node_Kind::tab: return "tab"; } return "vertical"; } template Json composition_node_to_json(const Composition_Node& node) { Json result = json_object(); json_set(result, "kind", std::string(composition_node_kind_name(node.kind))); if(!node.slot.empty()) { json_set(result, "slot", node.slot); } if(!node.title.empty()) { json_set(result, "title", node.title); } if(!node.description.empty()) { json_set(result, "description", node.description); } if(!node.visible_on.empty()) { json_set(result, "visible_on", node.visible_on); } if(!node.accent_color.empty()) { json_set(result, "accent_color", node.accent_color); } if(node.columns != 0) { json_set(result, "columns", node.columns); } if(node.collapsible) { json_set(result, "collapsible", true); json_set(result, "collapsed", node.collapsed); } if(!node.children.empty()) { Json children = json_array(); for(const auto& child : node.children) { json_append(children, composition_node_to_json(child)); } json_set(result, "children", std::move(children)); } return result; } template Json to_view_json(const Composition_View& view) { validate_composition_view(view); Json result = json_object(); json_set(result, "protocol", view_protocol); json_set(result, "protocol_version", view_protocol_version); json_set(result, "kind", "composition"); json_set(result, "name", view.name); if(!view.title.empty()) { json_set(result, "title", view.title); } json_set(result, "body", composition_node_to_json(view.body)); return result; } template Json to_composition_manifest_json(const Composition_View& view, const std::vector& contracts, const std::map& schemas) { validate_composition_slot_contracts(view, contracts); Json slots = json_object(); for(const auto& contract : contracts) { const auto schema = schemas.find(contract.name); if(schema == schemas.end()) { throw std::invalid_argument("composition slot schema is not provided: " + contract.name); } Json item = json_object(); json_set(item, "component_kind", contract.component_kind); if(!contract.descriptor_api.empty()) { json_set(item, "descriptor_api", contract.descriptor_api); } if(!contract.view_api.empty()) { json_set(item, "view_api", contract.view_api); } if(!contract.data_api.empty()) { json_set(item, "data_api", contract.data_api); } if(!contract.amis_api.empty()) { json_set(item, "amis_api", contract.amis_api); } json_set(item, "schema", schema->second); json_set(slots, contract.name, std::move(item)); } Json result = json_object(); json_set(result, "protocol", composition_manifest_protocol); json_set(result, "protocol_version", composition_manifest_protocol_version); json_set(result, "view", to_view_json(view)); json_set(result, "slots", std::move(slots)); return result; } inline std::string_view view_node_kind_name(View_Node_Kind value) noexcept { switch(value) { case View_Node_Kind::all_fields: return "all_fields"; case View_Node_Kind::field: return "field"; case View_Node_Kind::frontend: return "frontend"; case View_Node_Kind::vertical: return "vertical"; case View_Node_Kind::horizontal: return "horizontal"; case View_Node_Kind::flow: return "flow"; case View_Node_Kind::grid: return "grid"; case View_Node_Kind::list: return "list"; case View_Node_Kind::group: return "group"; case View_Node_Kind::card: return "card"; case View_Node_Kind::tabs: return "tabs"; case View_Node_Kind::tab: return "tab"; } return "vertical"; } inline std::string_view form_mode_name(Form_Mode value) noexcept { switch(value) { case Form_Mode::display: return "display"; case Form_Mode::edit: return "edit"; case Form_Mode::create: return "create"; } return "display"; } inline std::string_view table_fixed_name(Table_Fixed value) noexcept { switch(value) { case Table_Fixed::none: return "none"; case Table_Fixed::left: return "left"; case Table_Fixed::right: return "right"; } return "none"; } template Json view_node_to_json(const View_Node& node) { Json result = json_object(); json_set(result, "kind", std::string(view_node_kind_name(node.kind))); if(!node.field.empty()) { json_set(result, "field", node.field); } if(!node.title.empty()) { json_set(result, "title", node.title); } if(!node.description.empty()) { json_set(result, "description", node.description); } if(!node.visible_on.empty()) { json_set(result, "visible_on", node.visible_on); } if(node.columns != 0) { json_set(result, "columns", node.columns); } if(node.collapsible) { json_set(result, "collapsible", true); json_set(result, "collapsed", node.collapsed); } if(!node.children.empty()) { Json children = json_array(); for(const auto& child : node.children) { json_append(children, view_node_to_json(child)); } json_set(result, "children", std::move(children)); } return result; } template Json to_view_json(const Form_View& view) { validate_form_view(view); Json result = json_object(); json_set(result, "protocol", view_protocol); json_set(result, "protocol_version", view_protocol_version); json_set(result, "kind", "form"); json_set(result, "name", view.name); json_set(result, "title", view.title); json_set(result, "mode", std::string(form_mode_name(view.mode))); json_set(result, "submit_label", view.submit_label); json_set(result, "affix_footer", view.affix_footer); json_set(result, "body", view_node_to_json(view.body)); return result; } inline std::string_view collection_mode_name(Collection_Mode value) noexcept { switch(value) { case Collection_Mode::table: return "table"; case Collection_Mode::list: return "list"; case Collection_Mode::cards: return "cards"; } return "table"; } template void append_table_view_json(Json& result, const Table_View& view) { json_set(result, "name", view.name); json_set(result, "title", view.title); json_set(result, "id_label", view.id_label); json_set(result, "create_label", view.create_label); json_set(result, "edit_label", view.edit_label); json_set(result, "delete_label", view.delete_label); json_set(result, "actions_label", view.actions_label); json_set(result, "confirm_label", view.confirm_label); json_set(result, "delete_confirm", view.delete_confirm); json_set(result, "view_status_label", view.view_status_label); json_set(result, "default_order_by", view.default_order_by); json_set(result, "default_order_dir", view.default_order_dir); json_set(result, "user_reorderable", view.user_reorderable); json_set(result, "column_reorderable", view.column_reorderable); Json columns = json_array(); for(const auto& column : view.columns) { Json item = json_object(); json_set(item, "field", column.field); if(!column.label.empty()) { json_set(item, "label", column.label); } json_set(item, "sortable", column.sortable); json_set(item, "searchable", column.searchable); json_set(item, "filterable", column.filterable); json_set(item, "fixed", std::string(table_fixed_name(column.fixed))); json_append(columns, std::move(item)); } json_set(result, "columns", std::move(columns)); json_set(result, "create_body", view_node_to_json(view.create_body)); json_set(result, "edit_body", view_node_to_json(view.edit_body)); } template Json to_view_json(const Table_View& view) { validate_table_view(view); Json result = json_object(); json_set(result, "protocol", view_protocol); json_set(result, "protocol_version", view_protocol_version); json_set(result, "kind", "table"); append_table_view_json(result, view); return result; } template Json to_view_json(const Collection_View& view) { validate_collection_view(view); Json result = json_object(); json_set(result, "protocol", view_protocol); json_set(result, "protocol_version", view_protocol_version); json_set(result, "kind", "collection"); json_set(result, "mode", std::string(collection_mode_name(view.mode))); append_table_view_json(result, view.table); if(view.mode != Collection_Mode::table) { Json item = json_object(); json_set(item, "title_field", view.item.title_field); if(!view.item.subtitle_field.empty()) { json_set(item, "subtitle_field", view.item.subtitle_field); } if(!view.item.accent_field.empty()) { json_set(item, "accent_field", view.item.accent_field); } Json fields = json_array(); for(const auto& field : view.item.fields) { json_append(fields, field); } json_set(item, "fields", std::move(fields)); json_set(result, "item", std::move(item)); } if(view.mode == Collection_Mode::cards) { json_set(result, "cards_columns", view.cards_columns); } return result; } }