lcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.claude/skills/lcp-custom-field/SKILL.md +205 -0
- data/.claude/skills/lcp-getting-started/SKILL.md +332 -0
- data/.claude/skills/lcp-host-binding/SKILL.md +287 -0
- data/.claude/skills/lcp-model/SKILL.md +185 -0
- data/.claude/skills/lcp-permissions/SKILL.md +176 -0
- data/.claude/skills/lcp-presenter/SKILL.md +194 -0
- data/.claude/skills/lcp-workflow/SKILL.md +281 -0
- data/CHANGELOG.md +69 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +17 -0
- data/app/assets/javascripts/lcp_ruby/application.js +58 -0
- data/app/assets/javascripts/lcp_ruby/controllers/advanced_filter_controller.js +1521 -0
- data/app/assets/javascripts/lcp_ruby/controllers/approval_actions_controller.js +24 -0
- data/app/assets/javascripts/lcp_ruby/controllers/array_input_controller.js +156 -0
- data/app/assets/javascripts/lcp_ruby/controllers/batch_select_controller.js +405 -0
- data/app/assets/javascripts/lcp_ruby/controllers/cascading_selects_controller.js +436 -0
- data/app/assets/javascripts/lcp_ruby/controllers/char_counter_controller.js +28 -0
- data/app/assets/javascripts/lcp_ruby/controllers/clipboard_controller.js +62 -0
- data/app/assets/javascripts/lcp_ruby/controllers/conditional_rendering_controller.js +178 -0
- data/app/assets/javascripts/lcp_ruby/controllers/confirm_dialog_controller.js +131 -0
- data/app/assets/javascripts/lcp_ruby/controllers/custom_fields_manage_controller.js +80 -0
- data/app/assets/javascripts/lcp_ruby/controllers/dialog_controller.js +421 -0
- data/app/assets/javascripts/lcp_ruby/controllers/dialog_nested_controller.js +182 -0
- data/app/assets/javascripts/lcp_ruby/controllers/direct_upload_controller.js +100 -0
- data/app/assets/javascripts/lcp_ruby/controllers/drawer_controller.js +205 -0
- data/app/assets/javascripts/lcp_ruby/controllers/dropdown_controller.js +160 -0
- data/app/assets/javascripts/lcp_ruby/controllers/export_dialog_controller.js +15 -0
- data/app/assets/javascripts/lcp_ruby/controllers/field_picker_controller.js +290 -0
- data/app/assets/javascripts/lcp_ruby/controllers/file_upload_controller.js +135 -0
- data/app/assets/javascripts/lcp_ruby/controllers/filter_auto_submit_controller.js +46 -0
- data/app/assets/javascripts/lcp_ruby/controllers/filter_dirty_controller.js +94 -0
- data/app/assets/javascripts/lcp_ruby/controllers/form_actions_controller.js +68 -0
- data/app/assets/javascripts/lcp_ruby/controllers/form_handling_controller.js +67 -0
- data/app/assets/javascripts/lcp_ruby/controllers/import_dialog_controller.js +142 -0
- data/app/assets/javascripts/lcp_ruby/controllers/import_mapper_controller.js +322 -0
- data/app/assets/javascripts/lcp_ruby/controllers/index_sortable_controller.js +388 -0
- data/app/assets/javascripts/lcp_ruby/controllers/inline_create_controller.js +137 -0
- data/app/assets/javascripts/lcp_ruby/controllers/kanban_board_controller.js +290 -0
- data/app/assets/javascripts/lcp_ruby/controllers/menu_controller.js +19 -0
- data/app/assets/javascripts/lcp_ruby/controllers/nested_forms_controller.js +261 -0
- data/app/assets/javascripts/lcp_ruby/controllers/responsive_sidebar_controller.js +161 -0
- data/app/assets/javascripts/lcp_ruby/controllers/responsive_top_nav_controller.js +394 -0
- data/app/assets/javascripts/lcp_ruby/controllers/saved_filters_controller.js +129 -0
- data/app/assets/javascripts/lcp_ruby/controllers/search_controller.js +82 -0
- data/app/assets/javascripts/lcp_ruby/controllers/selection_controller.js +33 -0
- data/app/assets/javascripts/lcp_ruby/controllers/sidebar_controller.js +83 -0
- data/app/assets/javascripts/lcp_ruby/controllers/sidebar_toggle_controller.js +66 -0
- data/app/assets/javascripts/lcp_ruby/controllers/slider_controller.js +26 -0
- data/app/assets/javascripts/lcp_ruby/controllers/submenu_controller.js +55 -0
- data/app/assets/javascripts/lcp_ruby/controllers/tom_select_controller.js +168 -0
- data/app/assets/javascripts/lcp_ruby/controllers/tree_index_controller.js +106 -0
- data/app/assets/javascripts/lcp_ruby/controllers/tree_reparent_controller.js +182 -0
- data/app/assets/javascripts/lcp_ruby/controllers/tree_select_controller.js +119 -0
- data/app/assets/javascripts/lcp_ruby/controllers/ui_components_controller.js +135 -0
- data/app/assets/javascripts/lcp_ruby/controllers/zone_tabs_controller.js +66 -0
- data/app/assets/javascripts/lcp_ruby/dev_toolbar.js +494 -0
- data/app/assets/javascripts/lcp_ruby/i18n.js.erb +29 -0
- data/app/assets/javascripts/lcp_ruby/lucide_init.js +24 -0
- data/app/assets/javascripts/lcp_ruby/stimulus_bootstrap.js +7 -0
- data/app/assets/javascripts/lcp_ruby/utils.js +119 -0
- data/app/assets/javascripts/lcp_ruby/xhr_fetch.js +411 -0
- data/app/assets/stylesheets/lcp_ruby/application.css +1940 -0
- data/app/assets/stylesheets/lcp_ruby/dev_toolbar.css +355 -0
- data/app/controllers/concerns/lcp_ruby/dialog_rendering.rb +167 -0
- data/app/controllers/concerns/lcp_ruby/form_action_execution.rb +249 -0
- data/app/controllers/concerns/lcp_ruby/page_authorization.rb +105 -0
- data/app/controllers/concerns/lcp_ruby/zone_resolution.rb +199 -0
- data/app/controllers/lcp_ruby/actions_controller.rb +481 -0
- data/app/controllers/lcp_ruby/application_controller.rb +59 -0
- data/app/controllers/lcp_ruby/approval_tasks_controller.rb +98 -0
- data/app/controllers/lcp_ruby/auth/base_controller.rb +58 -0
- data/app/controllers/lcp_ruby/auth/callbacks_controller.rb +103 -0
- data/app/controllers/lcp_ruby/auth/passwords_controller.rb +15 -0
- data/app/controllers/lcp_ruby/auth/registrations_controller.rb +37 -0
- data/app/controllers/lcp_ruby/auth/sessions_controller.rb +112 -0
- data/app/controllers/lcp_ruby/custom_fields_controller.rb +370 -0
- data/app/controllers/lcp_ruby/dev_toolbar_controller.rb +197 -0
- data/app/controllers/lcp_ruby/dialogs_controller.rb +199 -0
- data/app/controllers/lcp_ruby/health_controller.rb +23 -0
- data/app/controllers/lcp_ruby/impersonation_controller.rb +32 -0
- data/app/controllers/lcp_ruby/landing_controller.rb +49 -0
- data/app/controllers/lcp_ruby/metrics_controller.rb +17 -0
- data/app/controllers/lcp_ruby/resources_controller.rb +2218 -0
- data/app/controllers/lcp_ruby/saved_filters_controller.rb +221 -0
- data/app/helpers/lcp_ruby/condition_helper.rb +87 -0
- data/app/helpers/lcp_ruby/dashboard_helper.rb +45 -0
- data/app/helpers/lcp_ruby/dev_toolbar_helper.rb +17 -0
- data/app/helpers/lcp_ruby/dialog_helper.rb +27 -0
- data/app/helpers/lcp_ruby/display/card_helper.rb +317 -0
- data/app/helpers/lcp_ruby/display_helper.rb +63 -0
- data/app/helpers/lcp_ruby/display_template_helper.rb +100 -0
- data/app/helpers/lcp_ruby/form_helper.rb +1020 -0
- data/app/helpers/lcp_ruby/grouped_query_helper.rb +107 -0
- data/app/helpers/lcp_ruby/i18n_payload_helper.rb +197 -0
- data/app/helpers/lcp_ruby/layout_helper.rb +832 -0
- data/app/helpers/lcp_ruby/link_through_helper.rb +29 -0
- data/app/helpers/lcp_ruby/oidc_button_helper.rb +59 -0
- data/app/helpers/lcp_ruby/search_helper.rb +31 -0
- data/app/helpers/lcp_ruby/tree_helper.rb +140 -0
- data/app/helpers/lcp_ruby/view_slot_helper.rb +33 -0
- data/app/models/lcp_ruby/user.rb +61 -0
- data/app/views/layouts/lcp_ruby/application.html.erb +168 -0
- data/app/views/layouts/lcp_ruby/auth.html.erb +170 -0
- data/app/views/lcp_ruby/auth/callbacks/failure.html.erb +18 -0
- data/app/views/lcp_ruby/auth/mailer/reset_password_instructions.html.erb +9 -0
- data/app/views/lcp_ruby/auth/passwords/edit.html.erb +34 -0
- data/app/views/lcp_ruby/auth/passwords/new.html.erb +24 -0
- data/app/views/lcp_ruby/auth/registrations/edit.html.erb +48 -0
- data/app/views/lcp_ruby/auth/registrations/new.html.erb +46 -0
- data/app/views/lcp_ruby/auth/sessions/new.html.erb +55 -0
- data/app/views/lcp_ruby/auth/shared/_links.html.erb +13 -0
- data/app/views/lcp_ruby/custom_fields/_manage_row.html.erb +82 -0
- data/app/views/lcp_ruby/custom_fields/manage.html.erb +47 -0
- data/app/views/lcp_ruby/dialogs/_dialog_composite_frame.html.erb +87 -0
- data/app/views/lcp_ruby/dialogs/_dialog_frame.html.erb +60 -0
- data/app/views/lcp_ruby/dialogs/_dialog_show_frame.html.erb +14 -0
- data/app/views/lcp_ruby/dialogs/_show_secret.html.erb +10 -0
- data/app/views/lcp_ruby/dialogs/_success.html.erb +1 -0
- data/app/views/lcp_ruby/errors/not_found.html.erb +5 -0
- data/app/views/lcp_ruby/menu_renderers/_user_menu.html.erb +51 -0
- data/app/views/lcp_ruby/menu_renderers/_user_menu_panel.html.erb +32 -0
- data/app/views/lcp_ruby/navigation/_auto_top.html.erb +16 -0
- data/app/views/lcp_ruby/navigation/_both_sidebar.html.erb +1 -0
- data/app/views/lcp_ruby/navigation/_both_top.html.erb +1 -0
- data/app/views/lcp_ruby/navigation/_impersonation_banner.html.erb +14 -0
- data/app/views/lcp_ruby/navigation/_item_content.html.erb +23 -0
- data/app/views/lcp_ruby/navigation/_link_or_button.html.erb +51 -0
- data/app/views/lcp_ruby/navigation/_mobile_header.html.erb +23 -0
- data/app/views/lcp_ruby/navigation/_panel_item.html.erb +32 -0
- data/app/views/lcp_ruby/navigation/_sidebar.html.erb +38 -0
- data/app/views/lcp_ruby/navigation/_sidebar_item.html.erb +38 -0
- data/app/views/lcp_ruby/navigation/_sidebar_toggle.html.erb +32 -0
- data/app/views/lcp_ruby/navigation/_top.html.erb +26 -0
- data/app/views/lcp_ruby/navigation/_top_item.html.erb +112 -0
- data/app/views/lcp_ruby/navigation/_widget_item.html.erb +14 -0
- data/app/views/lcp_ruby/resources/_action_button.html.erb +138 -0
- data/app/views/lcp_ruby/resources/_advanced_filter.html.erb +55 -0
- data/app/views/lcp_ruby/resources/_api_status_banner.html.erb +11 -0
- data/app/views/lcp_ruby/resources/_association_list.html.erb +132 -0
- data/app/views/lcp_ruby/resources/_audit_history.html.erb +66 -0
- data/app/views/lcp_ruby/resources/_batch_toolbar.html.erb +54 -0
- data/app/views/lcp_ruby/resources/_filter_form.html.erb +57 -0
- data/app/views/lcp_ruby/resources/_form.html.erb +73 -0
- data/app/views/lcp_ruby/resources/_form_action_button.html.erb +20 -0
- data/app/views/lcp_ruby/resources/_form_action_dropdown_item.html.erb +17 -0
- data/app/views/lcp_ruby/resources/_form_actions.html.erb +38 -0
- data/app/views/lcp_ruby/resources/_form_section.html.erb +235 -0
- data/app/views/lcp_ruby/resources/_grid_page.html.erb +17 -0
- data/app/views/lcp_ruby/resources/_grouped_index.html.erb +61 -0
- data/app/views/lcp_ruby/resources/_inline_create_form.html.erb +50 -0
- data/app/views/lcp_ruby/resources/_json_items_list.html.erb +77 -0
- data/app/views/lcp_ruby/resources/_json_nested_fields.html.erb +30 -0
- data/app/views/lcp_ruby/resources/_kanban_card.html.erb +31 -0
- data/app/views/lcp_ruby/resources/_kanban_card_body.html.erb +17 -0
- data/app/views/lcp_ruby/resources/_kanban_column.html.erb +33 -0
- data/app/views/lcp_ruby/resources/_kanban_column_header.html.erb +10 -0
- data/app/views/lcp_ruby/resources/_kanban_index.html.erb +17 -0
- data/app/views/lcp_ruby/resources/_nested_field_cell.html.erb +33 -0
- data/app/views/lcp_ruby/resources/_nested_fields.html.erb +26 -0
- data/app/views/lcp_ruby/resources/_nested_row_content.html.erb +65 -0
- data/app/views/lcp_ruby/resources/_scope_filters.html.erb +71 -0
- data/app/views/lcp_ruby/resources/_semantic_index_page.html.erb +162 -0
- data/app/views/lcp_ruby/resources/_semantic_page.html.erb +121 -0
- data/app/views/lcp_ruby/resources/_show_sections.html.erb +128 -0
- data/app/views/lcp_ruby/resources/_table_index.html.erb +169 -0
- data/app/views/lcp_ruby/resources/_tile_card.html.erb +26 -0
- data/app/views/lcp_ruby/resources/_tile_card_body.html.erb +19 -0
- data/app/views/lcp_ruby/resources/_tiles_index.html.erb +15 -0
- data/app/views/lcp_ruby/resources/_transition_button.html.erb +33 -0
- data/app/views/lcp_ruby/resources/_tree_index.html.erb +61 -0
- data/app/views/lcp_ruby/resources/_view_switcher.html.erb +24 -0
- data/app/views/lcp_ruby/resources/edit.html.erb +9 -0
- data/app/views/lcp_ruby/resources/index.html.erb +80 -0
- data/app/views/lcp_ruby/resources/new.html.erb +9 -0
- data/app/views/lcp_ruby/resources/show.html.erb +37 -0
- data/app/views/lcp_ruby/shared/_breadcrumbs.html.erb +15 -0
- data/app/views/lcp_ruby/shared/_custom_partial_error.html.erb +3 -0
- data/app/views/lcp_ruby/shared/_flash_messages.html.erb +19 -0
- data/app/views/lcp_ruby/slots/index/_advanced_filter.html.erb +3 -0
- data/app/views/lcp_ruby/slots/index/_collection_actions.html.erb +50 -0
- data/app/views/lcp_ruby/slots/index/_manage_all.html.erb +4 -0
- data/app/views/lcp_ruby/slots/index/_pagination_footer.html.erb +71 -0
- data/app/views/lcp_ruby/slots/index/_predefined_filters.html.erb +8 -0
- data/app/views/lcp_ruby/slots/index/_saved_filters.html.erb +87 -0
- data/app/views/lcp_ruby/slots/index/_search.html.erb +52 -0
- data/app/views/lcp_ruby/slots/index/_search_parameter.html.erb +72 -0
- data/app/views/lcp_ruby/slots/index/_sort_dropdown.html.erb +21 -0
- data/app/views/lcp_ruby/slots/index/_summary_bar.html.erb +22 -0
- data/app/views/lcp_ruby/slots/index/_view_switcher.html.erb +1 -0
- data/app/views/lcp_ruby/slots/show/_approval_status.html.erb +8 -0
- data/app/views/lcp_ruby/slots/show/_back_to_list.html.erb +1 -0
- data/app/views/lcp_ruby/slots/show/_copy_url.html.erb +5 -0
- data/app/views/lcp_ruby/slots/show/_single_actions.html.erb +4 -0
- data/app/views/lcp_ruby/slots/show/_view_switcher.html.erb +1 -0
- data/app/views/lcp_ruby/widgets/_approval_status.html.erb +150 -0
- data/app/views/lcp_ruby/widgets/_chart.html.erb +36 -0
- data/app/views/lcp_ruby/widgets/_embed.html.erb +13 -0
- data/app/views/lcp_ruby/widgets/_kpi_card.html.erb +29 -0
- data/app/views/lcp_ruby/widgets/_list.html.erb +17 -0
- data/app/views/lcp_ruby/widgets/_presenter_zone.html.erb +84 -0
- data/app/views/lcp_ruby/widgets/_record_show_zone.html.erb +11 -0
- data/app/views/lcp_ruby/widgets/_text.html.erb +3 -0
- data/app/views/lcp_ruby/widgets/_workflow_graph.html.erb +32 -0
- data/app/views/lcp_ruby/zones/_custom_zone.html.erb +16 -0
- data/app/views/lcp_ruby/zones/_error.html.erb +5 -0
- data/app/views/lcp_ruby/zones/_zone_frame.html.erb +25 -0
- data/app/views/lcp_ruby/zones/_zone_search.html.erb +20 -0
- data/config/locales/cs.yml +859 -0
- data/config/locales/en.yml +731 -0
- data/config/routes.rb +119 -0
- data/docs/README.md +225 -0
- data/docs/architecture.md +212 -0
- data/docs/feature-catalog.md +763 -0
- data/docs/feature-catalog.yml +20911 -0
- data/docs/getting-started.md +1187 -0
- data/docs/guides/action-buttons.md +537 -0
- data/docs/guides/adding-locale.md +353 -0
- data/docs/guides/api-backed-models.md +478 -0
- data/docs/guides/attachments.md +399 -0
- data/docs/guides/auditing.md +333 -0
- data/docs/guides/batch-actions.md +342 -0
- data/docs/guides/composite-pages.md +1290 -0
- data/docs/guides/computed-fields.md +350 -0
- data/docs/guides/conditional-rendering.md +832 -0
- data/docs/guides/custom-actions.md +238 -0
- data/docs/guides/custom-fields.md +439 -0
- data/docs/guides/custom-renderers.md +234 -0
- data/docs/guides/custom-types.md +274 -0
- data/docs/guides/dashboards.md +504 -0
- data/docs/guides/debugging/README.md +38 -0
- data/docs/guides/debugging/controllers.md +147 -0
- data/docs/guides/debugging/data.md +165 -0
- data/docs/guides/debugging/metadata.md +143 -0
- data/docs/guides/debugging/models.md +126 -0
- data/docs/guides/debugging/permissions.md +147 -0
- data/docs/guides/debugging/presenters.md +151 -0
- data/docs/guides/developer-tools.md +418 -0
- data/docs/guides/dialogs.md +392 -0
- data/docs/guides/display-types.md +1430 -0
- data/docs/guides/eager-loading.md +230 -0
- data/docs/guides/event-handlers.md +135 -0
- data/docs/guides/export.md +305 -0
- data/docs/guides/extensibility.md +761 -0
- data/docs/guides/groups.md +220 -0
- data/docs/guides/hierarchical-authorization.md +427 -0
- data/docs/guides/host-application.md +556 -0
- data/docs/guides/host-controller-integration.md +473 -0
- data/docs/guides/impersonation.md +83 -0
- data/docs/guides/import.md +165 -0
- data/docs/guides/inherited-permissions.md +459 -0
- data/docs/guides/menu.md +373 -0
- data/docs/guides/monitoring.md +254 -0
- data/docs/guides/oidc-setup.md +399 -0
- data/docs/guides/permission-source.md +205 -0
- data/docs/guides/permissions.md +364 -0
- data/docs/guides/presenters.md +2324 -0
- data/docs/guides/record-aliases.md +303 -0
- data/docs/guides/rendering-extension-points.md +280 -0
- data/docs/guides/role-source.md +288 -0
- data/docs/guides/selectbox.md +516 -0
- data/docs/guides/sequences.md +291 -0
- data/docs/guides/soft-delete.md +460 -0
- data/docs/guides/theming.md +129 -0
- data/docs/guides/tiles.md +383 -0
- data/docs/guides/tree-structures.md +297 -0
- data/docs/guides/userstamps.md +288 -0
- data/docs/guides/view-groups.md +259 -0
- data/docs/guides/view-slots.md +352 -0
- data/docs/guides/virtual-columns.md +810 -0
- data/docs/guides/workflow.md +692 -0
- data/docs/reference/api-backed-models.md +404 -0
- data/docs/reference/api-tokens.md +128 -0
- data/docs/reference/auditing.md +277 -0
- data/docs/reference/boot_lifecycle.md +188 -0
- data/docs/reference/cascading_selects.md +189 -0
- data/docs/reference/condition-operators.md +445 -0
- data/docs/reference/custom-fields.md +483 -0
- data/docs/reference/dialogs.md +286 -0
- data/docs/reference/doctor.md +168 -0
- data/docs/reference/dynamic-references.md +95 -0
- data/docs/reference/eager-loading.md +192 -0
- data/docs/reference/engine-configuration.md +989 -0
- data/docs/reference/export.md +309 -0
- data/docs/reference/forms.md +68 -0
- data/docs/reference/groups.md +176 -0
- data/docs/reference/host-controller-integration.md +342 -0
- data/docs/reference/i18n.md +497 -0
- data/docs/reference/i18n_check.md +351 -0
- data/docs/reference/import.md +260 -0
- data/docs/reference/invariant_check.md +216 -0
- data/docs/reference/menu.md +985 -0
- data/docs/reference/model-dsl.md +1157 -0
- data/docs/reference/models.md +2972 -0
- data/docs/reference/monitoring.md +222 -0
- data/docs/reference/oidc-bearer.md +269 -0
- data/docs/reference/oidc.md +407 -0
- data/docs/reference/page_filters.md +328 -0
- data/docs/reference/pages.md +1375 -0
- data/docs/reference/permission-source.md +185 -0
- data/docs/reference/permissions.md +715 -0
- data/docs/reference/presenter-dsl.md +1719 -0
- data/docs/reference/presenters.md +3627 -0
- data/docs/reference/role-source.md +227 -0
- data/docs/reference/theme-variables.md +139 -0
- data/docs/reference/tree-structures.md +374 -0
- data/docs/reference/types.md +470 -0
- data/docs/reference/view-groups.md +347 -0
- data/docs/reference/view-slots.md +228 -0
- data/docs/reference/virtual_forms.md +196 -0
- data/docs/reference/workflow-approvals.md +387 -0
- data/docs/reference/workflow.md +651 -0
- data/examples/crm/Gemfile +9 -0
- data/examples/crm/Gemfile.lock +417 -0
- data/examples/crm/Rakefile +2 -0
- data/examples/crm/app/actions/activity/complete.rb +20 -0
- data/examples/crm/app/actions/deal/close_won.rb +20 -0
- data/examples/crm/app/assets/config/manifest.js +3 -0
- data/examples/crm/app/controllers/application_controller.rb +9 -0
- data/examples/crm/app/event_handlers/deal/on_stage_change.rb +17 -0
- data/examples/crm/app/lcp_services/computed/weighted_deal_value.rb +13 -0
- data/examples/crm/app/lcp_services/data_providers/active_contacts_count.rb +14 -0
- data/examples/crm/app/lcp_services/data_providers/open_deals_count.rb +14 -0
- data/examples/crm/app/lcp_services/data_providers/pending_activities_count.rb +15 -0
- data/examples/crm/app/lcp_services/data_providers/pipeline_value.rb +27 -0
- data/examples/crm/app/lcp_services/data_providers/won_deals_count.rb +14 -0
- data/examples/crm/app/lcp_services/defaults/thirty_days_out.rb +11 -0
- data/examples/crm/app/lcp_services/transforms/titlecase.rb +11 -0
- data/examples/crm/app/lcp_services/validators/deal_credit_limit.rb +19 -0
- data/examples/crm/app/lcp_services/validators/deal_documents_required.rb +17 -0
- data/examples/crm/app/renderers/conditional_badge.rb +39 -0
- data/examples/crm/bin/rails +4 -0
- data/examples/crm/bin/rake +4 -0
- data/examples/crm/config/application.rb +31 -0
- data/examples/crm/config/boot.rb +2 -0
- data/examples/crm/config/database.yml +12 -0
- data/examples/crm/config/environment.rb +2 -0
- data/examples/crm/config/initializers/lcp_ruby.rb +45 -0
- data/examples/crm/config/lcp_ruby/menu.yml +53 -0
- data/examples/crm/config/lcp_ruby/models/activity.rb +43 -0
- data/examples/crm/config/lcp_ruby/models/city.rb +19 -0
- data/examples/crm/config/lcp_ruby/models/company.rb +65 -0
- data/examples/crm/config/lcp_ruby/models/contact.rb +62 -0
- data/examples/crm/config/lcp_ruby/models/country.rb +22 -0
- data/examples/crm/config/lcp_ruby/models/custom_field_definition.rb +60 -0
- data/examples/crm/config/lcp_ruby/models/deal.rb +85 -0
- data/examples/crm/config/lcp_ruby/models/deal_category.rb +15 -0
- data/examples/crm/config/lcp_ruby/models/gapfree_sequence.rb +17 -0
- data/examples/crm/config/lcp_ruby/models/region.rb +15 -0
- data/examples/crm/config/lcp_ruby/models/saved_filter.rb +50 -0
- data/examples/crm/config/lcp_ruby/pages/activity_quick_log.yml +19 -0
- data/examples/crm/config/lcp_ruby/pages/company_detail.yml +127 -0
- data/examples/crm/config/lcp_ruby/pages/deals_overview.yml +65 -0
- data/examples/crm/config/lcp_ruby/permissions/activity.yml +40 -0
- data/examples/crm/config/lcp_ruby/permissions/custom_field_definition.yml +16 -0
- data/examples/crm/config/lcp_ruby/permissions/deal.yml +54 -0
- data/examples/crm/config/lcp_ruby/permissions/default.yml +16 -0
- data/examples/crm/config/lcp_ruby/permissions/gapfree_sequence.yml +6 -0
- data/examples/crm/config/lcp_ruby/permissions/saved_filter.yml +27 -0
- data/examples/crm/config/lcp_ruby/presenters/activity.rb +140 -0
- data/examples/crm/config/lcp_ruby/presenters/activity_quick_form.rb +36 -0
- data/examples/crm/config/lcp_ruby/presenters/activity_short.rb +16 -0
- data/examples/crm/config/lcp_ruby/presenters/activity_tiles.rb +35 -0
- data/examples/crm/config/lcp_ruby/presenters/city.rb +53 -0
- data/examples/crm/config/lcp_ruby/presenters/company.rb +147 -0
- data/examples/crm/config/lcp_ruby/presenters/company_activities_zone.rb +26 -0
- data/examples/crm/config/lcp_ruby/presenters/company_archive.rb +37 -0
- data/examples/crm/config/lcp_ruby/presenters/company_contacts_zone.rb +22 -0
- data/examples/crm/config/lcp_ruby/presenters/company_deals_zone.rb +36 -0
- data/examples/crm/config/lcp_ruby/presenters/company_short.rb +21 -0
- data/examples/crm/config/lcp_ruby/presenters/company_show_zone.rb +38 -0
- data/examples/crm/config/lcp_ruby/presenters/company_sidebar.rb +15 -0
- data/examples/crm/config/lcp_ruby/presenters/company_tiles.rb +35 -0
- data/examples/crm/config/lcp_ruby/presenters/contact.rb +120 -0
- data/examples/crm/config/lcp_ruby/presenters/contact_quick_form.rb +17 -0
- data/examples/crm/config/lcp_ruby/presenters/contact_short.rb +20 -0
- data/examples/crm/config/lcp_ruby/presenters/contact_tiles.rb +34 -0
- data/examples/crm/config/lcp_ruby/presenters/country.rb +44 -0
- data/examples/crm/config/lcp_ruby/presenters/custom_fields.rb +124 -0
- data/examples/crm/config/lcp_ruby/presenters/deal.rb +181 -0
- data/examples/crm/config/lcp_ruby/presenters/deal_category.rb +46 -0
- data/examples/crm/config/lcp_ruby/presenters/deal_overview.rb +6 -0
- data/examples/crm/config/lcp_ruby/presenters/deal_pipeline.rb +18 -0
- data/examples/crm/config/lcp_ruby/presenters/deal_short.rb +25 -0
- data/examples/crm/config/lcp_ruby/presenters/deal_tiles.rb +48 -0
- data/examples/crm/config/lcp_ruby/presenters/region.rb +42 -0
- data/examples/crm/config/lcp_ruby/presenters/save_filter_dialog.rb +17 -0
- data/examples/crm/config/lcp_ruby/presenters/saved_filters.rb +93 -0
- data/examples/crm/config/lcp_ruby/views/activities.yml +16 -0
- data/examples/crm/config/lcp_ruby/views/cities.yml +10 -0
- data/examples/crm/config/lcp_ruby/views/companies.yml +14 -0
- data/examples/crm/config/lcp_ruby/views/contacts.yml +16 -0
- data/examples/crm/config/lcp_ruby/views/countries.yml +8 -0
- data/examples/crm/config/lcp_ruby/views/custom_fields.rb +9 -0
- data/examples/crm/config/lcp_ruby/views/deal_categories.yml +8 -0
- data/examples/crm/config/lcp_ruby/views/deals.yml +19 -0
- data/examples/crm/config/lcp_ruby/views/pipeline.yml +10 -0
- data/examples/crm/config/lcp_ruby/views/regions.yml +10 -0
- data/examples/crm/config/lcp_ruby/views/saved_filters.yml +7 -0
- data/examples/crm/config/locales/cs.yml +338 -0
- data/examples/crm/config/locales/en.yml +353 -0
- data/examples/crm/config/routes.rb +4 -0
- data/examples/crm/config/storage.yml +3 -0
- data/examples/crm/config.ru +2 -0
- data/examples/crm/db/migrate/20260219104942_create_active_storage_tables.active_storage.rb +57 -0
- data/examples/crm/db/schema.rb +245 -0
- data/examples/crm/db/seeds.rb +1111 -0
- data/examples/crm/erd.md +163 -0
- data/examples/hr/Gemfile +9 -0
- data/examples/hr/Gemfile.lock +419 -0
- data/examples/hr/Rakefile +6 -0
- data/examples/hr/app/actions/asset/assign_asset.rb +20 -0
- data/examples/hr/app/actions/asset/return_asset.rb +20 -0
- data/examples/hr/app/actions/candidate/advance.rb +32 -0
- data/examples/hr/app/actions/candidate/hire.rb +20 -0
- data/examples/hr/app/actions/candidate/reject_candidate.rb +20 -0
- data/examples/hr/app/actions/expense_claim/approve.rb +21 -0
- data/examples/hr/app/actions/expense_claim/reject.rb +21 -0
- data/examples/hr/app/actions/expense_claim/submit.rb +20 -0
- data/examples/hr/app/actions/interview/complete_interview.rb +20 -0
- data/examples/hr/app/actions/leave_request/approve.rb +21 -0
- data/examples/hr/app/actions/leave_request/cancel.rb +20 -0
- data/examples/hr/app/actions/leave_request/reject.rb +21 -0
- data/examples/hr/app/assets/config/manifest.js +1 -0
- data/examples/hr/app/assets/stylesheets/application.css +10 -0
- data/examples/hr/app/condition_services/is_own_org_unit.rb +14 -0
- data/examples/hr/app/condition_services/is_own_record.rb +20 -0
- data/examples/hr/app/controllers/application_controller.rb +15 -0
- data/examples/hr/app/event_handlers/asset_assignment/on_create.rb +24 -0
- data/examples/hr/app/event_handlers/candidate/on_status_change.rb +18 -0
- data/examples/hr/app/event_handlers/leave_request/on_status_change.rb +45 -0
- data/examples/hr/app/helpers/application_helper.rb +2 -0
- data/examples/hr/app/javascript/application.js +1 -0
- data/examples/hr/app/jobs/application_job.rb +7 -0
- data/examples/hr/app/lcp_services/computed/employee_tenure.rb +28 -0
- data/examples/hr/app/lcp_services/computed/leave_remaining.rb +13 -0
- data/examples/hr/app/lcp_services/data_providers/headcount_text.rb +14 -0
- data/examples/hr/app/lcp_services/data_providers/open_positions_count.rb +14 -0
- data/examples/hr/app/lcp_services/data_providers/pending_expenses_count.rb +14 -0
- data/examples/hr/app/lcp_services/data_providers/pending_leaves_count.rb +14 -0
- data/examples/hr/app/lcp_services/defaults/current_year.rb +11 -0
- data/examples/hr/app/lcp_services/transforms/titlecase.rb +11 -0
- data/examples/hr/app/lcp_services/validators/expense_receipt_required.rb +17 -0
- data/examples/hr/app/lcp_services/validators/leave_balance_check.rb +37 -0
- data/examples/hr/app/models/application_record.rb +3 -0
- data/examples/hr/app/views/layouts/application.html.erb +29 -0
- data/examples/hr/app/views/pwa/manifest.json.erb +22 -0
- data/examples/hr/app/views/pwa/service-worker.js +26 -0
- data/examples/hr/bin/brakeman +7 -0
- data/examples/hr/bin/bundler-audit +6 -0
- data/examples/hr/bin/ci +6 -0
- data/examples/hr/bin/dev +2 -0
- data/examples/hr/bin/docker-entrypoint +8 -0
- data/examples/hr/bin/importmap +4 -0
- data/examples/hr/bin/jobs +6 -0
- data/examples/hr/bin/kamal +27 -0
- data/examples/hr/bin/rails +4 -0
- data/examples/hr/bin/rake +4 -0
- data/examples/hr/bin/rubocop +8 -0
- data/examples/hr/bin/setup +35 -0
- data/examples/hr/bin/thrust +5 -0
- data/examples/hr/config/application.rb +31 -0
- data/examples/hr/config/boot.rb +2 -0
- data/examples/hr/config/bundler-audit.yml +5 -0
- data/examples/hr/config/cache.yml +16 -0
- data/examples/hr/config/ci.rb +20 -0
- data/examples/hr/config/credentials.yml.enc +1 -0
- data/examples/hr/config/database.yml +36 -0
- data/examples/hr/config/deploy.yml +119 -0
- data/examples/hr/config/environment.rb +5 -0
- data/examples/hr/config/environments/development.rb +66 -0
- data/examples/hr/config/environments/production.rb +74 -0
- data/examples/hr/config/environments/test.rb +45 -0
- data/examples/hr/config/importmap.rb +3 -0
- data/examples/hr/config/initializers/assets.rb +7 -0
- data/examples/hr/config/initializers/content_security_policy.rb +29 -0
- data/examples/hr/config/initializers/filter_parameter_logging.rb +8 -0
- data/examples/hr/config/initializers/inflections.rb +16 -0
- data/examples/hr/config/initializers/lcp_ruby.rb +18 -0
- data/examples/hr/config/lcp_ruby/menu.yml +86 -0
- data/examples/hr/config/lcp_ruby/models/announcement.rb +33 -0
- data/examples/hr/config/lcp_ruby/models/asset.rb +73 -0
- data/examples/hr/config/lcp_ruby/models/asset_assignment.rb +39 -0
- data/examples/hr/config/lcp_ruby/models/audit_log.yml +57 -0
- data/examples/hr/config/lcp_ruby/models/candidate.rb +68 -0
- data/examples/hr/config/lcp_ruby/models/custom_field_definition.rb +60 -0
- data/examples/hr/config/lcp_ruby/models/dashboard.rb +17 -0
- data/examples/hr/config/lcp_ruby/models/document.rb +38 -0
- data/examples/hr/config/lcp_ruby/models/employee.rb +128 -0
- data/examples/hr/config/lcp_ruby/models/employee_skill.rb +29 -0
- data/examples/hr/config/lcp_ruby/models/expense_claim.rb +70 -0
- data/examples/hr/config/lcp_ruby/models/goal.rb +48 -0
- data/examples/hr/config/lcp_ruby/models/group.rb +37 -0
- data/examples/hr/config/lcp_ruby/models/group_membership.rb +26 -0
- data/examples/hr/config/lcp_ruby/models/interview.rb +54 -0
- data/examples/hr/config/lcp_ruby/models/job_posting.rb +67 -0
- data/examples/hr/config/lcp_ruby/models/leave_balance.rb +31 -0
- data/examples/hr/config/lcp_ruby/models/leave_request.rb +52 -0
- data/examples/hr/config/lcp_ruby/models/leave_type.rb +35 -0
- data/examples/hr/config/lcp_ruby/models/organization_unit.rb +42 -0
- data/examples/hr/config/lcp_ruby/models/performance_review.rb +59 -0
- data/examples/hr/config/lcp_ruby/models/position.rb +43 -0
- data/examples/hr/config/lcp_ruby/models/skill.rb +27 -0
- data/examples/hr/config/lcp_ruby/models/training_course.rb +53 -0
- data/examples/hr/config/lcp_ruby/models/training_enrollment.rb +35 -0
- data/examples/hr/config/lcp_ruby/pages/dashboard.yml +101 -0
- data/examples/hr/config/lcp_ruby/permissions/announcement.yml +25 -0
- data/examples/hr/config/lcp_ruby/permissions/asset.yml +35 -0
- data/examples/hr/config/lcp_ruby/permissions/audit_log.yml +28 -0
- data/examples/hr/config/lcp_ruby/permissions/candidate.yml +25 -0
- data/examples/hr/config/lcp_ruby/permissions/custom_field_definition.yml +28 -0
- data/examples/hr/config/lcp_ruby/permissions/dashboard.yml +25 -0
- data/examples/hr/config/lcp_ruby/permissions/default.yml +25 -0
- data/examples/hr/config/lcp_ruby/permissions/document.yml +37 -0
- data/examples/hr/config/lcp_ruby/permissions/employee.yml +55 -0
- data/examples/hr/config/lcp_ruby/permissions/expense_claim.yml +45 -0
- data/examples/hr/config/lcp_ruby/permissions/group.yml +27 -0
- data/examples/hr/config/lcp_ruby/permissions/job_posting.yml +34 -0
- data/examples/hr/config/lcp_ruby/permissions/leave_request.yml +45 -0
- data/examples/hr/config/lcp_ruby/permissions/performance_review.yml +42 -0
- data/examples/hr/config/lcp_ruby/presenters/announcement.rb +47 -0
- data/examples/hr/config/lcp_ruby/presenters/asset.rb +69 -0
- data/examples/hr/config/lcp_ruby/presenters/asset_assignment.rb +47 -0
- data/examples/hr/config/lcp_ruby/presenters/audit_logs.yml +43 -0
- data/examples/hr/config/lcp_ruby/presenters/candidate.rb +71 -0
- data/examples/hr/config/lcp_ruby/presenters/custom_fields.rb +124 -0
- data/examples/hr/config/lcp_ruby/presenters/dashboard.rb +37 -0
- data/examples/hr/config/lcp_ruby/presenters/document.rb +46 -0
- data/examples/hr/config/lcp_ruby/presenters/employee.rb +167 -0
- data/examples/hr/config/lcp_ruby/presenters/employee_archive.rb +38 -0
- data/examples/hr/config/lcp_ruby/presenters/employee_directory.rb +27 -0
- data/examples/hr/config/lcp_ruby/presenters/employee_skill.rb +57 -0
- data/examples/hr/config/lcp_ruby/presenters/expense_claim.rb +76 -0
- data/examples/hr/config/lcp_ruby/presenters/goal.rb +60 -0
- data/examples/hr/config/lcp_ruby/presenters/group.rb +48 -0
- data/examples/hr/config/lcp_ruby/presenters/interview.rb +59 -0
- data/examples/hr/config/lcp_ruby/presenters/job_posting.rb +73 -0
- data/examples/hr/config/lcp_ruby/presenters/leave_balance.rb +50 -0
- data/examples/hr/config/lcp_ruby/presenters/leave_request.rb +89 -0
- data/examples/hr/config/lcp_ruby/presenters/leave_type.rb +52 -0
- data/examples/hr/config/lcp_ruby/presenters/organization_unit.rb +56 -0
- data/examples/hr/config/lcp_ruby/presenters/performance_review.rb +87 -0
- data/examples/hr/config/lcp_ruby/presenters/position.rb +54 -0
- data/examples/hr/config/lcp_ruby/presenters/skill.rb +45 -0
- data/examples/hr/config/lcp_ruby/presenters/training_course.rb +61 -0
- data/examples/hr/config/lcp_ruby/presenters/training_enrollment.rb +49 -0
- data/examples/hr/config/lcp_ruby/views/announcements.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/asset_assignments.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/assets.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/audit_logs.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/candidates.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/custom_fields.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/dashboard.yml +6 -0
- data/examples/hr/config/lcp_ruby/views/documents.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/employee_skills.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/employees.yml +14 -0
- data/examples/hr/config/lcp_ruby/views/expense_claims.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/goals.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/groups.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/interviews.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/job_postings.yml +6 -0
- data/examples/hr/config/lcp_ruby/views/leave_balances.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/leave_requests.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/leave_types.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/organization_units.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/performance_reviews.yml +10 -0
- data/examples/hr/config/lcp_ruby/views/positions.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/skills.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/training_courses.yml +8 -0
- data/examples/hr/config/lcp_ruby/views/training_enrollments.yml +10 -0
- data/examples/hr/config/locales/cs.yml +496 -0
- data/examples/hr/config/locales/en.yml +740 -0
- data/examples/hr/config/locales/sk.yml +496 -0
- data/examples/hr/config/puma.rb +42 -0
- data/examples/hr/config/queue.yml +18 -0
- data/examples/hr/config/recurring.yml +15 -0
- data/examples/hr/config/routes.rb +4 -0
- data/examples/hr/config/storage.yml +27 -0
- data/examples/hr/config.ru +6 -0
- data/examples/hr/db/cache_schema.rb +12 -0
- data/examples/hr/db/migrate/20260303202825_create_active_storage_tables.active_storage.rb +57 -0
- data/examples/hr/db/queue_schema.rb +129 -0
- data/examples/hr/db/schema.rb +588 -0
- data/examples/hr/db/seeds.rb +932 -0
- data/examples/hr/erd.md +396 -0
- data/examples/hr/public/400.html +135 -0
- data/examples/hr/public/404.html +135 -0
- data/examples/hr/public/406-unsupported-browser.html +135 -0
- data/examples/hr/public/422.html +135 -0
- data/examples/hr/public/500.html +135 -0
- data/examples/hr/public/icon.svg +3 -0
- data/examples/hr/public/robots.txt +1 -0
- data/examples/showcase/Gemfile +15 -0
- data/examples/showcase/Gemfile.lock +425 -0
- data/examples/showcase/Rakefile +2 -0
- data/examples/showcase/app/actions/showcase_batch_task/assign_batch.rb +20 -0
- data/examples/showcase/app/actions/showcase_batch_task/close_task.rb +21 -0
- data/examples/showcase/app/actions/showcase_condition/approve.rb +20 -0
- data/examples/showcase/app/actions/showcase_permission/lock.rb +20 -0
- data/examples/showcase/app/assets/config/manifest.js +3 -0
- data/examples/showcase/app/assets/stylesheets/application.css +27 -0
- data/examples/showcase/app/condition_services/budget_threshold.rb +16 -0
- data/examples/showcase/app/condition_services/overdue_check.rb +11 -0
- data/examples/showcase/app/controllers/application_controller.rb +2 -0
- data/examples/showcase/app/controllers/docs_controller.rb +48 -0
- data/examples/showcase/app/controllers/host_inventory_items_managed_controller.rb +90 -0
- data/examples/showcase/app/controllers/host_inventory_items_report_controller.rb +39 -0
- data/examples/showcase/app/controllers/host_inventory_items_wizard_controller.rb +50 -0
- data/examples/showcase/app/data_providers/showcase_amount_provider.rb +52 -0
- data/examples/showcase/app/data_providers/weather_station_provider.rb +140 -0
- data/examples/showcase/app/event_handlers/showcase_model/on_status_change.rb +17 -0
- data/examples/showcase/app/event_handlers/showcase_permission/on_status_change.rb +17 -0
- data/examples/showcase/app/event_handlers/showcase_workflow/log_review_exit.rb +16 -0
- data/examples/showcase/app/event_handlers/showcase_workflow/notify_reviewers.rb +15 -0
- data/examples/showcase/app/event_handlers/showcase_workflow/request_submitted.rb +15 -0
- data/examples/showcase/app/lcp_metrics/showcase_metrics.rb +31 -0
- data/examples/showcase/app/lcp_services/computed/showcase_score.rb +18 -0
- data/examples/showcase/app/lcp_services/computed/showcase_total.rb +13 -0
- data/examples/showcase/app/lcp_services/defaults/one_week_from_now.rb +11 -0
- data/examples/showcase/app/lcp_services/menu_items/recent_announcements.rb +36 -0
- data/examples/showcase/app/lcp_services/virtual_columns/project_health.rb +26 -0
- data/examples/showcase/app/model_extensions/lcp_error_log_extension.rb +12 -0
- data/examples/showcase/app/models/host_inventory_item.rb +20 -0
- data/examples/showcase/app/models/platform_profile.rb +27 -0
- data/examples/showcase/app/models/platform_user.rb +5 -0
- data/examples/showcase/app/views/docs/show.html.erb +19 -0
- data/examples/showcase/app/views/host_inventory_items_report/index.html.erb +61 -0
- data/examples/showcase/app/views/host_inventory_items_report/show.html.erb +39 -0
- data/examples/showcase/app/views/layouts/docs.html.erb +46 -0
- data/examples/showcase/app/views/showcase/menu_renderers/_status_pill.html.erb +17 -0
- data/examples/showcase/app/views/showcase_custom/_activity_timeline.html.erb +37 -0
- data/examples/showcase/app/views/showcase_custom/_card_index.html.erb +74 -0
- data/examples/showcase/app/views/showcase_custom/_detail_show.html.erb +115 -0
- data/examples/showcase/app/views/showcase_custom/_location_map.html.erb +29 -0
- data/examples/showcase/app/views/showcase_custom/_location_view.html.erb +34 -0
- data/examples/showcase/app/views/showcase_custom/_quick_stats.html.erb +50 -0
- data/examples/showcase/app/views/showcase_custom/_stats_sidebar.html.erb +42 -0
- data/examples/showcase/app/views/showcase_custom/_tags_editor.html.erb +48 -0
- data/examples/showcase/bin/rails +4 -0
- data/examples/showcase/bin/rake +4 -0
- data/examples/showcase/config/application.rb +37 -0
- data/examples/showcase/config/boot.rb +2 -0
- data/examples/showcase/config/database.yml +12 -0
- data/examples/showcase/config/environment.rb +2 -0
- data/examples/showcase/config/initializers/devise.rb +5 -0
- data/examples/showcase/config/initializers/lcp_ruby.rb +175 -0
- data/examples/showcase/config/lcp_ruby/auth.yml +60 -0
- data/examples/showcase/config/lcp_ruby/jobs/data_import.yml +7 -0
- data/examples/showcase/config/lcp_ruby/jobs/showcase_cleanup.yml +11 -0
- data/examples/showcase/config/lcp_ruby/jobs/showcase_event_triggered.yml +11 -0
- data/examples/showcase/config/lcp_ruby/jobs/showcase_multi_step.yml +9 -0
- data/examples/showcase/config/lcp_ruby/jobs/showcase_webhook.yml +13 -0
- data/examples/showcase/config/lcp_ruby/menu.yml +249 -0
- data/examples/showcase/config/lcp_ruby/models/_base_document.rb +26 -0
- data/examples/showcase/config/lcp_ruby/models/_categorized_document.rb +19 -0
- data/examples/showcase/config/lcp_ruby/models/_contactable.rb +20 -0
- data/examples/showcase/config/lcp_ruby/models/api_token.rb +23 -0
- data/examples/showcase/config/lcp_ruby/models/article.rb +49 -0
- data/examples/showcase/config/lcp_ruby/models/article_tag.rb +10 -0
- data/examples/showcase/config/lcp_ruby/models/author.rb +16 -0
- data/examples/showcase/config/lcp_ruby/models/batch_operation.yml +79 -0
- data/examples/showcase/config/lcp_ruby/models/batch_operation_item.yml +45 -0
- data/examples/showcase/config/lcp_ruby/models/category.rb +23 -0
- data/examples/showcase/config/lcp_ruby/models/comment.rb +21 -0
- data/examples/showcase/config/lcp_ruby/models/custom_field_definition.rb +59 -0
- data/examples/showcase/config/lcp_ruby/models/department.rb +32 -0
- data/examples/showcase/config/lcp_ruby/models/employee.rb +52 -0
- data/examples/showcase/config/lcp_ruby/models/employee_emergency_contact.rb +24 -0
- data/examples/showcase/config/lcp_ruby/models/employee_profile.rb +18 -0
- data/examples/showcase/config/lcp_ruby/models/employee_skill.rb +10 -0
- data/examples/showcase/config/lcp_ruby/models/export_log.yml +27 -0
- data/examples/showcase/config/lcp_ruby/models/export_profile.yml +37 -0
- data/examples/showcase/config/lcp_ruby/models/feature.rb +76 -0
- data/examples/showcase/config/lcp_ruby/models/gapfree_sequence.rb +17 -0
- data/examples/showcase/config/lcp_ruby/models/group.rb +26 -0
- data/examples/showcase/config/lcp_ruby/models/group_membership.rb +21 -0
- data/examples/showcase/config/lcp_ruby/models/group_role_mapping.rb +14 -0
- data/examples/showcase/config/lcp_ruby/models/host_inventory_item.yml +111 -0
- data/examples/showcase/config/lcp_ruby/models/import_profile.rb +30 -0
- data/examples/showcase/config/lcp_ruby/models/import_row.rb +32 -0
- data/examples/showcase/config/lcp_ruby/models/ingredient_def.rb +11 -0
- data/examples/showcase/config/lcp_ruby/models/lcp_error_log.yml +61 -0
- data/examples/showcase/config/lcp_ruby/models/page_config.rb +19 -0
- data/examples/showcase/config/lcp_ruby/models/permission_config.rb +21 -0
- data/examples/showcase/config/lcp_ruby/models/pipeline.rb +15 -0
- data/examples/showcase/config/lcp_ruby/models/pipeline_stage.rb +17 -0
- data/examples/showcase/config/lcp_ruby/models/platform_profile.rb +104 -0
- data/examples/showcase/config/lcp_ruby/models/profile_setting.rb +17 -0
- data/examples/showcase/config/lcp_ruby/models/profile_tag.rb +19 -0
- data/examples/showcase/config/lcp_ruby/models/project.rb +23 -0
- data/examples/showcase/config/lcp_ruby/models/role.rb +24 -0
- data/examples/showcase/config/lcp_ruby/models/saved_filter.rb +50 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_aggregate.rb +93 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_aggregate_company.rb +14 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_aggregate_item.rb +25 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_announcement.rb +20 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_array.rb +45 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_attachment.rb +49 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_audit_log.yml +44 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_audited_record.rb +20 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_batch_task.rb +27 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_business_unit.rb +26 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_condition.rb +24 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_condition_category.rb +16 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_condition_task.rb +16 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_condition_threshold.rb +13 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_contact.rb +22 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_custom_render.rb +38 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_delete_reason.rb +11 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_division.rb +31 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_extensibility.rb +25 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_field.rb +53 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_form.rb +23 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_form_action.rb +38 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_grade.rb +18 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_hr_employee.rb +90 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_item_class.rb +31 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_job_execution.rb +49 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_memo.rb +23 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_model.rb +74 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_organization.rb +31 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_permission.rb +30 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_person.rb +23 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_positioning.rb +19 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_quick_note.rb +13 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_recipe.rb +17 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_report.rb +32 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_school_class.rb +17 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_search.rb +84 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_sequence.rb +46 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_soft_delete.rb +30 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_soft_delete_item.rb +21 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_student.rb +17 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_type_default.rb +49 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_userstamps.rb +26 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_virtual_field.rb +68 -0
- data/examples/showcase/config/lcp_ruby/models/showcase_workflow.rb +54 -0
- data/examples/showcase/config/lcp_ruby/models/skill.rb +22 -0
- data/examples/showcase/config/lcp_ruby/models/tag.rb +16 -0
- data/examples/showcase/config/lcp_ruby/models/user.rb +54 -0
- data/examples/showcase/config/lcp_ruby/models/weather_station.rb +20 -0
- data/examples/showcase/config/lcp_ruby/models/workflow_approval_request.yml +77 -0
- data/examples/showcase/config/lcp_ruby/models/workflow_approval_step.yml +56 -0
- data/examples/showcase/config/lcp_ruby/models/workflow_approval_task.yml +51 -0
- data/examples/showcase/config/lcp_ruby/models/workflow_audit_log.yml +74 -0
- data/examples/showcase/config/lcp_ruby/pages/article_detail.yml +32 -0
- data/examples/showcase/config/lcp_ruby/pages/author_detail.yml +27 -0
- data/examples/showcase/config/lcp_ruby/pages/category_detail.yml +24 -0
- data/examples/showcase/config/lcp_ruby/pages/chart_showcase.yml +151 -0
- data/examples/showcase/config/lcp_ruby/pages/department_detail.yml +55 -0
- data/examples/showcase/config/lcp_ruby/pages/department_explorer.yml +60 -0
- data/examples/showcase/config/lcp_ruby/pages/employee_overview.yml +106 -0
- data/examples/showcase/config/lcp_ruby/pages/employee_transfer_dialog.yml +24 -0
- data/examples/showcase/config/lcp_ruby/pages/hr_turnover_dashboard.yml +288 -0
- data/examples/showcase/config/lcp_ruby/pages/main_dashboard.yml +142 -0
- data/examples/showcase/config/lcp_ruby/pages/monitoring_dashboard.yml +58 -0
- data/examples/showcase/config/lcp_ruby/pages/pipeline_detail.yml +16 -0
- data/examples/showcase/config/lcp_ruby/pages/showcase_custom_zones.yml +39 -0
- data/examples/showcase/config/lcp_ruby/pages/showcase_form_action_dialog.yml +11 -0
- data/examples/showcase/config/lcp_ruby/pages/workflow_request_detail.yml +34 -0
- data/examples/showcase/config/lcp_ruby/permissions/api_token.yml +26 -0
- data/examples/showcase/config/lcp_ruby/permissions/batch_operation.yml +36 -0
- data/examples/showcase/config/lcp_ruby/permissions/custom_field_definition.yml +15 -0
- data/examples/showcase/config/lcp_ruby/permissions/default.yml +28 -0
- data/examples/showcase/config/lcp_ruby/permissions/export_log.yml +23 -0
- data/examples/showcase/config/lcp_ruby/permissions/export_profile.yml +25 -0
- data/examples/showcase/config/lcp_ruby/permissions/gapfree_sequence.yml +6 -0
- data/examples/showcase/config/lcp_ruby/permissions/group.yml +20 -0
- data/examples/showcase/config/lcp_ruby/permissions/group_membership.yml +20 -0
- data/examples/showcase/config/lcp_ruby/permissions/group_role_mapping.yml +20 -0
- data/examples/showcase/config/lcp_ruby/permissions/host_inventory_item.yml +34 -0
- data/examples/showcase/config/lcp_ruby/permissions/import_profile.yml +29 -0
- data/examples/showcase/config/lcp_ruby/permissions/import_row.yml +17 -0
- data/examples/showcase/config/lcp_ruby/permissions/lcp_error_log.yml +8 -0
- data/examples/showcase/config/lcp_ruby/permissions/page_config.yml +15 -0
- data/examples/showcase/config/lcp_ruby/permissions/permission_config.yml +15 -0
- data/examples/showcase/config/lcp_ruby/permissions/platform_profile.yml +23 -0
- data/examples/showcase/config/lcp_ruby/permissions/profile_setting.yml +23 -0
- data/examples/showcase/config/lcp_ruby/permissions/profile_tag.yml +23 -0
- data/examples/showcase/config/lcp_ruby/permissions/role.yml +20 -0
- data/examples/showcase/config/lcp_ruby/permissions/saved_filter.yml +39 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_announcement.yml +22 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_audit_log.yml +15 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_audited_record.yml +15 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_batch_task.yml +31 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_condition.yml +54 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_contact.yml +21 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_custom_render.yml +21 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_delete_reason.yml +12 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_form_action.yml +26 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_grade.yml +33 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_job_execution.yml +23 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_permission.yml +47 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_quick_note.yml +12 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_school_class.yml +37 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_student.yml +43 -0
- data/examples/showcase/config/lcp_ruby/permissions/showcase_workflow.yml +34 -0
- data/examples/showcase/config/lcp_ruby/permissions/workflow_approval_request.yml +18 -0
- data/examples/showcase/config/lcp_ruby/permissions/workflow_approval_step.yml +18 -0
- data/examples/showcase/config/lcp_ruby/permissions/workflow_approval_task.yml +18 -0
- data/examples/showcase/config/lcp_ruby/permissions/workflow_audit_log.yml +19 -0
- data/examples/showcase/config/lcp_ruby/presenters/announcements.rb +59 -0
- data/examples/showcase/config/lcp_ruby/presenters/approval_requests.rb +69 -0
- data/examples/showcase/config/lcp_ruby/presenters/approval_steps.rb +59 -0
- data/examples/showcase/config/lcp_ruby/presenters/approval_tasks.rb +65 -0
- data/examples/showcase/config/lcp_ruby/presenters/article_comments_zone.rb +12 -0
- data/examples/showcase/config/lcp_ruby/presenters/article_related_zone.rb +15 -0
- data/examples/showcase/config/lcp_ruby/presenters/articles.rb +149 -0
- data/examples/showcase/config/lcp_ruby/presenters/articles_tiles.rb +30 -0
- data/examples/showcase/config/lcp_ruby/presenters/author_articles_zone.rb +12 -0
- data/examples/showcase/config/lcp_ruby/presenters/author_show_zone.rb +12 -0
- data/examples/showcase/config/lcp_ruby/presenters/authors.rb +39 -0
- data/examples/showcase/config/lcp_ruby/presenters/batch_operation_items.yml +45 -0
- data/examples/showcase/config/lcp_ruby/presenters/batch_operations.yml +66 -0
- data/examples/showcase/config/lcp_ruby/presenters/categories.rb +50 -0
- data/examples/showcase/config/lcp_ruby/presenters/category_articles_zone.rb +20 -0
- data/examples/showcase/config/lcp_ruby/presenters/category_children_zone.rb +10 -0
- data/examples/showcase/config/lcp_ruby/presenters/comment_quick_add_dialog.rb +15 -0
- data/examples/showcase/config/lcp_ruby/presenters/custom_fields.rb +124 -0
- data/examples/showcase/config/lcp_ruby/presenters/dashboard_employees.rb +16 -0
- data/examples/showcase/config/lcp_ruby/presenters/delete_reason_dialog.rb +13 -0
- data/examples/showcase/config/lcp_ruby/presenters/departments.rb +55 -0
- data/examples/showcase/config/lcp_ruby/presenters/dept_add_employee_dialog.rb +19 -0
- data/examples/showcase/config/lcp_ruby/presenters/dept_children_zone.rb +10 -0
- data/examples/showcase/config/lcp_ruby/presenters/dept_detail_zone.rb +14 -0
- data/examples/showcase/config/lcp_ruby/presenters/dept_employees_zone.rb +16 -0
- data/examples/showcase/config/lcp_ruby/presenters/dept_list_selection_zone.rb +19 -0
- data/examples/showcase/config/lcp_ruby/presenters/employee_overview_index_zone.rb +36 -0
- data/examples/showcase/config/lcp_ruby/presenters/employee_quick_add_dialog.rb +14 -0
- data/examples/showcase/config/lcp_ruby/presenters/employee_show_zone.rb +20 -0
- data/examples/showcase/config/lcp_ruby/presenters/employee_transfer_form_zone.rb +19 -0
- data/examples/showcase/config/lcp_ruby/presenters/employees.rb +165 -0
- data/examples/showcase/config/lcp_ruby/presenters/employees_tiles.rb +33 -0
- data/examples/showcase/config/lcp_ruby/presenters/export_logs.yml +58 -0
- data/examples/showcase/config/lcp_ruby/presenters/export_profiles.yml +70 -0
- data/examples/showcase/config/lcp_ruby/presenters/extensibility_quick_edit_dialog.rb +13 -0
- data/examples/showcase/config/lcp_ruby/presenters/feature_kanban.rb +30 -0
- data/examples/showcase/config/lcp_ruby/presenters/features_card.rb +179 -0
- data/examples/showcase/config/lcp_ruby/presenters/features_hub.rb +44 -0
- data/examples/showcase/config/lcp_ruby/presenters/features_table.rb +37 -0
- data/examples/showcase/config/lcp_ruby/presenters/features_tiles.rb +48 -0
- data/examples/showcase/config/lcp_ruby/presenters/group_memberships.rb +54 -0
- data/examples/showcase/config/lcp_ruby/presenters/group_role_mappings.rb +48 -0
- data/examples/showcase/config/lcp_ruby/presenters/groups.rb +74 -0
- data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items.rb +99 -0
- data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items_managed.rb +84 -0
- data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items_report.rb +40 -0
- data/examples/showcase/config/lcp_ruby/presenters/host_inventory_items_wizard.rb +76 -0
- data/examples/showcase/config/lcp_ruby/presenters/import_profiles.rb +61 -0
- data/examples/showcase/config/lcp_ruby/presenters/import_rows.rb +39 -0
- data/examples/showcase/config/lcp_ruby/presenters/lcp_error_logs.yml +46 -0
- data/examples/showcase/config/lcp_ruby/presenters/my_api_tokens.rb +35 -0
- data/examples/showcase/config/lcp_ruby/presenters/my_api_tokens_create_dialog.rb +27 -0
- data/examples/showcase/config/lcp_ruby/presenters/my_employee_profile.rb +24 -0
- data/examples/showcase/config/lcp_ruby/presenters/my_settings.rb +50 -0
- data/examples/showcase/config/lcp_ruby/presenters/page_configs.rb +56 -0
- data/examples/showcase/config/lcp_ruby/presenters/permission_configs.rb +58 -0
- data/examples/showcase/config/lcp_ruby/presenters/pipeline_edit_zone.rb +11 -0
- data/examples/showcase/config/lcp_ruby/presenters/pipeline_stages.rb +51 -0
- data/examples/showcase/config/lcp_ruby/presenters/pipeline_stages_zone.rb +11 -0
- data/examples/showcase/config/lcp_ruby/presenters/pipelines.rb +40 -0
- data/examples/showcase/config/lcp_ruby/presenters/platform_profiles.rb +90 -0
- data/examples/showcase/config/lcp_ruby/presenters/projects.rb +58 -0
- data/examples/showcase/config/lcp_ruby/presenters/quick_note_dialog.rb +14 -0
- data/examples/showcase/config/lcp_ruby/presenters/roles.rb +60 -0
- data/examples/showcase/config/lcp_ruby/presenters/save_filter_dialog.rb +17 -0
- data/examples/showcase/config/lcp_ruby/presenters/saved_filters.rb +94 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_aggregate_items.rb +61 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_aggregates.rb +115 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_aggregates_tiles.rb +44 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_amount_kanban.rb +31 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_arrays.rb +140 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_attachments.rb +66 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_audit_logs.rb +46 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_audited_records.rb +58 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_batch_tasks.rb +97 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_batch_tasks_archive.rb +52 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_business_units.rb +43 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_conditions.rb +195 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_contacts.rb +82 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_custom_render_with.rb +50 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_custom_sections.rb +88 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_custom_zones_main.rb +75 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_divisions.rb +53 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_extensibility.rb +48 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_fields_card.rb +34 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_fields_table.rb +128 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_fields_tiles.rb +42 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_form_action_dialog.rb +27 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_form_actions.rb +132 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_form_actions_overflow.rb +76 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_forms.rb +110 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_grades.rb +38 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_hr_employees.rb +84 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_item_classes.rb +120 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_job_executions.rb +150 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_memos.rb +96 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_models.rb +112 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_organizations.rb +106 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_people.rb +97 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_permissions.rb +84 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_positioning.rb +61 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_recipes.rb +105 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_recipes_raw.rb +32 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_reports.rb +109 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_school_classes.rb +34 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_searches.rb +226 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_sequences.rb +67 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_soft_delete.rb +88 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_soft_delete_archive.rb +52 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_soft_delete_items.rb +47 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_students.rb +33 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_type_defaults.rb +92 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_userstamps.rb +76 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_virtual_fields.rb +104 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_workflow_admin.rb +24 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_workflow_kanban.rb +36 -0
- data/examples/showcase/config/lcp_ruby/presenters/showcase_workflows.rb +103 -0
- data/examples/showcase/config/lcp_ruby/presenters/tags.rb +35 -0
- data/examples/showcase/config/lcp_ruby/presenters/users.rb +61 -0
- data/examples/showcase/config/lcp_ruby/presenters/weather_stations.rb +60 -0
- data/examples/showcase/config/lcp_ruby/presenters/workflow_audit_logs.rb +76 -0
- data/examples/showcase/config/lcp_ruby/presenters/workflow_request_audit_zone.rb +28 -0
- data/examples/showcase/config/lcp_ruby/theme.yml +2 -0
- data/examples/showcase/config/lcp_ruby/types/currency.yml +15 -0
- data/examples/showcase/config/lcp_ruby/types/percentage.yml +16 -0
- data/examples/showcase/config/lcp_ruby/types/rating.yml +20 -0
- data/examples/showcase/config/lcp_ruby/views/announcements.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/approval_requests.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/approval_steps.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/approval_tasks.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/articles.yml +12 -0
- data/examples/showcase/config/lcp_ruby/views/authors.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/batch_operation_items.yml +6 -0
- data/examples/showcase/config/lcp_ruby/views/batch_operations.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/categories.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/custom_fields.rb +8 -0
- data/examples/showcase/config/lcp_ruby/views/dashboard.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/department_explorer.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/departments.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/employee_overview.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/employees.yml +12 -0
- data/examples/showcase/config/lcp_ruby/views/export_logs.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/export_profiles.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/features.yml +19 -0
- data/examples/showcase/config/lcp_ruby/views/group_memberships.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/group_role_mappings.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/groups.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/host_inventory_items.yml +16 -0
- data/examples/showcase/config/lcp_ruby/views/hr_turnover_dashboard.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/import_profiles.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/import_rows.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/lcp_error_logs.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/monitoring.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/my_api_tokens.rb +9 -0
- data/examples/showcase/config/lcp_ruby/views/page_configs.yml +11 -0
- data/examples/showcase/config/lcp_ruby/views/permission_configs.yml +11 -0
- data/examples/showcase/config/lcp_ruby/views/pipeline_stages.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/pipelines.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/platform_profiles.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/projects.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/roles.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/saved_filters.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_aggregate_items.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_aggregates.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_arrays.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_attachments.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_audit_logs.yml +11 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_audited_records.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_batch_tasks.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_business_units.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_conditions.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_contacts.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_custom_render_with.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_custom_sections.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_custom_zones.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_divisions.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_extensibility.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_fields.yml +13 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_form_actions.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_forms.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_grades.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_hr_employees.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_item_classes.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_job_executions.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_memos.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_models.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_organizations.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_people.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_permissions.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_positioning.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_recipes.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_reports.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_school_classes.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_searches.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_sequences.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_soft_delete.yml +10 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_soft_delete_items.yml +8 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_students.yml +9 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_userstamps.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_virtual_fields.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/showcase_workflows.yml +16 -0
- data/examples/showcase/config/lcp_ruby/views/tags.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/users.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/weather_stations.yml +7 -0
- data/examples/showcase/config/lcp_ruby/views/workflow_audit_logs.yml +9 -0
- data/examples/showcase/config/lcp_ruby/workflows/showcase_form_action.rb +48 -0
- data/examples/showcase/config/lcp_ruby/workflows/showcase_workflow.rb +199 -0
- data/examples/showcase/config/locales/cs.yml +2818 -0
- data/examples/showcase/config/locales/en.yml +67 -0
- data/examples/showcase/config/locales/lcp_ruby/api_tokens.cs.yml +46 -0
- data/examples/showcase/config/locales/lcp_ruby/api_tokens.en.yml +49 -0
- data/examples/showcase/config/routes.rb +21 -0
- data/examples/showcase/config/storage.yml +3 -0
- data/examples/showcase/config.ru +2 -0
- data/examples/showcase/db/migrate/20260219124321_create_active_storage_tables.active_storage.rb +57 -0
- data/examples/showcase/db/migrate/20260220072723_create_lcp_ruby_users.rb +42 -0
- data/examples/showcase/db/migrate/20260406120000_create_host_inventory_items.rb +32 -0
- data/examples/showcase/db/migrate/20260428134542_add_oidc_columns_to_lcp_ruby_users.rb +13 -0
- data/examples/showcase/db/migrate/20260502120000_create_platform_profiles.rb +19 -0
- data/examples/showcase/db/schema.rb +1222 -0
- data/examples/showcase/db/seeds.rb +3085 -0
- data/examples/showcase/erd.md +567 -0
- data/examples/showcase/test/fixtures/import_articles.csv +4 -0
- data/examples/showcase/test/fixtures/import_employees.csv +4 -0
- data/examples/todo/Gemfile +8 -0
- data/examples/todo/Gemfile.lock +415 -0
- data/examples/todo/Rakefile +2 -0
- data/examples/todo/app/assets/config/manifest.js +1 -0
- data/examples/todo/app/controllers/application_controller.rb +6 -0
- data/examples/todo/app/lcp_services/defaults/one_week_from_now.rb +11 -0
- data/examples/todo/bin/rails +4 -0
- data/examples/todo/bin/rake +4 -0
- data/examples/todo/config/application.rb +31 -0
- data/examples/todo/config/boot.rb +2 -0
- data/examples/todo/config/database.yml +12 -0
- data/examples/todo/config/environment.rb +2 -0
- data/examples/todo/config/lcp_ruby/models/todo_item.yml +67 -0
- data/examples/todo/config/lcp_ruby/models/todo_list.yml +49 -0
- data/examples/todo/config/lcp_ruby/permissions/default.yml +10 -0
- data/examples/todo/config/lcp_ruby/permissions/todo_item.yml +21 -0
- data/examples/todo/config/lcp_ruby/presenters/todo_item.yml +75 -0
- data/examples/todo/config/lcp_ruby/presenters/todo_list.yml +68 -0
- data/examples/todo/config/lcp_ruby/views/todo_items.yml +11 -0
- data/examples/todo/config/lcp_ruby/views/todo_lists.yml +9 -0
- data/examples/todo/config/routes.rb +4 -0
- data/examples/todo/config/storage.yml +3 -0
- data/examples/todo/config.ru +2 -0
- data/examples/todo/db/migrate/20260219103416_create_active_storage_tables.active_storage.rb +57 -0
- data/examples/todo/db/schema.rb +63 -0
- data/examples/todo/db/seeds.rb +24 -0
- data/examples/todo/erd.md +21 -0
- data/exe/lcp +33 -0
- data/lib/generators/lcp_ruby/agent_setup_generator.rb +102 -0
- data/lib/generators/lcp_ruby/api_tokens_generator.rb +102 -0
- data/lib/generators/lcp_ruby/auditing_generator.rb +54 -0
- data/lib/generators/lcp_ruby/background_jobs_generator.rb +61 -0
- data/lib/generators/lcp_ruby/batch_operations_generator.rb +62 -0
- data/lib/generators/lcp_ruby/claude_skills_generator.rb +47 -0
- data/lib/generators/lcp_ruby/custom_fields_generator.rb +54 -0
- data/lib/generators/lcp_ruby/dsl_to_yaml.rb +72 -0
- data/lib/generators/lcp_ruby/entity/color_palette.rb +22 -0
- data/lib/generators/lcp_ruby/entity/field_descriptor.rb +24 -0
- data/lib/generators/lcp_ruby/entity/field_token_parser.rb +101 -0
- data/lib/generators/lcp_ruby/entity/role_discovery.rb +45 -0
- data/lib/generators/lcp_ruby/entity_generator.rb +1104 -0
- data/lib/generators/lcp_ruby/export_generator.rb +71 -0
- data/lib/generators/lcp_ruby/format_support.rb +56 -0
- data/lib/generators/lcp_ruby/gapfree_sequences_generator.rb +64 -0
- data/lib/generators/lcp_ruby/groups_generator.rb +94 -0
- data/lib/generators/lcp_ruby/host_controller_generator.rb +202 -0
- data/lib/generators/lcp_ruby/import_generator.rb +96 -0
- data/lib/generators/lcp_ruby/install_auth_generator.rb +432 -0
- data/lib/generators/lcp_ruby/install_generator.rb +319 -0
- data/lib/generators/lcp_ruby/monitoring_generator.rb +58 -0
- data/lib/generators/lcp_ruby/oidc_role_mappings_generator.rb +60 -0
- data/lib/generators/lcp_ruby/pages_generator.rb +66 -0
- data/lib/generators/lcp_ruby/permission_source_generator.rb +66 -0
- data/lib/generators/lcp_ruby/role_model_generator.rb +73 -0
- data/lib/generators/lcp_ruby/saved_filters_generator.rb +62 -0
- data/lib/generators/lcp_ruby/templates/add_oidc_columns_to_lcp_ruby_users.rb.erb +13 -0
- data/lib/generators/lcp_ruby/templates/agent_setup/agents_md.md +3 -0
- data/lib/generators/lcp_ruby/templates/agent_setup/claude_md.md +12 -0
- data/lib/generators/lcp_ruby/templates/api_tokens/create_dialog.rb +31 -0
- data/lib/generators/lcp_ruby/templates/api_tokens/locales.en.yml +43 -0
- data/lib/generators/lcp_ruby/templates/api_tokens/model.rb +23 -0
- data/lib/generators/lcp_ruby/templates/api_tokens/permissions.yml +30 -0
- data/lib/generators/lcp_ruby/templates/api_tokens/presenter.rb +38 -0
- data/lib/generators/lcp_ruby/templates/api_tokens/view_group.rb +13 -0
- data/lib/generators/lcp_ruby/templates/auditing/model.rb +34 -0
- data/lib/generators/lcp_ruby/templates/auditing/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/auditing/presenter.rb +41 -0
- data/lib/generators/lcp_ruby/templates/auditing/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/background_jobs/model.rb +59 -0
- data/lib/generators/lcp_ruby/templates/background_jobs/permissions.yml +22 -0
- data/lib/generators/lcp_ruby/templates/background_jobs/presenter.rb +82 -0
- data/lib/generators/lcp_ruby/templates/background_jobs/view_group.rb +9 -0
- data/lib/generators/lcp_ruby/templates/batch_operations/item_model.rb +28 -0
- data/lib/generators/lcp_ruby/templates/batch_operations/item_presenter.rb +43 -0
- data/lib/generators/lcp_ruby/templates/batch_operations/model.rb +42 -0
- data/lib/generators/lcp_ruby/templates/batch_operations/permissions.yml +44 -0
- data/lib/generators/lcp_ruby/templates/batch_operations/presenter.rb +61 -0
- data/lib/generators/lcp_ruby/templates/batch_operations/view_group.rb +9 -0
- data/lib/generators/lcp_ruby/templates/create_lcp_ruby_users.rb.erb +50 -0
- data/lib/generators/lcp_ruby/templates/custom_fields/model.rb +60 -0
- data/lib/generators/lcp_ruby/templates/custom_fields/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/custom_fields/presenter.rb +128 -0
- data/lib/generators/lcp_ruby/templates/custom_fields/view_group.rb +9 -0
- data/lib/generators/lcp_ruby/templates/entity/model.rb +42 -0
- data/lib/generators/lcp_ruby/templates/entity/permissions.yml +20 -0
- data/lib/generators/lcp_ruby/templates/entity/presenter.rb +55 -0
- data/lib/generators/lcp_ruby/templates/entity/view_group.rb +8 -0
- data/lib/generators/lcp_ruby/templates/export/export_log_model.rb +30 -0
- data/lib/generators/lcp_ruby/templates/export/export_log_permissions.yml +24 -0
- data/lib/generators/lcp_ruby/templates/export/export_logs_presenter.rb +51 -0
- data/lib/generators/lcp_ruby/templates/export/export_profile_model.rb +28 -0
- data/lib/generators/lcp_ruby/templates/export/export_profile_permissions.yml +26 -0
- data/lib/generators/lcp_ruby/templates/export/export_profiles_presenter.rb +59 -0
- data/lib/generators/lcp_ruby/templates/gapfree_sequences/model.rb +18 -0
- data/lib/generators/lcp_ruby/templates/gapfree_sequences/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/gapfree_sequences/presenter.rb +51 -0
- data/lib/generators/lcp_ruby/templates/gapfree_sequences/view_group.rb +9 -0
- data/lib/generators/lcp_ruby/templates/groups/group_membership_model.rb +17 -0
- data/lib/generators/lcp_ruby/templates/groups/group_model.rb +28 -0
- data/lib/generators/lcp_ruby/templates/groups/group_permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/groups/group_presenter.rb +53 -0
- data/lib/generators/lcp_ruby/templates/groups/group_role_mapping_model.rb +15 -0
- data/lib/generators/lcp_ruby/templates/groups/group_view_group.rb +9 -0
- data/lib/generators/lcp_ruby/templates/host_controller/controller.rb.erb +131 -0
- data/lib/generators/lcp_ruby/templates/import/data_import_job.yml +7 -0
- data/lib/generators/lcp_ruby/templates/import/import_profile_model.rb +30 -0
- data/lib/generators/lcp_ruby/templates/import/import_profile_permissions.yml +29 -0
- data/lib/generators/lcp_ruby/templates/import/import_profiles_presenter.rb +57 -0
- data/lib/generators/lcp_ruby/templates/import/import_row_model.rb +32 -0
- data/lib/generators/lcp_ruby/templates/import/import_row_permissions.yml +11 -0
- data/lib/generators/lcp_ruby/templates/import/import_rows_presenter.rb +35 -0
- data/lib/generators/lcp_ruby/templates/install/default_permissions.yml +26 -0
- data/lib/generators/lcp_ruby/templates/install/menu.yml.tt +71 -0
- data/lib/generators/lcp_ruby/templates/install/model.rb +20 -0
- data/lib/generators/lcp_ruby/templates/install/permissions.yml +25 -0
- data/lib/generators/lcp_ruby/templates/install/presenter.rb +44 -0
- data/lib/generators/lcp_ruby/templates/install/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/install_auth/oidc/entra.yml.erb +46 -0
- data/lib/generators/lcp_ruby/templates/install_auth/oidc/generic.yml.erb +55 -0
- data/lib/generators/lcp_ruby/templates/install_auth/oidc/google.yml.erb +42 -0
- data/lib/generators/lcp_ruby/templates/install_auth/oidc/keycloak.yml.erb +45 -0
- data/lib/generators/lcp_ruby/templates/install_auth/oidc/okta.yml.erb +45 -0
- data/lib/generators/lcp_ruby/templates/install_auth/user.rb +36 -0
- data/lib/generators/lcp_ruby/templates/monitoring/model.rb +34 -0
- data/lib/generators/lcp_ruby/templates/monitoring/model_extension.rb +12 -0
- data/lib/generators/lcp_ruby/templates/monitoring/page.yml +49 -0
- data/lib/generators/lcp_ruby/templates/monitoring/permissions.yml +8 -0
- data/lib/generators/lcp_ruby/templates/monitoring/presenter.rb +44 -0
- data/lib/generators/lcp_ruby/templates/oidc_role_mappings/locales.en.yml +15 -0
- data/lib/generators/lcp_ruby/templates/oidc_role_mappings/model.rb +32 -0
- data/lib/generators/lcp_ruby/templates/oidc_role_mappings/permissions.yml +21 -0
- data/lib/generators/lcp_ruby/templates/oidc_role_mappings/presenter.rb +41 -0
- data/lib/generators/lcp_ruby/templates/pages/model.rb +19 -0
- data/lib/generators/lcp_ruby/templates/pages/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/pages/presenter.rb +51 -0
- data/lib/generators/lcp_ruby/templates/pages/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/permission_source/model.rb +21 -0
- data/lib/generators/lcp_ruby/templates/permission_source/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/permission_source/presenter.rb +51 -0
- data/lib/generators/lcp_ruby/templates/permission_source/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/role_model/model.rb +24 -0
- data/lib/generators/lcp_ruby/templates/role_model/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/role_model/presenter.rb +62 -0
- data/lib/generators/lcp_ruby/templates/role_model/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/saved_filters/model.rb +51 -0
- data/lib/generators/lcp_ruby/templates/saved_filters/permissions.yml +44 -0
- data/lib/generators/lcp_ruby/templates/saved_filters/presenter.rb +96 -0
- data/lib/generators/lcp_ruby/templates/saved_filters/save_dialog_presenter.rb +19 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/request_model.rb +50 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/request_permissions.yml +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/request_presenter.rb +44 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/request_view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/step_model.rb +36 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/step_permissions.yml +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/task_model.rb +34 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/task_permissions.yml +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/task_presenter.rb +39 -0
- data/lib/generators/lcp_ruby/templates/workflow_approvals/task_view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_audit_log/model.rb +48 -0
- data/lib/generators/lcp_ruby/templates/workflow_audit_log/permissions.yml +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_audit_log/presenter.rb +44 -0
- data/lib/generators/lcp_ruby/templates/workflow_audit_log/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/templates/workflow_definition/model.rb +43 -0
- data/lib/generators/lcp_ruby/templates/workflow_definition/permissions.yml +19 -0
- data/lib/generators/lcp_ruby/templates/workflow_definition/presenter.rb +70 -0
- data/lib/generators/lcp_ruby/templates/workflow_definition/view_group.rb +10 -0
- data/lib/generators/lcp_ruby/workflow_approvals_generator.rb +93 -0
- data/lib/generators/lcp_ruby/workflow_audit_log_generator.rb +54 -0
- data/lib/generators/lcp_ruby/workflow_definition_generator.rb +77 -0
- data/lib/lcp.rb +6 -0
- data/lib/lcp_ruby/actions/action_executor.rb +66 -0
- data/lib/lcp_ruby/actions/action_registry.rb +48 -0
- data/lib/lcp_ruby/actions/api_tokens/revoke.rb +13 -0
- data/lib/lcp_ruby/actions/base_action.rb +79 -0
- data/lib/lcp_ruby/actions/form_action_pipeline.rb +138 -0
- data/lib/lcp_ruby/aggregates/query_builder.rb +6 -0
- data/lib/lcp_ruby/api_tokens/model_extension.rb +41 -0
- data/lib/lcp_ruby/api_tokens/resolver_registry.rb +53 -0
- data/lib/lcp_ruby/api_tokens/token_generator.rb +27 -0
- data/lib/lcp_ruby/api_tokens/verifier.rb +38 -0
- data/lib/lcp_ruby/app_template.rb +181 -0
- data/lib/lcp_ruby/array_query.rb +120 -0
- data/lib/lcp_ruby/asset_copier.rb +62 -0
- data/lib/lcp_ruby/association_fk_type.rb +191 -0
- data/lib/lcp_ruby/association_join_column.rb +28 -0
- data/lib/lcp_ruby/association_options_builder.rb +231 -0
- data/lib/lcp_ruby/auditing/audit_writer.rb +258 -0
- data/lib/lcp_ruby/auditing/contract_validator.rb +95 -0
- data/lib/lcp_ruby/auditing/registry.rb +29 -0
- data/lib/lcp_ruby/auditing/setup.rb +49 -0
- data/lib/lcp_ruby/authentication/audit_subscriber.rb +51 -0
- data/lib/lcp_ruby/authentication/bearer_jwt_verifier.rb +139 -0
- data/lib/lcp_ruby/authentication/devise_setup.rb +47 -0
- data/lib/lcp_ruby/authentication/errors.rb +27 -0
- data/lib/lcp_ruby/authentication/http_fetcher.rb +36 -0
- data/lib/lcp_ruby/authentication/jwks_cache.rb +91 -0
- data/lib/lcp_ruby/authentication/oidc_bearer_resolver.rb +84 -0
- data/lib/lcp_ruby/authentication/omniauth_builder.rb +147 -0
- data/lib/lcp_ruby/authentication/provider.rb +108 -0
- data/lib/lcp_ruby/authentication/provider_registry.rb +227 -0
- data/lib/lcp_ruby/authentication/role_mapper.rb +94 -0
- data/lib/lcp_ruby/authentication/test_support.rb +257 -0
- data/lib/lcp_ruby/authentication/user_resolver.rb +169 -0
- data/lib/lcp_ruby/authentication.rb +40 -0
- data/lib/lcp_ruby/authorization/association_lookup.rb +56 -0
- data/lib/lcp_ruby/authorization/authorization_error.rb +12 -0
- data/lib/lcp_ruby/authorization/cache.rb +89 -0
- data/lib/lcp_ruby/authorization/codes.rb +17 -0
- data/lib/lcp_ruby/authorization/impersonated_user.rb +29 -0
- data/lib/lcp_ruby/authorization/includes_hint.rb +110 -0
- data/lib/lcp_ruby/authorization/inherited_parent_validator.rb +142 -0
- data/lib/lcp_ruby/authorization/invariant_check/configuration.rb +132 -0
- data/lib/lcp_ruby/authorization/invariant_error.rb +15 -0
- data/lib/lcp_ruby/authorization/misconfigured_page_error.rb +30 -0
- data/lib/lcp_ruby/authorization/page_gate.rb +57 -0
- data/lib/lcp_ruby/authorization/permission_evaluator.rb +343 -0
- data/lib/lcp_ruby/authorization/policy_factory.rb +91 -0
- data/lib/lcp_ruby/authorization/runtime_invariant_validator.rb +421 -0
- data/lib/lcp_ruby/authorization/scope_builder.rb +227 -0
- data/lib/lcp_ruby/authorization/scope_resolver.rb +28 -0
- data/lib/lcp_ruby/authorized_controller.rb +44 -0
- data/lib/lcp_ruby/background_jobs/base_handler.rb +113 -0
- data/lib/lcp_ruby/background_jobs/change_handler.rb +17 -0
- data/lib/lcp_ruby/background_jobs/contract.rb +16 -0
- data/lib/lcp_ruby/background_jobs/contract_validator.rb +112 -0
- data/lib/lcp_ruby/background_jobs/declarative/base_action.rb +11 -0
- data/lib/lcp_ruby/background_jobs/declarative/call_webhook_action.rb +174 -0
- data/lib/lcp_ruby/background_jobs/declarative/fire_event_action.rb +24 -0
- data/lib/lcp_ruby/background_jobs/declarative/registry.rb +34 -0
- data/lib/lcp_ruby/background_jobs/declarative/run_scope_action.rb +98 -0
- data/lib/lcp_ruby/background_jobs/declarative/send_notification_action.rb +13 -0
- data/lib/lcp_ruby/background_jobs/definition.rb +134 -0
- data/lib/lcp_ruby/background_jobs/enqueue.rb +83 -0
- data/lib/lcp_ruby/background_jobs/errors.rb +9 -0
- data/lib/lcp_ruby/background_jobs/executor_job.rb +111 -0
- data/lib/lcp_ruby/background_jobs/handler_factory.rb +46 -0
- data/lib/lcp_ruby/background_jobs/host_source.rb +33 -0
- data/lib/lcp_ruby/background_jobs/model_source.rb +93 -0
- data/lib/lcp_ruby/background_jobs/registry.rb +81 -0
- data/lib/lcp_ruby/background_jobs/resolver.rb +29 -0
- data/lib/lcp_ruby/background_jobs/schedule_adapter.rb +14 -0
- data/lib/lcp_ruby/background_jobs/setup.rb +145 -0
- data/lib/lcp_ruby/background_jobs/static_source.rb +19 -0
- data/lib/lcp_ruby/background_jobs/steps_executor.rb +52 -0
- data/lib/lcp_ruby/background_jobs/triggers/event_trigger.rb +97 -0
- data/lib/lcp_ruby/background_jobs/triggers/trigger_installer.rb +20 -0
- data/lib/lcp_ruby/background_jobs/unique_key_builder.rb +31 -0
- data/lib/lcp_ruby/batch_actions/base_service.rb +70 -0
- data/lib/lcp_ruby/batch_actions/batch_action_handler.rb +200 -0
- data/lib/lcp_ruby/batch_actions/custom_action_dispatcher.rb +133 -0
- data/lib/lcp_ruby/batch_actions/destroy_service.rb +37 -0
- data/lib/lcp_ruby/batch_actions/permanently_destroy_service.rb +33 -0
- data/lib/lcp_ruby/batch_actions/restore_service.rb +33 -0
- data/lib/lcp_ruby/batch_actions.rb +5 -0
- data/lib/lcp_ruby/bulk_updater.rb +25 -0
- data/lib/lcp_ruby/cli/docs_command.rb +29 -0
- data/lib/lcp_ruby/cli/examples_command.rb +29 -0
- data/lib/lcp_ruby/cli/new_command.rb +509 -0
- data/lib/lcp_ruby/cli/run_command.rb +155 -0
- data/lib/lcp_ruby/cli/skills_command.rb +54 -0
- data/lib/lcp_ruby/cli.rb +74 -0
- data/lib/lcp_ruby/condition_evaluator.rb +366 -0
- data/lib/lcp_ruby/condition_service_registry.rb +58 -0
- data/lib/lcp_ruby/condition_services/current_user_role.rb +28 -0
- data/lib/lcp_ruby/condition_services/feature_flag.rb +63 -0
- data/lib/lcp_ruby/condition_services/impersonating.rb +24 -0
- data/lib/lcp_ruby/conditions/validator.rb +35 -0
- data/lib/lcp_ruby/configuration.rb +431 -0
- data/lib/lcp_ruby/controller/authentication.rb +118 -0
- data/lib/lcp_ruby/controller/authorization.rb +198 -0
- data/lib/lcp_ruby/controller/bearer_authentication.rb +76 -0
- data/lib/lcp_ruby/controller/crud_helpers.rb +233 -0
- data/lib/lcp_ruby/controller/error_handling.rb +94 -0
- data/lib/lcp_ruby/controller/impersonation.rb +70 -0
- data/lib/lcp_ruby/controller/locale_binding.rb +62 -0
- data/lib/lcp_ruby/controller/path_helpers.rb +125 -0
- data/lib/lcp_ruby/controller/presenter_setup.rb +89 -0
- data/lib/lcp_ruby/controller/search.rb +321 -0
- data/lib/lcp_ruby/controller/view_helpers.rb +105 -0
- data/lib/lcp_ruby/current.rb +13 -0
- data/lib/lcp_ruby/custom_fields/applicator.rb +194 -0
- data/lib/lcp_ruby/custom_fields/contract_validator.rb +77 -0
- data/lib/lcp_ruby/custom_fields/definition_change_handler.rb +21 -0
- data/lib/lcp_ruby/custom_fields/query.rb +112 -0
- data/lib/lcp_ruby/custom_fields/registry.rb +70 -0
- data/lib/lcp_ruby/custom_fields/setup.rb +58 -0
- data/lib/lcp_ruby/custom_fields/utils.rb +40 -0
- data/lib/lcp_ruby/custom_fields.rb +9 -0
- data/lib/lcp_ruby/data_source/api_error_placeholder.rb +47 -0
- data/lib/lcp_ruby/data_source/api_filter_translator.rb +72 -0
- data/lib/lcp_ruby/data_source/api_model_concern.rb +131 -0
- data/lib/lcp_ruby/data_source/api_preloader.rb +44 -0
- data/lib/lcp_ruby/data_source/base.rb +85 -0
- data/lib/lcp_ruby/data_source/cached_wrapper.rb +107 -0
- data/lib/lcp_ruby/data_source/host.rb +71 -0
- data/lib/lcp_ruby/data_source/registry.rb +39 -0
- data/lib/lcp_ruby/data_source/resilient_wrapper.rb +67 -0
- data/lib/lcp_ruby/data_source/rest_json.rb +247 -0
- data/lib/lcp_ruby/data_source/setup.rb +57 -0
- data/lib/lcp_ruby/dev_toolbar.rb +8 -0
- data/lib/lcp_ruby/display/base_renderer.rb +21 -0
- data/lib/lcp_ruby/display/count_badge.rb +11 -0
- data/lib/lcp_ruby/display/icon_badge.rb +17 -0
- data/lib/lcp_ruby/display/renderer_registry.rb +138 -0
- data/lib/lcp_ruby/display/renderers/attachment_link.rb +26 -0
- data/lib/lcp_ruby/display/renderers/attachment_list.rb +32 -0
- data/lib/lcp_ruby/display/renderers/attachment_preview.rb +26 -0
- data/lib/lcp_ruby/display/renderers/avatar.rb +14 -0
- data/lib/lcp_ruby/display/renderers/badge.rb +43 -0
- data/lib/lcp_ruby/display/renderers/boolean_icon.rb +54 -0
- data/lib/lcp_ruby/display/renderers/code.rb +39 -0
- data/lib/lcp_ruby/display/renderers/collection.rb +34 -0
- data/lib/lcp_ruby/display/renderers/color_swatch.rb +25 -0
- data/lib/lcp_ruby/display/renderers/concerns/attachment_helpers.rb +73 -0
- data/lib/lcp_ruby/display/renderers/concerns/workflow_helpers.rb +35 -0
- data/lib/lcp_ruby/display/renderers/copy_code.rb +33 -0
- data/lib/lcp_ruby/display/renderers/currency.rb +14 -0
- data/lib/lcp_ruby/display/renderers/date.rb +17 -0
- data/lib/lcp_ruby/display/renderers/datetime.rb +17 -0
- data/lib/lcp_ruby/display/renderers/email_link.rb +15 -0
- data/lib/lcp_ruby/display/renderers/file_size.rb +11 -0
- data/lib/lcp_ruby/display/renderers/heading.rb +11 -0
- data/lib/lcp_ruby/display/renderers/image.rb +17 -0
- data/lib/lcp_ruby/display/renderers/internal_link.rb +23 -0
- data/lib/lcp_ruby/display/renderers/link.rb +15 -0
- data/lib/lcp_ruby/display/renderers/link_list.rb +90 -0
- data/lib/lcp_ruby/display/renderers/markdown.rb +33 -0
- data/lib/lcp_ruby/display/renderers/number.rb +14 -0
- data/lib/lcp_ruby/display/renderers/percentage.rb +12 -0
- data/lib/lcp_ruby/display/renderers/phone_link.rb +15 -0
- data/lib/lcp_ruby/display/renderers/progress_bar.rb +15 -0
- data/lib/lcp_ruby/display/renderers/rating.rb +15 -0
- data/lib/lcp_ruby/display/renderers/record_link.rb +101 -0
- data/lib/lcp_ruby/display/renderers/relative_date.rb +15 -0
- data/lib/lcp_ruby/display/renderers/rich_text.rb +15 -0
- data/lib/lcp_ruby/display/renderers/text.rb +12 -0
- data/lib/lcp_ruby/display/renderers/truncate.rb +17 -0
- data/lib/lcp_ruby/display/renderers/url_link.rb +22 -0
- data/lib/lcp_ruby/display/renderers/workflow_badge.rb +37 -0
- data/lib/lcp_ruby/display/renderers/workflow_timeline.rb +173 -0
- data/lib/lcp_ruby/display/renderers.rb +3 -0
- data/lib/lcp_ruby/display/text_badge.rb +15 -0
- data/lib/lcp_ruby/dsl/condition_builder.rb +190 -0
- data/lib/lcp_ruby/dsl/dsl_loader.rb +365 -0
- data/lib/lcp_ruby/dsl/field_builder.rb +35 -0
- data/lib/lcp_ruby/dsl/job_builder.rb +92 -0
- data/lib/lcp_ruby/dsl/model_builder.rb +544 -0
- data/lib/lcp_ruby/dsl/presenter_builder.rb +1272 -0
- data/lib/lcp_ruby/dsl/source_location_capture.rb +52 -0
- data/lib/lcp_ruby/dsl/type_builder.rb +88 -0
- data/lib/lcp_ruby/dsl/view_group_builder.rb +92 -0
- data/lib/lcp_ruby/dsl/workflow_builder.rb +319 -0
- data/lib/lcp_ruby/dynamic.rb +7 -0
- data/lib/lcp_ruby/dynamic_references/resolver.rb +154 -0
- data/lib/lcp_ruby/dynamic_references/validator.rb +92 -0
- data/lib/lcp_ruby/embed_providers/base.rb +18 -0
- data/lib/lcp_ruby/embed_providers/grafana.rb +38 -0
- data/lib/lcp_ruby/embed_providers/metabase.rb +37 -0
- data/lib/lcp_ruby/engine.rb +680 -0
- data/lib/lcp_ruby/events/async_handler_job.rb +21 -0
- data/lib/lcp_ruby/events/dispatcher.rb +52 -0
- data/lib/lcp_ruby/events/handler_base.rb +51 -0
- data/lib/lcp_ruby/events/handler_registry.rb +49 -0
- data/lib/lcp_ruby/export/data_generator.rb +158 -0
- data/lib/lcp_ruby/export/export_handler.rb +315 -0
- data/lib/lcp_ruby/export/field_tree_builder.rb +219 -0
- data/lib/lcp_ruby/export/setup.rb +94 -0
- data/lib/lcp_ruby/export/value_formatter.rb +223 -0
- data/lib/lcp_ruby/export.rb +9 -0
- data/lib/lcp_ruby/gem_paths.rb +51 -0
- data/lib/lcp_ruby/generators/entity_menu_writer.rb +258 -0
- data/lib/lcp_ruby/generators/feature_registry.rb +208 -0
- data/lib/lcp_ruby/generators/prerequisites.rb +90 -0
- data/lib/lcp_ruby/grouped_query/builder.rb +206 -0
- data/lib/lcp_ruby/grouped_query/result_wrapper.rb +72 -0
- data/lib/lcp_ruby/grouped_query/row.rb +31 -0
- data/lib/lcp_ruby/groups/change_handler.rb +18 -0
- data/lib/lcp_ruby/groups/contract.rb +42 -0
- data/lib/lcp_ruby/groups/contract_validator.rb +110 -0
- data/lib/lcp_ruby/groups/host_loader.rb +54 -0
- data/lib/lcp_ruby/groups/model_loader.rb +186 -0
- data/lib/lcp_ruby/groups/registry.rb +113 -0
- data/lib/lcp_ruby/groups/setup.rb +129 -0
- data/lib/lcp_ruby/groups/yaml_loader.rb +97 -0
- data/lib/lcp_ruby/hash_utils.rb +42 -0
- data/lib/lcp_ruby/i18n_check/configuration.rb +104 -0
- data/lib/lcp_ruby/i18n_check/heuristics.rb +26 -0
- data/lib/lcp_ruby/i18n_check/key_deriver.rb +136 -0
- data/lib/lcp_ruby/i18n_check/offense.rb +29 -0
- data/lib/lcp_ruby/i18n_check/registry_walker.rb +621 -0
- data/lib/lcp_ruby/i18n_check/reporter.rb +96 -0
- data/lib/lcp_ruby/i18n_check/runner.rb +46 -0
- data/lib/lcp_ruby/i18n_check.rb +15 -0
- data/lib/lcp_ruby/i18n_lint.rb +145 -0
- data/lib/lcp_ruby/import/auto_mapper.rb +98 -0
- data/lib/lcp_ruby/import/field_tree_builder.rb +178 -0
- data/lib/lcp_ruby/import/file_parser.rb +223 -0
- data/lib/lcp_ruby/import/import_dialog_handler.rb +410 -0
- data/lib/lcp_ruby/import/import_job_handler.rb +224 -0
- data/lib/lcp_ruby/import/row_processor.rb +281 -0
- data/lib/lcp_ruby/import/setup.rb +277 -0
- data/lib/lcp_ruby/import/value_coercer.rb +143 -0
- data/lib/lcp_ruby/import.rb +14 -0
- data/lib/lcp_ruby/json_item_wrapper.rb +152 -0
- data/lib/lcp_ruby/kanban/board.rb +28 -0
- data/lib/lcp_ruby/kanban/column.rb +28 -0
- data/lib/lcp_ruby/kanban/default_provider.rb +376 -0
- data/lib/lcp_ruby/kanban/host_provider.rb +94 -0
- data/lib/lcp_ruby/kanban/move_result.rb +52 -0
- data/lib/lcp_ruby/kanban/provider_test_harness.rb +54 -0
- data/lib/lcp_ruby/kanban/swimlane.rb +21 -0
- data/lib/lcp_ruby/menu.rb +46 -0
- data/lib/lcp_ruby/metadata/aggregate_definition.rb +6 -0
- data/lib/lcp_ruby/metadata/association_definition.rb +196 -0
- data/lib/lcp_ruby/metadata/auth_validator.rb +222 -0
- data/lib/lcp_ruby/metadata/configuration_validator.rb +7958 -0
- data/lib/lcp_ruby/metadata/contract_result.rb +9 -0
- data/lib/lcp_ruby/metadata/display_template_definition.rb +77 -0
- data/lib/lcp_ruby/metadata/enum_label_resolver.rb +27 -0
- data/lib/lcp_ruby/metadata/erd_generator.rb +274 -0
- data/lib/lcp_ruby/metadata/event_definition.rb +55 -0
- data/lib/lcp_ruby/metadata/field_definition.rb +267 -0
- data/lib/lcp_ruby/metadata/group_definition.rb +31 -0
- data/lib/lcp_ruby/metadata/i18n_label.rb +29 -0
- data/lib/lcp_ruby/metadata/loader.rb +916 -0
- data/lib/lcp_ruby/metadata/menu_definition.rb +116 -0
- data/lib/lcp_ruby/metadata/menu_item.rb +792 -0
- data/lib/lcp_ruby/metadata/menu_item_resolver.rb +105 -0
- data/lib/lcp_ruby/metadata/model_definition.rb +612 -0
- data/lib/lcp_ruby/metadata/model_hash_merger.rb +88 -0
- data/lib/lcp_ruby/metadata/model_inheritance_resolver.rb +165 -0
- data/lib/lcp_ruby/metadata/page_definition.rb +245 -0
- data/lib/lcp_ruby/metadata/path_template.rb +231 -0
- data/lib/lcp_ruby/metadata/permission_definition.rb +237 -0
- data/lib/lcp_ruby/metadata/permission_merger.rb +81 -0
- data/lib/lcp_ruby/metadata/presenter_definition.rb +689 -0
- data/lib/lcp_ruby/metadata/reserved_names.rb +79 -0
- data/lib/lcp_ruby/metadata/responsive_policy.rb +95 -0
- data/lib/lcp_ruby/metadata/schema_validator.rb +172 -0
- data/lib/lcp_ruby/metadata/validation_definition.rb +69 -0
- data/lib/lcp_ruby/metadata/view_group_definition.rb +208 -0
- data/lib/lcp_ruby/metadata/virtual_column_definition.rb +154 -0
- data/lib/lcp_ruby/metadata/zone_definition.rb +423 -0
- data/lib/lcp_ruby/metrics/collector.rb +123 -0
- data/lib/lcp_ruby/metrics/collector_registry.rb +57 -0
- data/lib/lcp_ruby/metrics/error_recorder.rb +70 -0
- data/lib/lcp_ruby/metrics/fingerprint.rb +33 -0
- data/lib/lcp_ruby/metrics/json_query.rb +47 -0
- data/lib/lcp_ruby/metrics/metric_definitions.rb +105 -0
- data/lib/lcp_ruby/metrics/prometheus_check.rb +9 -0
- data/lib/lcp_ruby/metrics/rate_limiter.rb +99 -0
- data/lib/lcp_ruby/metrics/setup.rb +71 -0
- data/lib/lcp_ruby/metrics/subscriber.rb +126 -0
- data/lib/lcp_ruby/model_factory/aggregate_applicator.rb +14 -0
- data/lib/lcp_ruby/model_factory/api_association_applicator.rb +119 -0
- data/lib/lcp_ruby/model_factory/api_builder.rb +85 -0
- data/lib/lcp_ruby/model_factory/array_type.rb +78 -0
- data/lib/lcp_ruby/model_factory/array_type_applicator.rb +33 -0
- data/lib/lcp_ruby/model_factory/association_applicator.rb +201 -0
- data/lib/lcp_ruby/model_factory/attachment_applicator.rb +160 -0
- data/lib/lcp_ruby/model_factory/auditing_applicator.rb +72 -0
- data/lib/lcp_ruby/model_factory/builder.rb +235 -0
- data/lib/lcp_ruby/model_factory/callback_applicator.rb +63 -0
- data/lib/lcp_ruby/model_factory/computed_applicator.rb +55 -0
- data/lib/lcp_ruby/model_factory/default_applicator.rb +85 -0
- data/lib/lcp_ruby/model_factory/enum_applicator.rb +24 -0
- data/lib/lcp_ruby/model_factory/inherited_parent_validator_applicator.rb +39 -0
- data/lib/lcp_ruby/model_factory/label_method_builder.rb +82 -0
- data/lib/lcp_ruby/model_factory/managed_tracking.rb +71 -0
- data/lib/lcp_ruby/model_factory/positioning_applicator.rb +23 -0
- data/lib/lcp_ruby/model_factory/ransack_applicator.rb +66 -0
- data/lib/lcp_ruby/model_factory/registry.rb +33 -0
- data/lib/lcp_ruby/model_factory/schema_manager.rb +655 -0
- data/lib/lcp_ruby/model_factory/scope_applicator.rb +87 -0
- data/lib/lcp_ruby/model_factory/sequence_applicator.rb +173 -0
- data/lib/lcp_ruby/model_factory/service_accessor_applicator.rb +40 -0
- data/lib/lcp_ruby/model_factory/soft_delete_applicator.rb +141 -0
- data/lib/lcp_ruby/model_factory/transform_applicator.rb +51 -0
- data/lib/lcp_ruby/model_factory/tree_applicator.rb +239 -0
- data/lib/lcp_ruby/model_factory/userstamps_applicator.rb +73 -0
- data/lib/lcp_ruby/model_factory/validation_applicator.rb +319 -0
- data/lib/lcp_ruby/model_factory/virtual_column_applicator.rb +79 -0
- data/lib/lcp_ruby/model_factory/workflow_applicator.rb +141 -0
- data/lib/lcp_ruby/pages/change_handler.rb +15 -0
- data/lib/lcp_ruby/pages/contract_validator.rb +74 -0
- data/lib/lcp_ruby/pages/definition_validator.rb +42 -0
- data/lib/lcp_ruby/pages/filter_form.rb +200 -0
- data/lib/lcp_ruby/pages/filter_form_validator.rb +636 -0
- data/lib/lcp_ruby/pages/registry.rb +133 -0
- data/lib/lcp_ruby/pages/resolver.rb +32 -0
- data/lib/lcp_ruby/pages/scope_context_resolver.rb +37 -0
- data/lib/lcp_ruby/pages/scope_filter_set.rb +57 -0
- data/lib/lcp_ruby/pages/setup.rb +46 -0
- data/lib/lcp_ruby/path_utils.rb +12 -0
- data/lib/lcp_ruby/permissions/change_handler.rb +22 -0
- data/lib/lcp_ruby/permissions/contract_validator.rb +74 -0
- data/lib/lcp_ruby/permissions/definition_validator.rb +119 -0
- data/lib/lcp_ruby/permissions/registry.rb +135 -0
- data/lib/lcp_ruby/permissions/setup.rb +51 -0
- data/lib/lcp_ruby/permissions/source_resolver.rb +56 -0
- data/lib/lcp_ruby/presenter/action_set.rb +236 -0
- data/lib/lcp_ruby/presenter/breadcrumb_builder.rb +183 -0
- data/lib/lcp_ruby/presenter/breadcrumb_path_helper.rb +17 -0
- data/lib/lcp_ruby/presenter/column_set.rb +268 -0
- data/lib/lcp_ruby/presenter/enrichment.rb +136 -0
- data/lib/lcp_ruby/presenter/field_value_resolver.rb +237 -0
- data/lib/lcp_ruby/presenter/includes_resolver/association_dependency.rb +59 -0
- data/lib/lcp_ruby/presenter/includes_resolver/dependency_collector.rb +394 -0
- data/lib/lcp_ruby/presenter/includes_resolver/loading_strategy.rb +70 -0
- data/lib/lcp_ruby/presenter/includes_resolver/strategy_resolver.rb +123 -0
- data/lib/lcp_ruby/presenter/includes_resolver.rb +42 -0
- data/lib/lcp_ruby/presenter/layout_builder.rb +467 -0
- data/lib/lcp_ruby/presenter/link_resolver.rb +65 -0
- data/lib/lcp_ruby/presenter/metadata_lookup.rb +28 -0
- data/lib/lcp_ruby/presenter/resolver.rb +25 -0
- data/lib/lcp_ruby/record_aliases/metadata_checker.rb +213 -0
- data/lib/lcp_ruby/record_aliases/setup.rb +212 -0
- data/lib/lcp_ruby/reserved_route_segments.rb +37 -0
- data/lib/lcp_ruby/roles/change_handler.rb +11 -0
- data/lib/lcp_ruby/roles/contract_validator.rb +67 -0
- data/lib/lcp_ruby/roles/registry.rb +89 -0
- data/lib/lcp_ruby/roles/setup.rb +50 -0
- data/lib/lcp_ruby/routing/presenter_routes.rb +104 -0
- data/lib/lcp_ruby/saved_filters/change_handler.rb +13 -0
- data/lib/lcp_ruby/saved_filters/contract_validator.rb +85 -0
- data/lib/lcp_ruby/saved_filters/registry.rb +36 -0
- data/lib/lcp_ruby/saved_filters/resolver.rb +108 -0
- data/lib/lcp_ruby/saved_filters/setup.rb +42 -0
- data/lib/lcp_ruby/saved_filters/stale_field_validator.rb +84 -0
- data/lib/lcp_ruby/schemas/auth.json +208 -0
- data/lib/lcp_ruby/schemas/menu.json +338 -0
- data/lib/lcp_ruby/schemas/model.json +1161 -0
- data/lib/lcp_ruby/schemas/page.json +877 -0
- data/lib/lcp_ruby/schemas/permission.json +454 -0
- data/lib/lcp_ruby/schemas/presenter.json +2274 -0
- data/lib/lcp_ruby/schemas/theme.json +62 -0
- data/lib/lcp_ruby/schemas/type.json +146 -0
- data/lib/lcp_ruby/schemas/view_group.json +163 -0
- data/lib/lcp_ruby/search/custom_field_filter.rb +171 -0
- data/lib/lcp_ruby/search/custom_filter_interceptor.rb +40 -0
- data/lib/lcp_ruby/search/filter_metadata_builder.rb +409 -0
- data/lib/lcp_ruby/search/filter_param_builder.rb +177 -0
- data/lib/lcp_ruby/search/operator_registry.rb +79 -0
- data/lib/lcp_ruby/search/param_sanitizer.rb +25 -0
- data/lib/lcp_ruby/search/parameter_definition.rb +187 -0
- data/lib/lcp_ruby/search/parameterized_scope_applicator.rb +129 -0
- data/lib/lcp_ruby/search/query_builder.rb +143 -0
- data/lib/lcp_ruby/search/query_language_parser.rb +549 -0
- data/lib/lcp_ruby/search/query_language_serializer.rb +193 -0
- data/lib/lcp_ruby/search/quick_search.rb +162 -0
- data/lib/lcp_ruby/search/relative_date_expander.rb +57 -0
- data/lib/lcp_ruby/search_result.rb +70 -0
- data/lib/lcp_ruby/sequences/sequence_manager.rb +51 -0
- data/lib/lcp_ruby/services/accessors/json_field.rb +23 -0
- data/lib/lcp_ruby/services/built_in_accessors.rb +17 -0
- data/lib/lcp_ruby/services/built_in_defaults.rb +22 -0
- data/lib/lcp_ruby/services/built_in_transforms.rb +20 -0
- data/lib/lcp_ruby/services/checker.rb +133 -0
- data/lib/lcp_ruby/services/registry.rb +83 -0
- data/lib/lcp_ruby/skills_installer.rb +73 -0
- data/lib/lcp_ruby/sort/enum_sort_order.rb +38 -0
- data/lib/lcp_ruby/tasks/destroy_order_resolver.rb +57 -0
- data/lib/lcp_ruby/tasks/doctor.rb +294 -0
- data/lib/lcp_ruby/tasks/permission_resolve_formatter.rb +245 -0
- data/lib/lcp_ruby/types/built_in_types.rb +157 -0
- data/lib/lcp_ruby/types/transforms/base_transform.rb +11 -0
- data/lib/lcp_ruby/types/transforms/downcase.rb +11 -0
- data/lib/lcp_ruby/types/transforms/normalize_phone.rb +19 -0
- data/lib/lcp_ruby/types/transforms/normalize_url.rb +16 -0
- data/lib/lcp_ruby/types/transforms/strip.rb +11 -0
- data/lib/lcp_ruby/types/type_definition.rb +112 -0
- data/lib/lcp_ruby/types/type_registry.rb +75 -0
- data/lib/lcp_ruby/url_safety.rb +97 -0
- data/lib/lcp_ruby/user_snapshot.rb +15 -0
- data/lib/lcp_ruby/version.rb +3 -0
- data/lib/lcp_ruby/view_slots/registry.rb +71 -0
- data/lib/lcp_ruby/view_slots/slot_component.rb +22 -0
- data/lib/lcp_ruby/view_slots/slot_context.rb +20 -0
- data/lib/lcp_ruby/virtual_columns/builder.rb +234 -0
- data/lib/lcp_ruby/virtual_columns/collector.rb +186 -0
- data/lib/lcp_ruby/virtual_columns.rb +4 -0
- data/lib/lcp_ruby/virtual_fields/synthetic_marker.rb +17 -0
- data/lib/lcp_ruby/virtual_fields/types/array_of.rb +49 -0
- data/lib/lcp_ruby/virtual_fields/virtual_field.rb +107 -0
- data/lib/lcp_ruby/virtual_fields/virtual_form.rb +144 -0
- data/lib/lcp_ruby/widgets/chart_palette.rb +25 -0
- data/lib/lcp_ruby/widgets/chartkick_check.rb +9 -0
- data/lib/lcp_ruby/widgets/data_resolver.rb +676 -0
- data/lib/lcp_ruby/widgets/date_grouper.rb +54 -0
- data/lib/lcp_ruby/widgets/presenter_zone_resolver.rb +170 -0
- data/lib/lcp_ruby/widgets/record_source_resolver.rb +56 -0
- data/lib/lcp_ruby/widgets/scope_applicator.rb +187 -0
- data/lib/lcp_ruby/workflow/approval/activation_handler.rb +39 -0
- data/lib/lcp_ruby/workflow/approval/approval_definition.rb +117 -0
- data/lib/lcp_ruby/workflow/approval/approver_resolver.rb +98 -0
- data/lib/lcp_ruby/workflow/approval/cleanup_handler.rb +37 -0
- data/lib/lcp_ruby/workflow/approval/contract_validator.rb +96 -0
- data/lib/lcp_ruby/workflow/approval/data_builder.rb +53 -0
- data/lib/lcp_ruby/workflow/approval/discard_handler.rb +51 -0
- data/lib/lcp_ruby/workflow/approval/engine.rb +314 -0
- data/lib/lcp_ruby/workflow/approval/registry.rb +40 -0
- data/lib/lcp_ruby/workflow/approval/resolution_handler.rb +103 -0
- data/lib/lcp_ruby/workflow/approval/setup.rb +138 -0
- data/lib/lcp_ruby/workflow/approval/step_definition.rb +52 -0
- data/lib/lcp_ruby/workflow/approval/step_evaluator.rb +163 -0
- data/lib/lcp_ruby/workflow/approval/system_evaluator.rb +29 -0
- data/lib/lcp_ruby/workflow/approval/task_manager.rb +202 -0
- data/lib/lcp_ruby/workflow/audit_contract_validator.rb +64 -0
- data/lib/lcp_ruby/workflow/audit_registry.rb +24 -0
- data/lib/lcp_ruby/workflow/audit_writer.rb +51 -0
- data/lib/lcp_ruby/workflow/change_handler.rb +14 -0
- data/lib/lcp_ruby/workflow/contract.rb +21 -0
- data/lib/lcp_ruby/workflow/contract_validator.rb +44 -0
- data/lib/lcp_ruby/workflow/errors.rb +12 -0
- data/lib/lcp_ruby/workflow/host_source.rb +19 -0
- data/lib/lcp_ruby/workflow/mermaid_builder.rb +217 -0
- data/lib/lcp_ruby/workflow/model_source.rb +79 -0
- data/lib/lcp_ruby/workflow/registry.rb +113 -0
- data/lib/lcp_ruby/workflow/resolver.rb +32 -0
- data/lib/lcp_ruby/workflow/setup.rb +135 -0
- data/lib/lcp_ruby/workflow/state_definition.rb +59 -0
- data/lib/lcp_ruby/workflow/state_machine.rb +78 -0
- data/lib/lcp_ruby/workflow/static_source.rb +20 -0
- data/lib/lcp_ruby/workflow/transition_action_builder.rb +46 -0
- data/lib/lcp_ruby/workflow/transition_definition.rb +70 -0
- data/lib/lcp_ruby/workflow/transition_executor.rb +140 -0
- data/lib/lcp_ruby/workflow/transition_label_resolver.rb +21 -0
- data/lib/lcp_ruby/workflow/transition_result.rb +20 -0
- data/lib/lcp_ruby/workflow/value_resolver.rb +58 -0
- data/lib/lcp_ruby/workflow/workflow_definition.rb +195 -0
- data/lib/lcp_ruby.rb +764 -0
- data/lib/rubocop/cop/lcp_ruby/no_hardcoded_i18n_string.rb +249 -0
- data/lib/tasks/lcp_ruby.rake +432 -0
- data/lib/tasks/lcp_ruby_assets.rake +37 -0
- data/lib/tasks/lcp_ruby_auth.rake +49 -0
- data/lib/tasks/lcp_ruby_db.rake +76 -0
- data/lib/tasks/lcp_ruby_doctor.rake +20 -0
- data/lib/tasks/lcp_ruby_feature_catalog.rake +61 -0
- data/lib/tasks/lcp_ruby_gapfree_sequences.rake +39 -0
- data/lib/tasks/lcp_ruby_i18n_check.rake +23 -0
- data/lib/tasks/lcp_ruby_i18n_lint.rake +20 -0
- data/lib/tasks/lcp_ruby_invariant_check.rake +72 -0
- data/vendor/assets/javascripts/lcp_ruby/activestorage.min.js +866 -0
- data/vendor/assets/javascripts/lcp_ruby/highlight.min.js +1244 -0
- data/vendor/assets/javascripts/lcp_ruby/lucide.min.js +12 -0
- data/vendor/assets/javascripts/lcp_ruby/stimulus.umd.js +2588 -0
- data/vendor/assets/javascripts/lcp_ruby/tom-select.complete.min.js +444 -0
- data/vendor/assets/stylesheets/lcp_ruby/highlight-github.min.css +12 -0
- data/vendor/assets/stylesheets/lcp_ruby/tom-select.css +412 -0
- metadata +1950 -0
|
@@ -0,0 +1,1375 @@
|
|
|
1
|
+
# Pages Reference
|
|
2
|
+
|
|
3
|
+
Pages are the top-level layout unit in LCP Ruby. Every routable URL maps to a page, which contains one or more **zones** — regions that display either a presenter or a widget.
|
|
4
|
+
|
|
5
|
+
Pages are defined in `config/lcp_ruby/pages/` as YAML files with the root key `page:`.
|
|
6
|
+
|
|
7
|
+
## Page Attributes
|
|
8
|
+
|
|
9
|
+
| Attribute | Type | Required | Default | Description |
|
|
10
|
+
|-----------|------|----------|---------|-------------|
|
|
11
|
+
| `name` | string | yes | — | Unique page identifier. Used in view group references and dialog targets. |
|
|
12
|
+
| `slug` | string | no | — | URL slug for routing. Pages without a slug are only accessible as dialog targets. |
|
|
13
|
+
| `model` | string | no | — | Associated model name. Standalone dashboard pages may omit this. |
|
|
14
|
+
| `layout` | string | no | `semantic` | `semantic` (named areas) or `grid` (CSS grid with explicit positions). |
|
|
15
|
+
| `title_key` | string | no | — | i18n key for page title. Falls back to humanized name. |
|
|
16
|
+
| `index_presenter` | string | no | — | Presenter to use for the index action. Useful for composite pages where the main zone presenter (show-only) differs from the index presenter. Must have index columns defined and use the same model. |
|
|
17
|
+
| `dialog` | object | no | — | Dialog rendering config. When present, the page can be rendered in a modal. |
|
|
18
|
+
| `zones` | array | yes | — | One or more zone definitions (min 1). |
|
|
19
|
+
|
|
20
|
+
## Dialog Configuration
|
|
21
|
+
|
|
22
|
+
The `dialog:` key controls how the page renders as a modal.
|
|
23
|
+
|
|
24
|
+
| Attribute | Type | Default | Description |
|
|
25
|
+
|-----------|------|---------|-------------|
|
|
26
|
+
| `size` | string | `medium` | `small`, `medium`, `large`, or `fullscreen`. |
|
|
27
|
+
| `closable` | boolean | `true` | Whether clicking outside or pressing Escape closes the dialog. |
|
|
28
|
+
| `title_key` | string | — | i18n key for the dialog title bar. |
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
page:
|
|
32
|
+
name: quick_create_order
|
|
33
|
+
dialog:
|
|
34
|
+
size: large
|
|
35
|
+
closable: false
|
|
36
|
+
title_key: lcp_ruby.dialogs.quick_create
|
|
37
|
+
zones:
|
|
38
|
+
- name: main
|
|
39
|
+
presenter: order_create_form
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Zone Attributes
|
|
43
|
+
|
|
44
|
+
Each zone represents a content region within the page.
|
|
45
|
+
|
|
46
|
+
| Attribute | Type | Required | Default | Description |
|
|
47
|
+
|-----------|------|----------|---------|-------------|
|
|
48
|
+
| `name` | string | yes | — | Unique identifier within the page. |
|
|
49
|
+
| `type` | string | no | `presenter` | `presenter`, `widget`, or `custom`. |
|
|
50
|
+
| `presenter` | string | conditional | — | Presenter name. Required for presenter zones. |
|
|
51
|
+
| `area` | string | no | `main` | Named area for semantic layout (`main`, `tabs`, `sidebar`, `below`). |
|
|
52
|
+
| `widget` | object | conditional | — | Widget config. Required for widget zones. |
|
|
53
|
+
| `position` | object | no | — | Grid positioning (`row`, `col`, `width`, `height`). |
|
|
54
|
+
| `scope` | string | no | — | Named scope to apply to the zone's data query. |
|
|
55
|
+
| `default_scope` | string | no | — | Named scope applied before `scope`. Both are cumulative. |
|
|
56
|
+
| `limit` | integer | no | — | Maximum records to display (min 1). |
|
|
57
|
+
| `visible_when` | object | no | — | Conditional visibility (role shortcut or condition object). |
|
|
58
|
+
| `width` | integer | no | — | CSS grid column ratio for semantic layout (1-12 scale). Controls main/sidebar proportions. |
|
|
59
|
+
| `filters` | string | no | `quick` for tabs, `none` otherwise | Filter mode: `full`, `quick`, or `none`. Widget zones must use `none`. |
|
|
60
|
+
| `record_click` | string | no | `navigate` | Row click behavior: `navigate` (full-page), `dialog` (opens in dialog), `none` (no click). |
|
|
61
|
+
| `submit_redirect` | string | no | — | After form submit, redirect target. Only `self` supported (stay on composite page). Must be `main` area, presenter zone only. |
|
|
62
|
+
| `record_source` | object/string | no | — | Load a single record by ID from a URL parameter. Use `selection` (string) for master-detail. Mutually exclusive with `scope_context`. |
|
|
63
|
+
| `selection` | string | no | — | Enables row selection in sidebar. Only `single` is supported. Marks the zone as the selection zone for master-detail layouts. Only valid on presenter zones in `sidebar` area. |
|
|
64
|
+
|
|
65
|
+
### Presenter Zones
|
|
66
|
+
|
|
67
|
+
Render a full presenter (index table, show page, form). The presenter must exist in `config/lcp_ruby/presenters/`.
|
|
68
|
+
|
|
69
|
+
```yaml
|
|
70
|
+
zones:
|
|
71
|
+
- name: recent_tasks
|
|
72
|
+
presenter: dashboard_tasks
|
|
73
|
+
scope: recent
|
|
74
|
+
limit: 5
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Widget Zones
|
|
78
|
+
|
|
79
|
+
Render a standalone widget. Set `type: widget` and provide the `widget:` config.
|
|
80
|
+
|
|
81
|
+
### Custom Zones
|
|
82
|
+
|
|
83
|
+
Render a host-app partial inside a zone. Set `type: custom` and provide the `partial:` path. Custom zones delegate rendering entirely to the host app while participating in the composite page layout (areas, tabs, visibility).
|
|
84
|
+
|
|
85
|
+
| Attribute | Type | Required | Default | Description |
|
|
86
|
+
|-----------|------|----------|---------|-------------|
|
|
87
|
+
| `partial` | string | yes | — | Host-app partial path (e.g., `"employees/gantt_chart"`). |
|
|
88
|
+
| `model` | string | no | page's primary model | Model name for `scoped_records`. Enables cross-model zones. |
|
|
89
|
+
| `eager` | boolean | no | `false` | When `true`, rendered inline (no Turbo Frame). Solves inline `<script>` issues. |
|
|
90
|
+
| `apply_scope_context` | boolean | no | `true` | When `true`, builds `scoped_records` from `scope_context`. When `false`, only `scope_context_values` hash is passed. |
|
|
91
|
+
| `scope_context` | object | no | — | Dynamic references resolved before render (e.g., `employee_id: ":record.id"`). |
|
|
92
|
+
|
|
93
|
+
**Supported attributes:** `area`, `label_key`, `visible_when`, `width`, `scope_context`, `depends_on` — work as for other zone types.
|
|
94
|
+
|
|
95
|
+
**Not applicable (rejected at boot):** `scope`, `default_scope`, `filters`, `record_click`, `selection`, `record_source`, `submit_redirect`.
|
|
96
|
+
|
|
97
|
+
**Authorization:** Custom zones have no presenter, so only `visible_when` controls visibility. For fine-grained auth, check `evaluator.can?` inside the partial.
|
|
98
|
+
|
|
99
|
+
**Turbo Frames:** Lazy-loaded custom zones (default) are rendered inside Turbo Frames. Turbo does not execute inline `<script>` tags in frame payloads. Use Stimulus controllers (`data-controller`) for JS initialization, or set `eager: true` for inline rendering.
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
zones:
|
|
103
|
+
- name: project_timeline
|
|
104
|
+
type: custom
|
|
105
|
+
partial: "employees/gantt_chart"
|
|
106
|
+
area: tabs
|
|
107
|
+
label_key: lcp_ruby.pages.employee_detail.tabs.timeline
|
|
108
|
+
scope_context:
|
|
109
|
+
employee_id: ":record.id"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The partial receives: `record`, `presenter`, `evaluator`, `zone`, `scope_context_values`, `scoped_records`, `selection_id`, `current_user`, `page_definition`, `params`.
|
|
113
|
+
|
|
114
|
+
See also [Rendering Extension Points](../guides/rendering-extension-points.md).
|
|
115
|
+
|
|
116
|
+
## Widget Types
|
|
117
|
+
|
|
118
|
+
### kpi_card
|
|
119
|
+
|
|
120
|
+
Displays an aggregate value (count, sum, avg, min, max) over a model's records, or a Prometheus metric value via `value_source`. Two mutually exclusive modes: **model-based** (`model` + `aggregate`) or **metric-based** (`value_source`).
|
|
121
|
+
|
|
122
|
+
| Attribute | Type | Required | Description |
|
|
123
|
+
|-----------|------|----------|-------------|
|
|
124
|
+
| `type` | string | yes | Must be `kpi_card`. |
|
|
125
|
+
| `model` | string | yes* | Model to aggregate over. *Required for model-based KPI; must be omitted when `value_source` is used. |
|
|
126
|
+
| `aggregate` | string | yes* | `count`, `sum`, `avg`, `min`, or `max`. *Required for model-based KPI; must be omitted when `value_source` is used. |
|
|
127
|
+
| `aggregate_field` | string | no | Field to aggregate. Required for `sum`/`avg`/`min`/`max`. Defaults to `id` for `count`. |
|
|
128
|
+
| `value_source` | object | yes* | Read value from Prometheus metric. *Required for metric-based KPI; mutually exclusive with `model`+`aggregate`. |
|
|
129
|
+
| `format` | string | no | Display format (e.g., `currency`, `number`, `percent`). |
|
|
130
|
+
| `icon` | string | no | [Lucide](https://lucide.dev/icons/) icon name (kebab-case, e.g. `dollar-sign`, `activity`). The engine swaps it for an inline SVG client-side. |
|
|
131
|
+
| `link_to` | string | no | Page slug to navigate to on click. |
|
|
132
|
+
| `label_key` | string | no | i18n key for the card label. |
|
|
133
|
+
| `compare_scope` | string | no | Named scope for the comparison period. When set, displays a trend indicator (percentage change) next to the KPI value. |
|
|
134
|
+
|
|
135
|
+
**`value_source` object:**
|
|
136
|
+
|
|
137
|
+
| Attribute | Type | Required | Description |
|
|
138
|
+
|-----------|------|----------|-------------|
|
|
139
|
+
| `type` | string | yes | Must be `metric`. |
|
|
140
|
+
| `name` | string | yes | Prometheus metric name (e.g., `lcp_ruby_requests_total`). |
|
|
141
|
+
| `labels` | object | no | Label filter to select a specific time series. |
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
# Model-based KPI card
|
|
145
|
+
- name: total_revenue
|
|
146
|
+
type: widget
|
|
147
|
+
widget:
|
|
148
|
+
type: kpi_card
|
|
149
|
+
model: order
|
|
150
|
+
aggregate: sum
|
|
151
|
+
aggregate_field: total_amount
|
|
152
|
+
format: currency
|
|
153
|
+
icon: dollar-sign
|
|
154
|
+
link_to: orders
|
|
155
|
+
label_key: lcp_ruby.dashboard.revenue
|
|
156
|
+
position: { row: 1, col: 1, width: 4, height: 1 }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
# Metric-based KPI card (reads from Prometheus)
|
|
161
|
+
- name: total_requests
|
|
162
|
+
type: widget
|
|
163
|
+
widget:
|
|
164
|
+
type: kpi_card
|
|
165
|
+
value_source:
|
|
166
|
+
type: metric
|
|
167
|
+
name: lcp_ruby_requests_total
|
|
168
|
+
icon: activity
|
|
169
|
+
label_key: lcp_ruby.monitoring.total_requests
|
|
170
|
+
position: { row: 1, col: 5, width: 4, height: 1 }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### text
|
|
174
|
+
|
|
175
|
+
Displays translated static content.
|
|
176
|
+
|
|
177
|
+
| Attribute | Type | Required | Description |
|
|
178
|
+
|-----------|------|----------|-------------|
|
|
179
|
+
| `type` | string | yes | Must be `text`. |
|
|
180
|
+
| `content_key` | string | yes | i18n key for the text content. |
|
|
181
|
+
|
|
182
|
+
```yaml
|
|
183
|
+
- name: welcome
|
|
184
|
+
type: widget
|
|
185
|
+
widget:
|
|
186
|
+
type: text
|
|
187
|
+
content_key: lcp_ruby.dashboard.welcome
|
|
188
|
+
position: { row: 2, col: 1, width: 12, height: 1 }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### list
|
|
192
|
+
|
|
193
|
+
Displays recent records from a model.
|
|
194
|
+
|
|
195
|
+
| Attribute | Type | Required | Description |
|
|
196
|
+
|-----------|------|----------|-------------|
|
|
197
|
+
| `type` | string | yes | Must be `list`. |
|
|
198
|
+
| `model` | string | yes | Model to list records from. |
|
|
199
|
+
| `link_to` | string | no | Page slug for the "view all" link. |
|
|
200
|
+
| `label_key` | string | no | i18n key for the list header. |
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
- name: recent_orders
|
|
204
|
+
type: widget
|
|
205
|
+
widget:
|
|
206
|
+
type: list
|
|
207
|
+
model: order
|
|
208
|
+
link_to: orders
|
|
209
|
+
label_key: lcp_ruby.dashboard.recent_orders
|
|
210
|
+
scope: recent
|
|
211
|
+
limit: 5
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### chart
|
|
215
|
+
|
|
216
|
+
Displays a chart visualization. Requires the `chartkick` gem (optional dependency). Add `gem "chartkick"` to the host app Gemfile and declare the JS assets in `app/assets/config/manifest.js` (`//= link Chart.bundle.js` and `//= link chartkick.js`). See the [Dashboards Guide](../guides/dashboards.md#chart) for full setup instructions.
|
|
217
|
+
|
|
218
|
+
Charts support a single series (inline `aggregate`/`group_by`) or an explicit `series:` array for multi-series comparisons. The default color palette is the Okabe-Ito colorblind-safe 8-color set (Wong, 2011); per-series and chart-level overrides are supported.
|
|
219
|
+
|
|
220
|
+
#### Chart-level attributes
|
|
221
|
+
|
|
222
|
+
| Attribute | Type | Required | Description |
|
|
223
|
+
|-----------|------|----------|-------------|
|
|
224
|
+
| `type` | string | yes | Must be `chart`. |
|
|
225
|
+
| `chart_type` | string | yes | `line`, `bar` (horizontal), `column` (vertical), `pie`, `donut`, or `area`. |
|
|
226
|
+
| `model` | string | yes | Model to query. |
|
|
227
|
+
| `group_by` | string | yes | Field to group by. Inherited default for each series unless overridden. |
|
|
228
|
+
| `group_period` | string | no | Time grouping period for date/datetime `group_by`: `day`, `week`, `month`, `quarter`, `year`. |
|
|
229
|
+
| `aggregate` | string | yes | `count`, `sum`, `avg`, `min`, `max`. Inherited default for each series. |
|
|
230
|
+
| `aggregate_field` | string | no | Field for `sum`/`avg`/`min`/`max`. Required when at least one effective series ends up with aggregate ≠ count. |
|
|
231
|
+
| `scope` | string | no | Named scope on the model. Applied cumulatively with each series' own `scope:`. |
|
|
232
|
+
| `series` | array | no | Multi-series array (see [Series attributes](#series-attributes) below). When absent, top-level keys define an implicit single series. |
|
|
233
|
+
| `stacked` | boolean | no | Stack series on the Y axis. Legal for `column`/`bar`/`line`/`area`; hard error for `pie`/`donut`. |
|
|
234
|
+
| `legend` | boolean / string | no | `auto` (default), `true`, `false`, `top`, `bottom`, `left`, or `right`. `auto` shows the legend for multi-series and all pie/donut, hides it for single-series non-pie. |
|
|
235
|
+
| `colors` | array of strings | no | Chart-level color list. For `column`/`bar`/`line`/`area`, indexes by series; per-series `color:` wins on collision. For `pie`/`donut`, colors slices by category position. |
|
|
236
|
+
| `min`, `max` | number | no | Y axis bounds. Not used for `pie`/`donut`. |
|
|
237
|
+
| `xtitle` | string | no | X axis title (literal). |
|
|
238
|
+
| `xtitle_key` | string | no | i18n key for X axis title. |
|
|
239
|
+
| `ytitle` | string | no | Y axis title (literal). |
|
|
240
|
+
| `ytitle_key` | string | no | i18n key for Y axis title. |
|
|
241
|
+
| `empty` | string | no | Message shown when ALL series have zero rows (literal). |
|
|
242
|
+
| `empty_key` | string | no | i18n key for empty-state message. Falls back to `lcp_ruby.charts.no_data`. |
|
|
243
|
+
| `prefix`, `suffix` | string | no | Value prefix/suffix (e.g. `prefix: "€"`, `suffix: "%"`). |
|
|
244
|
+
| `precision` | integer | no | Decimal places for values. |
|
|
245
|
+
| `thousands` | string | no | Thousands separator (e.g. `" "` for cs-CZ). |
|
|
246
|
+
| `points` | boolean | no | Line/area dot markers. |
|
|
247
|
+
| `curve` | boolean | no | Line smoothing for line/area (default `true`). |
|
|
248
|
+
| `discrete` | boolean | no | Treat X axis as categorical (not numeric). |
|
|
249
|
+
| `sort` | string | no | `asc` (default for date `group_period`), `desc`, `none`, `value_asc`, `value_desc`. For multi-series, `value_*` sorts by per-category sum across all series. |
|
|
250
|
+
| `download` | boolean | no | Render Chartkick's PNG download button. |
|
|
251
|
+
| `height` | string | no | CSS height (e.g. `"320px"`). |
|
|
252
|
+
| `library` | object | no | Raw Chart.js options deep-merged over platform defaults. Escape hatch — has last word. |
|
|
253
|
+
| `label` | string | no | Chart title (literal). |
|
|
254
|
+
| `label_key` | string | no | i18n key for the chart title. |
|
|
255
|
+
| `link_to` | string | no | Page slug for the "view all" link. |
|
|
256
|
+
|
|
257
|
+
#### Series attributes
|
|
258
|
+
|
|
259
|
+
Each element of the chart-level `series:` array supports:
|
|
260
|
+
|
|
261
|
+
| Attribute | Type | Required | Inherited | Description |
|
|
262
|
+
|-----------|------|----------|-----------|-------------|
|
|
263
|
+
| `name` | string | no | — | Legend label (literal). Defaults to `Series N` (1-indexed) when both `name` and `name_key` are absent. |
|
|
264
|
+
| `name_key` | string | no | — | i18n key for the legend label. |
|
|
265
|
+
| `color` | string | no | from `colors[index]` or palette | CSS color string (`#hex`, named, `rgb()`, `hsl()`, `var(--…)`). |
|
|
266
|
+
| `aggregate` | string | no | chart-level | Per-series aggregate. |
|
|
267
|
+
| `aggregate_field` | string | no | chart-level | Required when the effective aggregate is not `count`. |
|
|
268
|
+
| `group_by` | string | no | chart-level | Per-series group field (own-table columns only in v1). |
|
|
269
|
+
| `group_period` | string | no | chart-level | Per-series date period. |
|
|
270
|
+
| `scope` | string | no | — | Named scope on the model. Applied on top of the chart-level `scope:`. |
|
|
271
|
+
|
|
272
|
+
#### Examples
|
|
273
|
+
|
|
274
|
+
Single-series line chart (existing form, unchanged):
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
- name: orders_by_month
|
|
278
|
+
type: widget
|
|
279
|
+
widget:
|
|
280
|
+
type: chart
|
|
281
|
+
chart_type: line
|
|
282
|
+
model: order
|
|
283
|
+
group_by: created_at
|
|
284
|
+
group_period: month
|
|
285
|
+
aggregate: count
|
|
286
|
+
label_key: lcp_ruby.dashboard.orders_by_month
|
|
287
|
+
position: { row: 2, col: 1, width: 8, height: 2 }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Multi-series column chart with different group fields:
|
|
291
|
+
|
|
292
|
+
```yaml
|
|
293
|
+
- name: hires_vs_departures
|
|
294
|
+
type: widget
|
|
295
|
+
widget:
|
|
296
|
+
type: chart
|
|
297
|
+
chart_type: column
|
|
298
|
+
model: employee
|
|
299
|
+
group_by: hire_date
|
|
300
|
+
group_period: year
|
|
301
|
+
aggregate: count
|
|
302
|
+
legend: bottom
|
|
303
|
+
min: 0
|
|
304
|
+
ytitle: "Number of employees"
|
|
305
|
+
series:
|
|
306
|
+
- name: Hires
|
|
307
|
+
color: "#0072B2"
|
|
308
|
+
scope: hired_in_year
|
|
309
|
+
- name: Departures
|
|
310
|
+
color: "#999999"
|
|
311
|
+
scope: departed_in_year
|
|
312
|
+
group_by: departure_date
|
|
313
|
+
position: { row: 1, col: 1, width: 8, height: 2 }
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Stacked column chart with localized series names:
|
|
317
|
+
|
|
318
|
+
```yaml
|
|
319
|
+
- name: turnover_stacked
|
|
320
|
+
type: widget
|
|
321
|
+
widget:
|
|
322
|
+
type: chart
|
|
323
|
+
chart_type: column
|
|
324
|
+
model: employee
|
|
325
|
+
group_by: departure_date
|
|
326
|
+
group_period: month
|
|
327
|
+
aggregate: count
|
|
328
|
+
stacked: true
|
|
329
|
+
legend: bottom
|
|
330
|
+
min: 0
|
|
331
|
+
ytitle_key: lcp_ruby.hr.charts.departures_per_month
|
|
332
|
+
series:
|
|
333
|
+
- name_key: lcp_ruby.hr.charts.series.managed
|
|
334
|
+
color: "#56B4E9"
|
|
335
|
+
scope: managed_departures
|
|
336
|
+
- name_key: lcp_ruby.hr.charts.series.unmanaged
|
|
337
|
+
color: "#E69F00"
|
|
338
|
+
scope: unmanaged_departures
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Single-series area chart with value formatting:
|
|
342
|
+
|
|
343
|
+
```yaml
|
|
344
|
+
- name: monthly_revenue
|
|
345
|
+
type: widget
|
|
346
|
+
widget:
|
|
347
|
+
type: chart
|
|
348
|
+
chart_type: area
|
|
349
|
+
model: invoice
|
|
350
|
+
group_by: issued_at
|
|
351
|
+
group_period: month
|
|
352
|
+
aggregate: sum
|
|
353
|
+
aggregate_field: amount
|
|
354
|
+
prefix: "€"
|
|
355
|
+
thousands: " "
|
|
356
|
+
precision: 0
|
|
357
|
+
min: 0
|
|
358
|
+
ytitle_key: lcp_ruby.finance.charts.revenue
|
|
359
|
+
label_key: lcp_ruby.finance.charts.monthly_revenue
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
For the full feature specification including scope composition, payload shapes, validation rules, and the `library:` escape hatch, see [docs/design/chart_widget_multi_series.md](../design/chart_widget_multi_series.md).
|
|
363
|
+
|
|
364
|
+
### workflow_graph
|
|
365
|
+
|
|
366
|
+
Renders a Mermaid.js `stateDiagram-v2` diagram from the model's workflow definition. Shows all states and transitions including branches, with the current record's state highlighted.
|
|
367
|
+
|
|
368
|
+
| Attribute | Type | Required | Default | Description |
|
|
369
|
+
|-----------|------|----------|---------|-------------|
|
|
370
|
+
| `type` | string | yes | — | Must be `workflow_graph`. |
|
|
371
|
+
| `model` | string | no | page model | Model name to resolve the workflow. Defaults to the page's model. |
|
|
372
|
+
| `direction` | string | no | `LR` | Mermaid direction: `LR` (left-right) or `TD` (top-down). |
|
|
373
|
+
| `show_transition_labels` | boolean | no | `true` | Show transition names on edges. |
|
|
374
|
+
| `show_roles` | boolean | no | `false` | Show allowed roles on transition edges. |
|
|
375
|
+
| `show_guards` | boolean | no | `false` | Show lock indicator on guarded transitions. |
|
|
376
|
+
| `show_audit_trail` | boolean | no | `false` | Highlight the path this record took (requires `audit_log: true` on the workflow). |
|
|
377
|
+
| `highlight_available` | boolean | no | `false` | Highlight transitions available to the current user from the current state. |
|
|
378
|
+
| `collapse_universal` | boolean | no | `true` | Collapse transitions that originate from all states into a note on the target state instead of drawing individual edges. Reduces clutter from "reset" style transitions. |
|
|
379
|
+
| `label_key` | string | no | — | i18n key for the widget label. |
|
|
380
|
+
|
|
381
|
+
```yaml
|
|
382
|
+
# Composite page with workflow graph in sidebar
|
|
383
|
+
page:
|
|
384
|
+
name: purchase_order_show
|
|
385
|
+
model: purchase_order
|
|
386
|
+
layout: composite
|
|
387
|
+
|
|
388
|
+
zones:
|
|
389
|
+
- name: details
|
|
390
|
+
area: main
|
|
391
|
+
type: presenter
|
|
392
|
+
presenter: purchase_order
|
|
393
|
+
|
|
394
|
+
- name: workflow
|
|
395
|
+
area: sidebar
|
|
396
|
+
type: widget
|
|
397
|
+
widget:
|
|
398
|
+
type: workflow_graph
|
|
399
|
+
show_audit_trail: true
|
|
400
|
+
highlight_available: true
|
|
401
|
+
direction: LR
|
|
402
|
+
label_key: lcp_ruby.widgets.workflow_graph.title
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Mermaid.js dependency:** The widget loads Mermaid.js via a `<script>` tag. Configure the source with `config.mermaid_source` — see [Engine Configuration](engine-configuration.md#mermaid_source).
|
|
406
|
+
|
|
407
|
+
**Audit trail overlay:** When `show_audit_trail: true`, traversed edges are annotated with step numbers (e.g., `#1`, `#2`), showing the actual path the record took through the workflow. Requires the workflow audit log model to be set up.
|
|
408
|
+
|
|
409
|
+
## Grid Positioning
|
|
410
|
+
|
|
411
|
+
Grid layout pages use a 12-column CSS grid. Each zone specifies its position:
|
|
412
|
+
|
|
413
|
+
| Key | Type | Description |
|
|
414
|
+
|-----|------|-------------|
|
|
415
|
+
| `row` | integer | Starting grid row (1-based). |
|
|
416
|
+
| `col` | integer | Starting grid column (1-12). |
|
|
417
|
+
| `width` | integer | Number of columns to span. |
|
|
418
|
+
| `height` | integer | Number of rows to span. |
|
|
419
|
+
|
|
420
|
+
All values must be positive integers (>= 1).
|
|
421
|
+
|
|
422
|
+
```yaml
|
|
423
|
+
page:
|
|
424
|
+
name: main_dashboard
|
|
425
|
+
layout: grid
|
|
426
|
+
zones:
|
|
427
|
+
- name: kpi_1
|
|
428
|
+
type: widget
|
|
429
|
+
widget: { type: kpi_card, model: order, aggregate: count }
|
|
430
|
+
position: { row: 1, col: 1, width: 4, height: 1 }
|
|
431
|
+
- name: kpi_2
|
|
432
|
+
type: widget
|
|
433
|
+
widget: { type: kpi_card, model: order, aggregate: sum, aggregate_field: total }
|
|
434
|
+
position: { row: 1, col: 5, width: 4, height: 1 }
|
|
435
|
+
- name: main_content
|
|
436
|
+
presenter: orders
|
|
437
|
+
position: { row: 2, col: 1, width: 12, height: 3 }
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Auto-Page Creation
|
|
441
|
+
|
|
442
|
+
Presenters that are not explicitly claimed by any page zone automatically get a simple page created at boot:
|
|
443
|
+
|
|
444
|
+
- Page name = presenter name
|
|
445
|
+
- Single presenter zone with `area: main`
|
|
446
|
+
- Slug inherited from the presenter
|
|
447
|
+
- `auto_generated: true` flag (skipped by schema validation)
|
|
448
|
+
|
|
449
|
+
This means every presenter is accessible without explicit page YAML. Explicit pages override auto-generated ones by claiming the presenter in a zone.
|
|
450
|
+
|
|
451
|
+
## Slug Ownership
|
|
452
|
+
|
|
453
|
+
- Explicit pages with a `slug:` take priority.
|
|
454
|
+
- Auto-generated pages inherit the presenter's slug.
|
|
455
|
+
- Duplicate slugs across pages produce a validation error.
|
|
456
|
+
|
|
457
|
+
## Standalone Pages
|
|
458
|
+
|
|
459
|
+
Pages without a `model:` are standalone — they are not tied to any single model. Standalone dashboard pages can contain widget zones referencing multiple different models.
|
|
460
|
+
|
|
461
|
+
```yaml
|
|
462
|
+
page:
|
|
463
|
+
name: main_dashboard
|
|
464
|
+
slug: dashboard
|
|
465
|
+
layout: grid
|
|
466
|
+
zones:
|
|
467
|
+
- name: orders_count
|
|
468
|
+
type: widget
|
|
469
|
+
widget: { type: kpi_card, model: order, aggregate: count }
|
|
470
|
+
position: { row: 1, col: 1, width: 6, height: 1 }
|
|
471
|
+
- name: tasks_count
|
|
472
|
+
type: widget
|
|
473
|
+
widget: { type: kpi_card, model: task, aggregate: count }
|
|
474
|
+
position: { row: 1, col: 7, width: 6, height: 1 }
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
## Dialog-Only Pages
|
|
478
|
+
|
|
479
|
+
Pages with `dialog:` config but no `slug:` are dialog-only — they cannot be navigated to directly but can be opened as modal dialogs by actions.
|
|
480
|
+
|
|
481
|
+
```yaml
|
|
482
|
+
page:
|
|
483
|
+
name: feedback_form
|
|
484
|
+
dialog:
|
|
485
|
+
size: medium
|
|
486
|
+
closable: true
|
|
487
|
+
title_key: lcp_ruby.dialogs.feedback
|
|
488
|
+
zones:
|
|
489
|
+
- name: main
|
|
490
|
+
presenter: feedback_create
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## Composite Pages (Semantic Layout)
|
|
494
|
+
|
|
495
|
+
Composite pages combine multiple zones into a record-bound layout with named areas: `main`, `tabs`, `sidebar`, and `below`. They use semantic layout (the default) to arrange zones into a structured detail page — for example, an employee detail page with a header, tabbed related records, and a sidebar.
|
|
496
|
+
|
|
497
|
+
A page is composite when it:
|
|
498
|
+
- Is explicitly defined (not auto-generated)
|
|
499
|
+
- Has more than one zone
|
|
500
|
+
- Is not standalone (has a `model:`)
|
|
501
|
+
|
|
502
|
+
### Areas
|
|
503
|
+
|
|
504
|
+
| Area | Description |
|
|
505
|
+
|------|-------------|
|
|
506
|
+
| `main` | Primary content area. Typically renders a show presenter with record details. |
|
|
507
|
+
| `tabs` | Tabbed content below the main area. Each tab zone becomes a tab in the tab bar. Only the active tab's data is loaded. |
|
|
508
|
+
| `sidebar` | Side panel alongside the main content. |
|
|
509
|
+
| `below` | Full-width area below the tabs. |
|
|
510
|
+
|
|
511
|
+
### Zone Attributes for Composite Pages
|
|
512
|
+
|
|
513
|
+
In addition to the [standard zone attributes](#zone-attributes), composite page zones support:
|
|
514
|
+
|
|
515
|
+
| Attribute | Type | Required | Default | Description |
|
|
516
|
+
|-----------|------|----------|---------|-------------|
|
|
517
|
+
| `scope_context` | object | no | — | Maps column names on the zone's model to dynamic references resolved from the parent record or request context. |
|
|
518
|
+
| `label_key` | string | no | — | i18n key for the zone's tab label. Recommended for tab zones. Falls back to humanized zone name. |
|
|
519
|
+
|
|
520
|
+
**Note:** The `filters`, `default_scope`, `width`, and `record_click` attributes from the [standard zone attributes](#zone-attributes) are especially useful in composite pages. Tab zones default to `filters: quick` (quick search), and `record_click: dialog` is useful for opening related records in a dialog without leaving the composite page.
|
|
521
|
+
|
|
522
|
+
### scope_context
|
|
523
|
+
|
|
524
|
+
`scope_context` scopes a zone's data query to the parent record. Each key is a column name on the zone's model, and each value is either a static value or a dynamic reference prefixed with `:`.
|
|
525
|
+
|
|
526
|
+
**Dynamic references:**
|
|
527
|
+
|
|
528
|
+
| Reference | Resolves to |
|
|
529
|
+
|-----------|-------------|
|
|
530
|
+
| `:record` | The parent record |
|
|
531
|
+
| `:record.<field>` | A field value from the parent record (e.g. `:record.id`, `:record.department_id`) |
|
|
532
|
+
| `:current_user` | The full `current_user` object (useful with `filter_*` interceptors) |
|
|
533
|
+
| `:current_user.<method>` | A method value on the user (e.g. `:current_user.id`, `:current_user.organization_id`) |
|
|
534
|
+
| `:selection_id` | The selected record's `id` from `?selected=N` (master-detail only) |
|
|
535
|
+
| `:current_year` | The current year (integer) |
|
|
536
|
+
| `:current_date` | Today's date |
|
|
537
|
+
|
|
538
|
+
Static values (without `:` prefix) pass through unchanged.
|
|
539
|
+
|
|
540
|
+
This is the unified dotted grammar — see [Dynamic References](dynamic-references.md) for the runtime contract and boot-time validation guarantees.
|
|
541
|
+
|
|
542
|
+
**Dot-path depth limit:** Only single-level dot-paths are supported (e.g. `:record.department_id`). Multi-level paths like `:record.department.company_id` raise a `MetadataError` at boot.
|
|
543
|
+
|
|
544
|
+
```yaml
|
|
545
|
+
# Scope leave requests to the current employee
|
|
546
|
+
scope_context:
|
|
547
|
+
employee_id: ":record.id"
|
|
548
|
+
|
|
549
|
+
# Scope by a field on the parent record
|
|
550
|
+
scope_context:
|
|
551
|
+
department_id: ":record.department_id"
|
|
552
|
+
|
|
553
|
+
# Scope to current user
|
|
554
|
+
scope_context:
|
|
555
|
+
assigned_to_id: ":current_user.id"
|
|
556
|
+
|
|
557
|
+
# Static value
|
|
558
|
+
scope_context:
|
|
559
|
+
year: 2024
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
**Scope application:** Each `scope_context` key is applied to the zone's query in this order:
|
|
563
|
+
|
|
564
|
+
1. If the zone's model defines a `filter_<key>` class method, that method is called. The method may accept 2 arguments `(scope, value)` or 3 arguments `(scope, value, evaluator)` — both signatures are supported.
|
|
565
|
+
2. Otherwise, if the key is a column name on the model, a `where(key => value)` clause is applied.
|
|
566
|
+
3. If neither exists, a warning is logged and the key is skipped.
|
|
567
|
+
|
|
568
|
+
### Zone Filters
|
|
569
|
+
|
|
570
|
+
The `filters` attribute controls the search/filtering UI rendered inside a zone.
|
|
571
|
+
|
|
572
|
+
| Value | Behavior |
|
|
573
|
+
|-------|----------|
|
|
574
|
+
| `quick` | Quick search text input. Searches across string/text fields using `LIKE` queries. |
|
|
575
|
+
| `full` | Quick search + advanced filter pipeline (Ransack, custom filter interception, custom field filters). |
|
|
576
|
+
| `none` | No filtering UI. |
|
|
577
|
+
|
|
578
|
+
**Defaults:** `quick` for tab zones, `none` for all other areas. Widget zones must always use `none`.
|
|
579
|
+
|
|
580
|
+
The quick search form is rendered inside the zone's Turbo Frame, so searching does not cause a full page reload. The `qs` parameter is preserved across pagination.
|
|
581
|
+
|
|
582
|
+
### Zone Record Click
|
|
583
|
+
|
|
584
|
+
The `record_click` attribute controls what happens when a user clicks a row in a presenter zone's table.
|
|
585
|
+
|
|
586
|
+
| Value | Behavior |
|
|
587
|
+
|-------|----------|
|
|
588
|
+
| `navigate` | Full-page navigation to the record's show page (default). |
|
|
589
|
+
| `dialog` | Opens the record's show page in a modal dialog (via `?_dialog=1`). Keeps the user on the composite page. |
|
|
590
|
+
| `none` | No click action. Rows are not clickable. |
|
|
591
|
+
|
|
592
|
+
When `record_click` is `navigate` or `dialog`, rows get `data-href` attributes pointing to the record's show page (resolved from the zone presenter's slug). If the presenter has no routable slug, rows degrade to non-clickable regardless of the setting.
|
|
593
|
+
|
|
594
|
+
### Zone Default Scope
|
|
595
|
+
|
|
596
|
+
The `default_scope` attribute applies a named scope before the zone's `scope` attribute. Both are cumulative — if both are set, the query is narrowed by `default_scope` first, then further narrowed by `scope`.
|
|
597
|
+
|
|
598
|
+
```yaml
|
|
599
|
+
zones:
|
|
600
|
+
- name: active_employees
|
|
601
|
+
presenter: employees_index
|
|
602
|
+
area: tabs
|
|
603
|
+
default_scope: active # applied first
|
|
604
|
+
scope: recent # applied second (on top of active)
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Zone Width
|
|
608
|
+
|
|
609
|
+
The `width` attribute (1–12) controls the CSS grid column ratio in semantic layouts. It is primarily used to adjust the main/sidebar proportions:
|
|
610
|
+
|
|
611
|
+
```yaml
|
|
612
|
+
zones:
|
|
613
|
+
- name: header
|
|
614
|
+
presenter: employee_show
|
|
615
|
+
area: main
|
|
616
|
+
width: 9 # 9 of 12 columns for main
|
|
617
|
+
|
|
618
|
+
- name: sidebar_info
|
|
619
|
+
presenter: employee_summary
|
|
620
|
+
area: sidebar
|
|
621
|
+
width: 3 # 3 of 12 columns for sidebar
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
When omitted, the default is 8 columns for main and 4 for sidebar. Width has no effect on tab or below zones (they always span the full width).
|
|
625
|
+
|
|
626
|
+
### Main Zone Selection
|
|
627
|
+
|
|
628
|
+
The main zone is selected automatically with this priority:
|
|
629
|
+
|
|
630
|
+
1. First presenter zone with `area: main`
|
|
631
|
+
2. First presenter zone (any area)
|
|
632
|
+
3. First zone of any type
|
|
633
|
+
|
|
634
|
+
### Tab Navigation
|
|
635
|
+
|
|
636
|
+
Tab zones render as a tab bar with links. The first tab is active by default. Tab switching is handled via **Turbo Frames** — clicking a tab loads the zone content without a full page reload.
|
|
637
|
+
|
|
638
|
+
Each tab link points to the zone endpoint (`GET /:slug/:id/zones/:zone_name`) and targets the shared `lcp-zone-tab-content` Turbo Frame. The URL is updated to `?tab=<zone_name>` via `history.replaceState` for bookmarkability.
|
|
639
|
+
|
|
640
|
+
Tab content is always loaded from the zone endpoint (even for the initially active tab). This ensures that all links inside the frame (pagination, sort) resolve to the zone endpoint URL.
|
|
641
|
+
|
|
642
|
+
### Zone Endpoint
|
|
643
|
+
|
|
644
|
+
`GET /:slug/:id/zones/:zone_name` renders a single zone's content wrapped in a `<turbo-frame>`.
|
|
645
|
+
|
|
646
|
+
**Response:** A `<turbo-frame>` fragment (no full page layout). The frame ID is:
|
|
647
|
+
- `lcp-zone-tab-content` for tab zones
|
|
648
|
+
- `lcp-zone-{zone_name}` for sidebar/below zones
|
|
649
|
+
|
|
650
|
+
**Parameters:**
|
|
651
|
+
- `page` — Kaminari pagination page number
|
|
652
|
+
- `sort` — Sort column name (must be a valid column)
|
|
653
|
+
- `direction` — `asc` or `desc` (default: `asc`)
|
|
654
|
+
- `qs` — Quick search query string (only when zone has `filters: quick` or `filters: full`)
|
|
655
|
+
- `f[...]` — Ransack filter parameters (only when zone has `filters: full`)
|
|
656
|
+
- `cf[...]` — Custom field filter parameters (only when zone has `filters: full`)
|
|
657
|
+
|
|
658
|
+
**Authorization:** The endpoint checks `show?` on the parent record, zone visibility (`visible_when`), and per-zone presenter access. Returns 404 for unknown or inaccessible zones.
|
|
659
|
+
|
|
660
|
+
### Sidebar/Below Zones
|
|
661
|
+
|
|
662
|
+
Sidebar and below zones are rendered eagerly (server-side) and wrapped in `<turbo-frame id="lcp-zone-{name}">` tags without a `src` attribute. This enables `reload_zone` support — after a dialog action with `on_success: reload_zone`, the frame's `src` is set to trigger a lazy reload via the zone endpoint.
|
|
663
|
+
|
|
664
|
+
### Per-Zone Authorization
|
|
665
|
+
|
|
666
|
+
Each presenter zone checks whether the current user has presenter access for that zone's presenter. If the user lacks access (via the `presenters` key in permissions YAML), the zone is completely hidden — no tab link appears in the tab bar and no data is loaded.
|
|
667
|
+
|
|
668
|
+
This differs from `visible_when`, which also hides zones but is based on conditions (role, field values) rather than presenter permissions. Both mechanisms produce the same result: the zone is excluded from rendering.
|
|
669
|
+
|
|
670
|
+
### Show in Dialog
|
|
671
|
+
|
|
672
|
+
When a presenter zone has `record_click: dialog`, clicking a row opens the record's show page inside a modal dialog. The dialog renders a read-only view of the record's show sections (from the record's own presenter layout) in a dialog frame with a header and close button.
|
|
673
|
+
|
|
674
|
+
The dialog is loaded via `GET /:slug/:id?_dialog=1`, which returns a `<turbo-frame>` fragment without the full page layout. This keeps the user on the composite page while viewing related record details.
|
|
675
|
+
|
|
676
|
+
### Composite Page Example
|
|
677
|
+
|
|
678
|
+
```yaml
|
|
679
|
+
page:
|
|
680
|
+
name: employee_detail
|
|
681
|
+
model: employee
|
|
682
|
+
slug: employees
|
|
683
|
+
zones:
|
|
684
|
+
- name: header
|
|
685
|
+
presenter: employee_show
|
|
686
|
+
area: main
|
|
687
|
+
width: 8
|
|
688
|
+
|
|
689
|
+
- name: leave_requests
|
|
690
|
+
presenter: leave_requests_index
|
|
691
|
+
area: tabs
|
|
692
|
+
label_key: pages.employee_detail.tabs.leave_requests
|
|
693
|
+
scope_context:
|
|
694
|
+
employee_id: ":record.id"
|
|
695
|
+
default_scope: recent
|
|
696
|
+
filters: quick
|
|
697
|
+
record_click: dialog
|
|
698
|
+
|
|
699
|
+
- name: trainings
|
|
700
|
+
presenter: trainings_index
|
|
701
|
+
area: tabs
|
|
702
|
+
label_key: pages.employee_detail.tabs.trainings
|
|
703
|
+
scope_context:
|
|
704
|
+
employee_id: ":record.id"
|
|
705
|
+
filters: full
|
|
706
|
+
record_click: navigate
|
|
707
|
+
|
|
708
|
+
- name: notes
|
|
709
|
+
presenter: employee_notes
|
|
710
|
+
area: sidebar
|
|
711
|
+
width: 4
|
|
712
|
+
scope_context:
|
|
713
|
+
employee_id: ":record.id"
|
|
714
|
+
record_click: none
|
|
715
|
+
|
|
716
|
+
- name: activity_log
|
|
717
|
+
presenter: audit_entries
|
|
718
|
+
area: below
|
|
719
|
+
scope_context:
|
|
720
|
+
record_id: ":record.id"
|
|
721
|
+
visible_when:
|
|
722
|
+
role: admin
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
This renders:
|
|
726
|
+
- **Main area** (8 columns): Employee detail (show presenter)
|
|
727
|
+
- **Tab bar**: "Leave requests" (quick search, dialog click, recent records first) and "Trainings" (full search, navigates to record) tabs, each scoped to this employee
|
|
728
|
+
- **Sidebar** (4 columns): Notes panel scoped to this employee, non-clickable rows
|
|
729
|
+
- **Below**: Activity log visible only to admins
|
|
730
|
+
|
|
731
|
+
### Widget Zones with scope_context
|
|
732
|
+
|
|
733
|
+
Widget zones on composite pages can also use `scope_context` to scope their data to the parent record:
|
|
734
|
+
|
|
735
|
+
```yaml
|
|
736
|
+
zones:
|
|
737
|
+
- name: header
|
|
738
|
+
presenter: employee_show
|
|
739
|
+
area: main
|
|
740
|
+
|
|
741
|
+
- name: leave_count
|
|
742
|
+
type: widget
|
|
743
|
+
area: sidebar
|
|
744
|
+
widget:
|
|
745
|
+
type: kpi_card
|
|
746
|
+
model: leave_request
|
|
747
|
+
aggregate: count
|
|
748
|
+
label_key: pages.employee.sidebar.leave_count
|
|
749
|
+
scope_context:
|
|
750
|
+
employee_id: ":record.id"
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
### Index-Level Composite Pages
|
|
754
|
+
|
|
755
|
+
A composite page where the main zone's presenter has `table_columns` is detected as an **index composite**. The main area renders a full index table (with search, filtering, sorting, pagination) while sidebar and below zones display contextual widgets.
|
|
756
|
+
|
|
757
|
+
```yaml
|
|
758
|
+
page:
|
|
759
|
+
name: orders_overview
|
|
760
|
+
model: order
|
|
761
|
+
slug: orders
|
|
762
|
+
zones:
|
|
763
|
+
- name: orders_table
|
|
764
|
+
presenter: orders_index # must have table_columns
|
|
765
|
+
area: main
|
|
766
|
+
|
|
767
|
+
- name: total_orders
|
|
768
|
+
type: widget
|
|
769
|
+
area: sidebar
|
|
770
|
+
widget:
|
|
771
|
+
type: kpi_card
|
|
772
|
+
model: order
|
|
773
|
+
aggregate: count
|
|
774
|
+
label_key: lcp_ruby.orders.total
|
|
775
|
+
|
|
776
|
+
- name: help_text
|
|
777
|
+
type: widget
|
|
778
|
+
area: below
|
|
779
|
+
widget:
|
|
780
|
+
type: text
|
|
781
|
+
content_key: lcp_ruby.orders.help
|
|
782
|
+
visible_when:
|
|
783
|
+
role: admin
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Detection:** `PageDefinition#index_composite?` returns `true` when the page is `composite?` AND the main zone's presenter has `table_columns` defined.
|
|
787
|
+
|
|
788
|
+
**Constraints specific to index composites:**
|
|
789
|
+
- **No tab zones.** Tab zones are not allowed because they use query parameters (`page`, `sort`, `qs`) that collide with the index table's parameters.
|
|
790
|
+
- **No `:record.*` in `scope_context`.** Index composites render a collection, not a single record. Use model-level scopes or `:current_user.id` instead.
|
|
791
|
+
|
|
792
|
+
### Master-Detail Pages
|
|
793
|
+
|
|
794
|
+
A master-detail page is a specialized index composite where the sidebar contains a **selection zone** (a clickable list or tree) and the main area shows the detail of the selected record. Tabs and below zones are scoped to the selection via `:selection_id`.
|
|
795
|
+
|
|
796
|
+
```yaml
|
|
797
|
+
page:
|
|
798
|
+
name: department_explorer
|
|
799
|
+
model: department
|
|
800
|
+
slug: department-explorer
|
|
801
|
+
zones:
|
|
802
|
+
# Sidebar: clickable selection list (rendered by the index pipeline)
|
|
803
|
+
- name: department_list
|
|
804
|
+
presenter: dept_list_zone # index presenter with table_columns
|
|
805
|
+
area: sidebar
|
|
806
|
+
width: 3 # sidebar proportion (1-12)
|
|
807
|
+
selection: single # enables row selection
|
|
808
|
+
record_click: none # prevents row navigation (selection handles clicks)
|
|
809
|
+
|
|
810
|
+
# Main: detail of the selected record
|
|
811
|
+
- name: department_detail
|
|
812
|
+
presenter: dept_detail_zone # show presenter
|
|
813
|
+
area: main
|
|
814
|
+
width: 9 # main proportion (1-12)
|
|
815
|
+
record_source: selection # loads record from ?selected=N
|
|
816
|
+
|
|
817
|
+
# Tab: scoped to selected record
|
|
818
|
+
- name: employees
|
|
819
|
+
presenter: employees_zone
|
|
820
|
+
area: tabs
|
|
821
|
+
label_key: lcp_ruby.pages.dept_explorer.tabs.employees
|
|
822
|
+
scope_context:
|
|
823
|
+
department_id: ":selection_id"
|
|
824
|
+
|
|
825
|
+
# Widget: scoped to selected record
|
|
826
|
+
- name: employee_kpi
|
|
827
|
+
type: widget
|
|
828
|
+
area: below
|
|
829
|
+
widget:
|
|
830
|
+
type: kpi_card
|
|
831
|
+
model: employee
|
|
832
|
+
aggregate: count
|
|
833
|
+
scope_context:
|
|
834
|
+
department_id: ":selection_id"
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
**Key attributes:**
|
|
838
|
+
|
|
839
|
+
| Attribute | Zone | Description |
|
|
840
|
+
|-----------|------|-------------|
|
|
841
|
+
| `selection: single` | sidebar | Marks the zone as the selection list. Rows get `click->lcp-selection#select` wiring. Only valid on `sidebar` presenter zones. |
|
|
842
|
+
| `record_click: none` | sidebar | Prevents default row navigation — selection handles clicks instead. |
|
|
843
|
+
| `record_source: selection` | main | Loads the record identified by `?selected=N` from the page's primary model. |
|
|
844
|
+
| `:selection_id` | tabs/below | Dynamic reference in `scope_context` resolving to the selected record's ID. |
|
|
845
|
+
|
|
846
|
+
**Layout:** In master-detail mode, the sidebar renders on the **left** and the main area on the **right** (reversed from standard composite pages). The `width` values on sidebar and main zones control the column ratio — e.g., `width: 3` + `width: 9` produces `3fr 9fr`.
|
|
847
|
+
|
|
848
|
+
**Tree support:** If the selection zone's presenter uses `tree_view: true`, the sidebar renders as a collapsible tree instead of a flat table. The tree rows are clickable for selection just like table rows.
|
|
849
|
+
|
|
850
|
+
**Empty state:** When no record is selected (`?selected` absent), the main area shows an empty state message ("Select a record to view details"), and tabs/below zones are hidden. The sidebar always renders regardless of selection.
|
|
851
|
+
|
|
852
|
+
**Actions column:** The Actions column is automatically hidden in the selection zone sidebar to keep it compact.
|
|
853
|
+
|
|
854
|
+
**Detection:** `PageDefinition#master_detail?` returns `true` when the page is `index_composite?` AND at least one zone has `selection: single`.
|
|
855
|
+
|
|
856
|
+
**Constraints specific to master-detail:**
|
|
857
|
+
- `selection: single` is only valid on presenter zones in `sidebar` area
|
|
858
|
+
- `record_source: selection` is only valid when the page has a zone with `selection: single`
|
|
859
|
+
- Page must have a model (standalone pages cannot use master-detail)
|
|
860
|
+
|
|
861
|
+
### Composite Dialog Pages
|
|
862
|
+
|
|
863
|
+
Dialog-only pages with multiple zones render all zones inline in a single response — no lazy loading or Turbo Frames inside the dialog.
|
|
864
|
+
|
|
865
|
+
```yaml
|
|
866
|
+
page:
|
|
867
|
+
name: transfer_dialog
|
|
868
|
+
model: inventory_transfer
|
|
869
|
+
dialog:
|
|
870
|
+
size: large
|
|
871
|
+
zones:
|
|
872
|
+
- name: transfer_form
|
|
873
|
+
presenter: transfer_form
|
|
874
|
+
area: main
|
|
875
|
+
width: 8
|
|
876
|
+
|
|
877
|
+
- name: source_item
|
|
878
|
+
presenter: item_show
|
|
879
|
+
area: sidebar
|
|
880
|
+
width: 4
|
|
881
|
+
record_source:
|
|
882
|
+
param: source_id
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
Tab zones in composite dialogs render as stacked sections (not switchable tabs). `scope_context` with `:record.*` is not allowed since dialog-only pages have no primary record.
|
|
886
|
+
|
|
887
|
+
See the [Dialogs Guide](../guides/dialogs.md#composite-dialog-pages) for triggering and workflow details.
|
|
888
|
+
|
|
889
|
+
### Constraints
|
|
890
|
+
|
|
891
|
+
The configuration validator enforces these rules at boot time:
|
|
892
|
+
|
|
893
|
+
- **Main zone must not be an index presenter when tabs are present.** Index presenters use `page`, `sort`, and `q[...]` query parameters that collide with tab parameters. Use a show presenter for the main zone.
|
|
894
|
+
- **Index composites must not have tab zones.** Tabs are not supported on index composite pages (query parameter collisions).
|
|
895
|
+
- **No `:record.*` in index composites or dialog-only pages.** These pages have no primary record. Use model-level scopes, `:current_user.id`, or `record_source` instead.
|
|
896
|
+
- **Main zone model should match the page model.** A warning is produced if the main zone's presenter references a different model than the page's `model:` attribute.
|
|
897
|
+
- **Tab zones should have `label_key`.** Tabs fall back to the humanized zone name, but explicit labels are recommended.
|
|
898
|
+
- **Max zones per page.** Warning when a page has more than 10 zones, error above 20 (auto-generated pages are exempt).
|
|
899
|
+
- **Zone `width` must be 1–12** when specified.
|
|
900
|
+
- **Zone `default_scope` must exist** as a named scope on the zone's model.
|
|
901
|
+
- **Widget zones cannot have `filters`** other than `none`.
|
|
902
|
+
- **Record click without slug warns.** A warning is produced when `record_click` is not `none` but the zone's presenter has no routable slug.
|
|
903
|
+
- **Unreachable presenters.** A boot-time warning is logged when a presenter's auto-page has no slug, the presenter is not used in any explicit page zone, and is not dialog-only.
|
|
904
|
+
|
|
905
|
+
### Claimed Presenters
|
|
906
|
+
|
|
907
|
+
Presenters used as zones in a composite page are "claimed" and do not get auto-generated pages. This prevents duplicate routes — the composite page owns the rendering for those presenters.
|
|
908
|
+
|
|
909
|
+
## Form Zones
|
|
910
|
+
|
|
911
|
+
A form zone renders an edit form in the main area of a composite page instead of a read-only show view. After a successful save, the user stays on the composite page.
|
|
912
|
+
|
|
913
|
+
```yaml
|
|
914
|
+
page:
|
|
915
|
+
name: order_detail
|
|
916
|
+
model: order
|
|
917
|
+
slug: orders
|
|
918
|
+
zones:
|
|
919
|
+
- name: order_form
|
|
920
|
+
presenter: order_edit # must have form sections defined
|
|
921
|
+
area: main
|
|
922
|
+
submit_redirect: self # redirect back to this composite page after save
|
|
923
|
+
|
|
924
|
+
- name: order_lines
|
|
925
|
+
presenter: order_lines_index
|
|
926
|
+
area: below
|
|
927
|
+
scope_context:
|
|
928
|
+
order_id: ":record.id"
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
**Constraints:**
|
|
932
|
+
- `submit_redirect` must be `self` (the only supported value)
|
|
933
|
+
- Must be on a presenter zone in the `main` area
|
|
934
|
+
- At most one form zone per page
|
|
935
|
+
- The presenter must have `form.sections` defined
|
|
936
|
+
- Users need both `show` and `update` permissions to access the page
|
|
937
|
+
|
|
938
|
+
On validation error, the composite page re-renders with the form errors and all other zones (sidebar, tabs, below) preserved.
|
|
939
|
+
|
|
940
|
+
## Record Source Zones
|
|
941
|
+
|
|
942
|
+
A `record_source` zone loads a **single record** by ID from a URL parameter, instead of loading a collection. The zone renders a show view of that record.
|
|
943
|
+
|
|
944
|
+
```yaml
|
|
945
|
+
page:
|
|
946
|
+
name: transfer_dialog
|
|
947
|
+
model: inventory_transfer
|
|
948
|
+
dialog:
|
|
949
|
+
size: large
|
|
950
|
+
zones:
|
|
951
|
+
- name: source_item
|
|
952
|
+
presenter: inventory_item_show
|
|
953
|
+
area: sidebar
|
|
954
|
+
width: 4
|
|
955
|
+
record_source:
|
|
956
|
+
param: source_id # loads from ?source_id=N
|
|
957
|
+
|
|
958
|
+
- name: transfer_form
|
|
959
|
+
presenter: transfer_form
|
|
960
|
+
area: main
|
|
961
|
+
width: 8
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
**Behavior:**
|
|
965
|
+
- The zone renders the presenter's show sections (reusing the standard `_show_sections` partial with all features: conditional visibility, section types, renderers, copyable fields, etc.)
|
|
966
|
+
- Eager loading is applied automatically based on the presenter's association dependencies
|
|
967
|
+
- Authorization checks: user must have `show` permission on the zone's model and access to the zone's presenter
|
|
968
|
+
|
|
969
|
+
**Constraints:**
|
|
970
|
+
- `record_source` requires a `param` key specifying the URL parameter name
|
|
971
|
+
- Must be on a presenter zone
|
|
972
|
+
- Mutually exclusive with `scope_context` (a zone either loads one record OR filters a collection)
|
|
973
|
+
- When the parameter is missing or the record is not found, the zone is hidden
|
|
974
|
+
|
|
975
|
+
## Conditional Zone Visibility
|
|
976
|
+
|
|
977
|
+
Use `visible_when` to show zones only to specific users.
|
|
978
|
+
|
|
979
|
+
### Role Shortcut
|
|
980
|
+
|
|
981
|
+
```yaml
|
|
982
|
+
visible_when:
|
|
983
|
+
role: admin
|
|
984
|
+
|
|
985
|
+
# or multiple roles
|
|
986
|
+
visible_when:
|
|
987
|
+
role: [admin, manager]
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
### Condition Object
|
|
991
|
+
|
|
992
|
+
Full condition expressions (same syntax as presenter `visible_when`):
|
|
993
|
+
|
|
994
|
+
```yaml
|
|
995
|
+
visible_when:
|
|
996
|
+
field: status
|
|
997
|
+
operator: eq
|
|
998
|
+
value: active
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
## Complete Example
|
|
1002
|
+
|
|
1003
|
+
```yaml
|
|
1004
|
+
page:
|
|
1005
|
+
name: main_dashboard
|
|
1006
|
+
slug: dashboard
|
|
1007
|
+
layout: grid
|
|
1008
|
+
title_key: lcp_ruby.dashboard.title
|
|
1009
|
+
zones:
|
|
1010
|
+
- name: order_count
|
|
1011
|
+
type: widget
|
|
1012
|
+
widget:
|
|
1013
|
+
type: kpi_card
|
|
1014
|
+
model: order
|
|
1015
|
+
aggregate: count
|
|
1016
|
+
icon: shopping-cart
|
|
1017
|
+
link_to: orders
|
|
1018
|
+
position: { row: 1, col: 1, width: 4, height: 1 }
|
|
1019
|
+
|
|
1020
|
+
- name: total_revenue
|
|
1021
|
+
type: widget
|
|
1022
|
+
widget:
|
|
1023
|
+
type: kpi_card
|
|
1024
|
+
model: order
|
|
1025
|
+
aggregate: sum
|
|
1026
|
+
aggregate_field: total_amount
|
|
1027
|
+
format: currency
|
|
1028
|
+
icon: dollar-sign
|
|
1029
|
+
position: { row: 1, col: 5, width: 4, height: 1 }
|
|
1030
|
+
|
|
1031
|
+
- name: open_tasks
|
|
1032
|
+
type: widget
|
|
1033
|
+
widget:
|
|
1034
|
+
type: kpi_card
|
|
1035
|
+
model: task
|
|
1036
|
+
aggregate: count
|
|
1037
|
+
icon: check-square
|
|
1038
|
+
scope: open
|
|
1039
|
+
position: { row: 1, col: 9, width: 4, height: 1 }
|
|
1040
|
+
visible_when:
|
|
1041
|
+
role: [admin, manager]
|
|
1042
|
+
|
|
1043
|
+
- name: welcome_text
|
|
1044
|
+
type: widget
|
|
1045
|
+
widget:
|
|
1046
|
+
type: text
|
|
1047
|
+
content_key: lcp_ruby.dashboard.welcome
|
|
1048
|
+
position: { row: 2, col: 1, width: 12, height: 1 }
|
|
1049
|
+
|
|
1050
|
+
- name: recent_tasks
|
|
1051
|
+
presenter: dashboard_tasks
|
|
1052
|
+
scope: recent
|
|
1053
|
+
limit: 5
|
|
1054
|
+
position: { row: 3, col: 1, width: 6, height: 2 }
|
|
1055
|
+
|
|
1056
|
+
- name: recent_orders
|
|
1057
|
+
presenter: dashboard_orders
|
|
1058
|
+
scope: recent
|
|
1059
|
+
limit: 5
|
|
1060
|
+
position: { row: 3, col: 7, width: 6, height: 2 }
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
## Page Filters
|
|
1064
|
+
|
|
1065
|
+
Two parallel YAML blocks define user-facing filter controls at the top
|
|
1066
|
+
of a dashboard or index composite page:
|
|
1067
|
+
|
|
1068
|
+
- **`filter_form:`** — value-based filters (text, select, multi-select,
|
|
1069
|
+
date range, association select with cascading dropdowns). Each entry
|
|
1070
|
+
is a form-syntax field that renders through the same `form_helper`
|
|
1071
|
+
pipeline as presenter forms.
|
|
1072
|
+
- **`scope_filters:`** — predefined AR-scope toggles (e.g. `active` vs.
|
|
1073
|
+
`archived`).
|
|
1074
|
+
|
|
1075
|
+
**See the dedicated [Page Filters reference](page_filters.md) for full
|
|
1076
|
+
syntax, the `auto_submit:` derivation table, cascading associations,
|
|
1077
|
+
and the `/select_options` fallback chain.**
|
|
1078
|
+
|
|
1079
|
+
> **How this relates to presenter search:** Presenter-level
|
|
1080
|
+
> [search parameters](presenters.md#search-parameters) add typed filter
|
|
1081
|
+
> controls (dropdowns, toggles, ranges) directly in a single presenter's
|
|
1082
|
+
> toolbar. **Page filters** apply to **all zones** on the page at
|
|
1083
|
+
> once. Use presenter search parameters for single-presenter index
|
|
1084
|
+
> pages; use page filters for composite pages or dashboards where
|
|
1085
|
+
> multiple zones share the same filter.
|
|
1086
|
+
|
|
1087
|
+
### Minimal example
|
|
1088
|
+
|
|
1089
|
+
```yaml
|
|
1090
|
+
page:
|
|
1091
|
+
name: workforce_overview
|
|
1092
|
+
model: employee
|
|
1093
|
+
slug: workforce
|
|
1094
|
+
filter_form:
|
|
1095
|
+
- field: department_id
|
|
1096
|
+
input_type: association_select
|
|
1097
|
+
model: department
|
|
1098
|
+
label_key: lcp_ruby.pages.workforce.filters.department
|
|
1099
|
+
- field: hired_at
|
|
1100
|
+
input_type: date_range
|
|
1101
|
+
scope_filters:
|
|
1102
|
+
- name: status
|
|
1103
|
+
scopes:
|
|
1104
|
+
active: Active
|
|
1105
|
+
inactive: Inactive
|
|
1106
|
+
default: active
|
|
1107
|
+
|
|
1108
|
+
zones:
|
|
1109
|
+
- { name: employees_table, presenter: employees_index, area: main }
|
|
1110
|
+
- { name: headcount, type: widget, area: sidebar,
|
|
1111
|
+
widget: { type: kpi_card, model: employee, aggregate: count } }
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
The legacy `parameters:` block was retired — the platform boots with
|
|
1115
|
+
`MetadataError` if it encounters one, pointing at the migration
|
|
1116
|
+
target. See [Page Filters § Migration](page_filters.md#migration-from-parameters).
|
|
1117
|
+
|
|
1118
|
+
## Embed Widget
|
|
1119
|
+
|
|
1120
|
+
The `embed` widget type renders external content inside an iframe. It delegates URL generation to a registered **embed provider**, keeping the page YAML declarative while the provider handles authentication, URL signing, or any provider-specific logic.
|
|
1121
|
+
|
|
1122
|
+
### Widget Attributes
|
|
1123
|
+
|
|
1124
|
+
| Attribute | Type | Required | Description |
|
|
1125
|
+
|-----------|------|----------|-------------|
|
|
1126
|
+
| `type` | string | yes | Must be `embed`. |
|
|
1127
|
+
| `provider` | string | yes | Name of the registered embed provider (e.g., `metabase`, `grafana`). |
|
|
1128
|
+
| `resource_type` | string | yes | Provider-specific resource type (e.g., `dashboard`, `question`, `panel`). |
|
|
1129
|
+
| `resource_id` | string/integer | yes | Provider-specific resource identifier. |
|
|
1130
|
+
| `params` | object | no | Key-value pairs passed to the provider's `embed_url` method. Used for filtering, theming, or other provider-specific options. |
|
|
1131
|
+
| `height` | string | no | CSS height for the iframe (e.g., `400px`, `60vh`). Defaults to the provider's default or `400px`. |
|
|
1132
|
+
|
|
1133
|
+
### Provider Setup
|
|
1134
|
+
|
|
1135
|
+
Register embed providers in the application initializer:
|
|
1136
|
+
|
|
1137
|
+
```ruby
|
|
1138
|
+
LcpRuby.configure do |config|
|
|
1139
|
+
config.register_embed_provider("metabase", MetabaseEmbedProvider.new(
|
|
1140
|
+
site_url: ENV["METABASE_SITE_URL"],
|
|
1141
|
+
secret_key: ENV["METABASE_SECRET_KEY"]
|
|
1142
|
+
))
|
|
1143
|
+
end
|
|
1144
|
+
```
|
|
1145
|
+
|
|
1146
|
+
### Provider Interface
|
|
1147
|
+
|
|
1148
|
+
Every provider class must extend `LcpRuby::EmbedProviders::Base` and implement the `embed_url` method:
|
|
1149
|
+
|
|
1150
|
+
```ruby
|
|
1151
|
+
class MetabaseEmbedProvider < LcpRuby::EmbedProviders::Base
|
|
1152
|
+
def initialize(site_url:, secret_key:)
|
|
1153
|
+
@site_url = site_url
|
|
1154
|
+
@secret_key = secret_key
|
|
1155
|
+
end
|
|
1156
|
+
|
|
1157
|
+
# @param resource_type [String] e.g., "dashboard", "question"
|
|
1158
|
+
# @param resource_id [String, Integer] provider-specific ID
|
|
1159
|
+
# @param params [Hash] additional parameters from the widget YAML
|
|
1160
|
+
# @return [String] fully qualified URL for the iframe src
|
|
1161
|
+
def embed_url(resource_type:, resource_id:, params: {})
|
|
1162
|
+
token = sign_token(resource_type, resource_id, params)
|
|
1163
|
+
"#{@site_url}/embed/#{resource_type}/#{token}#bordered=false&titled=false"
|
|
1164
|
+
end
|
|
1165
|
+
end
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
### Security
|
|
1169
|
+
|
|
1170
|
+
The embed iframe is rendered with a `sandbox` attribute to restrict the embedded content's capabilities. By default, the sandbox allows `allow-scripts` and `allow-same-origin` (required by most BI tools), but does not permit navigation or form submission in the parent frame.
|
|
1171
|
+
|
|
1172
|
+
For Content Security Policy (CSP) compliance, use the `LcpRuby.embed_csp_sources` helper to retrieve the list of registered provider origins. Add these to the `frame-src` directive in your CSP configuration:
|
|
1173
|
+
|
|
1174
|
+
```ruby
|
|
1175
|
+
# config/initializers/content_security_policy.rb
|
|
1176
|
+
Rails.application.config.content_security_policy do |policy|
|
|
1177
|
+
policy.frame_src :self, *LcpRuby.embed_csp_sources
|
|
1178
|
+
end
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
### Example
|
|
1182
|
+
|
|
1183
|
+
```yaml
|
|
1184
|
+
page:
|
|
1185
|
+
name: analytics_dashboard
|
|
1186
|
+
slug: analytics
|
|
1187
|
+
layout: grid
|
|
1188
|
+
zones:
|
|
1189
|
+
- name: revenue_dashboard
|
|
1190
|
+
type: widget
|
|
1191
|
+
widget:
|
|
1192
|
+
type: embed
|
|
1193
|
+
provider: metabase
|
|
1194
|
+
resource_type: dashboard
|
|
1195
|
+
resource_id: 42
|
|
1196
|
+
params:
|
|
1197
|
+
date_filter: this_month
|
|
1198
|
+
theme: transparent
|
|
1199
|
+
height: 600px
|
|
1200
|
+
position: { row: 1, col: 1, width: 12, height: 4 }
|
|
1201
|
+
|
|
1202
|
+
- name: recent_orders
|
|
1203
|
+
presenter: dashboard_orders
|
|
1204
|
+
scope: recent
|
|
1205
|
+
limit: 10
|
|
1206
|
+
position: { row: 5, col: 1, width: 12, height: 2 }
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
### approval_status
|
|
1210
|
+
|
|
1211
|
+
Displays the approval workflow status for the current record: step progress, task details with approver snapshots, and action buttons (approve/reject/return/claim/delegate) for the current user's pending tasks. No extra required fields — the widget reads the record's active approval request automatically.
|
|
1212
|
+
|
|
1213
|
+
| Attribute | Type | Required | Description |
|
|
1214
|
+
|-----------|------|----------|-------------|
|
|
1215
|
+
| `type` | string | yes | Must be `approval_status`. |
|
|
1216
|
+
|
|
1217
|
+
```yaml
|
|
1218
|
+
- name: approval
|
|
1219
|
+
type: widget
|
|
1220
|
+
area: below
|
|
1221
|
+
widget:
|
|
1222
|
+
type: approval_status
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
This widget requires the approval engine to be configured (see [Workflow Approvals Reference](workflow-approvals.md)). If no active approval request exists on the record, the widget is hidden.
|
|
1226
|
+
|
|
1227
|
+
**Note:** Even without an explicit widget zone, the approval status is automatically rendered on show pages via the `approval_status` [view slot](view-slots.md) when a record has an active approval request.
|
|
1228
|
+
|
|
1229
|
+
## DB-Stored Page Definitions
|
|
1230
|
+
|
|
1231
|
+
Page definitions can be stored in the database at runtime, following the platform's Configuration Source Principle. This enables non-technical users to create and manage pages (dashboards, composite layouts) without editing YAML files.
|
|
1232
|
+
|
|
1233
|
+
### Generator
|
|
1234
|
+
|
|
1235
|
+
Run the generator to create the required database tables, model YAML, presenter, and permissions:
|
|
1236
|
+
|
|
1237
|
+
```bash
|
|
1238
|
+
rails generate lcp_ruby:pages
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
### Configuration
|
|
1242
|
+
|
|
1243
|
+
Enable DB-stored pages in the initializer:
|
|
1244
|
+
|
|
1245
|
+
```ruby
|
|
1246
|
+
# config/initializers/lcp_ruby.rb
|
|
1247
|
+
LcpRuby.configure do |config|
|
|
1248
|
+
config.page_source = :model
|
|
1249
|
+
end
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
### Model Contract
|
|
1253
|
+
|
|
1254
|
+
The generator creates a `page_config` model with the following contract:
|
|
1255
|
+
|
|
1256
|
+
| Column | Type | Required | Description |
|
|
1257
|
+
|--------|------|----------|-------------|
|
|
1258
|
+
| `name` | string | yes | Unique page identifier. Must not collide with YAML page names unless overriding intentionally. |
|
|
1259
|
+
| `definition` | json | yes | Page definition in JSON format. Uses the same structure as the `page:` root in YAML files. |
|
|
1260
|
+
| `active` | boolean | no | When `false`, the page definition is ignored at boot. Defaults to `true`. |
|
|
1261
|
+
|
|
1262
|
+
### Definition JSON Format
|
|
1263
|
+
|
|
1264
|
+
The `definition` column stores the page structure as JSON, mirroring the YAML `page:` format:
|
|
1265
|
+
|
|
1266
|
+
```json
|
|
1267
|
+
{
|
|
1268
|
+
"name": "sales_dashboard",
|
|
1269
|
+
"slug": "sales",
|
|
1270
|
+
"layout": "grid",
|
|
1271
|
+
"title_key": "lcp_ruby.pages.sales.title",
|
|
1272
|
+
"zones": [
|
|
1273
|
+
{
|
|
1274
|
+
"name": "revenue",
|
|
1275
|
+
"type": "widget",
|
|
1276
|
+
"widget": {
|
|
1277
|
+
"type": "kpi_card",
|
|
1278
|
+
"model": "order",
|
|
1279
|
+
"aggregate": "sum",
|
|
1280
|
+
"aggregate_field": "total_amount",
|
|
1281
|
+
"format": "currency"
|
|
1282
|
+
},
|
|
1283
|
+
"position": { "row": 1, "col": 1, "width": 6, "height": 1 }
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
"name": "recent_orders",
|
|
1287
|
+
"presenter": "dashboard_orders",
|
|
1288
|
+
"scope": "recent",
|
|
1289
|
+
"limit": 5,
|
|
1290
|
+
"position": { "row": 2, "col": 1, "width": 12, "height": 2 }
|
|
1291
|
+
}
|
|
1292
|
+
]
|
|
1293
|
+
}
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
All page attributes (`model`, `dialog`, `filter_form`, `scope_filters`, `index_presenter`, etc.) and zone attributes (`scope_context`, `visible_when`, `filters`, `record_click`, `depends_on`, etc.) are supported in the JSON format. The structure is identical to YAML — only the serialization format differs.
|
|
1297
|
+
|
|
1298
|
+
### Override Priority
|
|
1299
|
+
|
|
1300
|
+
Page definitions are resolved in this priority order (highest wins):
|
|
1301
|
+
|
|
1302
|
+
1. **DB-stored pages** — runtime definitions from the `page_config` table (only active records)
|
|
1303
|
+
2. **YAML pages** — static definitions from `config/lcp_ruby/pages/*.yml`
|
|
1304
|
+
3. **Auto-generated pages** — created automatically for unclaimed presenters
|
|
1305
|
+
|
|
1306
|
+
When a DB page and a YAML page share the same `name`, the DB version takes precedence. This allows runtime overrides of static page definitions without modifying YAML files.
|
|
1307
|
+
|
|
1308
|
+
### Management UI
|
|
1309
|
+
|
|
1310
|
+
The generator creates a presenter for the `page_config` model, accessible at `/page-configs`. This provides a standard CRUD interface for creating, editing, and deactivating page definitions through the platform's own UI.
|
|
1311
|
+
|
|
1312
|
+
### Validation
|
|
1313
|
+
|
|
1314
|
+
DB-stored pages are validated with the same rules as YAML pages (zone constraints, scope_context references, widget configuration, etc.). Invalid definitions are logged as warnings at boot and skipped — they do not prevent the application from starting.
|
|
1315
|
+
|
|
1316
|
+
## Auto-Refresh (depends_on)
|
|
1317
|
+
|
|
1318
|
+
The `depends_on` zone attribute enables automatic zone refresh when records of a specified model are created, updated, or destroyed. This keeps dashboard widgets and related-record lists up to date without manual page reloads.
|
|
1319
|
+
|
|
1320
|
+
### Zone Attribute
|
|
1321
|
+
|
|
1322
|
+
| Attribute | Type | Required | Description |
|
|
1323
|
+
|-----------|------|----------|-------------|
|
|
1324
|
+
| `depends_on` | array of strings | no | List of model names. When a CRUD operation occurs on any listed model, the zone's Turbo Frame is replaced via a Turbo Stream broadcast. |
|
|
1325
|
+
|
|
1326
|
+
### Behavior
|
|
1327
|
+
|
|
1328
|
+
When a record of a model listed in `depends_on` is created, updated, or destroyed, the platform broadcasts a Turbo Stream `replace` action targeting the zone's Turbo Frame. The zone content is re-rendered server-side with fresh data and pushed to all connected clients viewing the page.
|
|
1329
|
+
|
|
1330
|
+
The broadcast is triggered by `after_commit` callbacks on the dependent models. Only zones currently visible to the user (i.e., not hidden by `visible_when` or authorization) receive the update.
|
|
1331
|
+
|
|
1332
|
+
### ActionCable Requirement
|
|
1333
|
+
|
|
1334
|
+
`depends_on` requires ActionCable to be available in the host application. If ActionCable is not configured:
|
|
1335
|
+
|
|
1336
|
+
- The `depends_on` attribute is silently ignored at runtime (no errors, zones simply do not auto-refresh).
|
|
1337
|
+
- A warning is logged at boot time: `[LcpRuby] depends_on requires ActionCable but it is not available. Auto-refresh will be disabled.`
|
|
1338
|
+
|
|
1339
|
+
This ensures graceful degradation — pages render and function normally, but without live updates.
|
|
1340
|
+
|
|
1341
|
+
### Example
|
|
1342
|
+
|
|
1343
|
+
```yaml
|
|
1344
|
+
page:
|
|
1345
|
+
name: order_detail
|
|
1346
|
+
model: order
|
|
1347
|
+
slug: orders
|
|
1348
|
+
zones:
|
|
1349
|
+
- name: header
|
|
1350
|
+
presenter: order_show
|
|
1351
|
+
area: main
|
|
1352
|
+
|
|
1353
|
+
- name: order_lines
|
|
1354
|
+
presenter: order_lines_index
|
|
1355
|
+
area: tabs
|
|
1356
|
+
label_key: lcp_ruby.pages.order_detail.tabs.lines
|
|
1357
|
+
scope_context:
|
|
1358
|
+
order_id: ":record.id"
|
|
1359
|
+
depends_on: [order_line]
|
|
1360
|
+
|
|
1361
|
+
- name: total_amount
|
|
1362
|
+
type: widget
|
|
1363
|
+
area: sidebar
|
|
1364
|
+
widget:
|
|
1365
|
+
type: kpi_card
|
|
1366
|
+
model: order_line
|
|
1367
|
+
aggregate: sum
|
|
1368
|
+
aggregate_field: amount
|
|
1369
|
+
label_key: lcp_ruby.pages.order_detail.sidebar.total
|
|
1370
|
+
scope_context:
|
|
1371
|
+
order_id: ":record.id"
|
|
1372
|
+
depends_on: [order_line]
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
In this example, both the order lines tab and the total amount KPI widget automatically refresh whenever an `order_line` record is created, updated, or destroyed. If a user adds a line item via a dialog, both zones update in real time without a page reload.
|