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,1290 @@
|
|
|
1
|
+
# Composite Pages Guide
|
|
2
|
+
|
|
3
|
+
Composite pages display a record with multiple related zones — a detail header, tabbed sub-tables, an optional sidebar, and below-content areas. This guide walks through building a typical master-detail page.
|
|
4
|
+
|
|
5
|
+
For the full attribute reference, see the [Pages Reference](../reference/pages.md#composite-pages-semantic-layout).
|
|
6
|
+
|
|
7
|
+
## When to Use Composite Pages
|
|
8
|
+
|
|
9
|
+
Use composite pages when a single record's show page needs to display related data from other models. Common examples:
|
|
10
|
+
|
|
11
|
+
- Employee detail with tabs for leave requests, trainings, and evaluations
|
|
12
|
+
- Company 360-degree view with contacts, deals, and activity feed
|
|
13
|
+
- Order detail with line items, shipments, and payment history
|
|
14
|
+
|
|
15
|
+
If you only need a standalone overview with KPI cards and widgets, use a [Dashboard](dashboards.md) instead (standalone page with `layout: grid`).
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### 1. Define the Models
|
|
20
|
+
|
|
21
|
+
You need a primary model and one or more related models:
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
# config/lcp_ruby/models/employee.yml
|
|
25
|
+
model:
|
|
26
|
+
name: employee
|
|
27
|
+
fields:
|
|
28
|
+
- { name: name, type: string }
|
|
29
|
+
- { name: email, type: email }
|
|
30
|
+
- { name: department, type: string }
|
|
31
|
+
- { name: status, type: enum, enum_values: [active, inactive] }
|
|
32
|
+
|
|
33
|
+
# config/lcp_ruby/models/leave_request.yml
|
|
34
|
+
model:
|
|
35
|
+
name: leave_request
|
|
36
|
+
fields:
|
|
37
|
+
- { name: employee_id, type: integer }
|
|
38
|
+
- { name: start_date, type: date }
|
|
39
|
+
- { name: end_date, type: date }
|
|
40
|
+
- { name: status, type: enum, enum_values: [pending, approved, rejected] }
|
|
41
|
+
- { name: reason, type: string }
|
|
42
|
+
associations:
|
|
43
|
+
- { type: belongs_to, name: employee }
|
|
44
|
+
|
|
45
|
+
# config/lcp_ruby/models/training.yml
|
|
46
|
+
model:
|
|
47
|
+
name: training
|
|
48
|
+
fields:
|
|
49
|
+
- { name: employee_id, type: integer }
|
|
50
|
+
- { name: title, type: string }
|
|
51
|
+
- { name: status, type: enum, enum_values: [scheduled, completed, cancelled] }
|
|
52
|
+
associations:
|
|
53
|
+
- { type: belongs_to, name: employee }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Create Presenters for Each Zone
|
|
57
|
+
|
|
58
|
+
Each zone in a composite page renders a presenter. The main zone typically uses a show presenter, while tab zones use index presenters:
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
# config/lcp_ruby/presenters/employee_detail.yml
|
|
62
|
+
presenter:
|
|
63
|
+
name: employee_detail
|
|
64
|
+
model: employee
|
|
65
|
+
slug: employees
|
|
66
|
+
show:
|
|
67
|
+
layout:
|
|
68
|
+
- section: Details
|
|
69
|
+
fields:
|
|
70
|
+
- { field: name }
|
|
71
|
+
- { field: email }
|
|
72
|
+
- { field: department }
|
|
73
|
+
- { field: status }
|
|
74
|
+
actions:
|
|
75
|
+
single:
|
|
76
|
+
- { name: edit, type: built_in }
|
|
77
|
+
|
|
78
|
+
# config/lcp_ruby/presenters/leave_requests_index.yml
|
|
79
|
+
presenter:
|
|
80
|
+
name: leave_requests_index
|
|
81
|
+
model: leave_request
|
|
82
|
+
index:
|
|
83
|
+
table_columns:
|
|
84
|
+
- { field: start_date }
|
|
85
|
+
- { field: end_date }
|
|
86
|
+
- { field: status }
|
|
87
|
+
- { field: reason }
|
|
88
|
+
|
|
89
|
+
# config/lcp_ruby/presenters/trainings_index.yml
|
|
90
|
+
presenter:
|
|
91
|
+
name: trainings_index
|
|
92
|
+
model: training
|
|
93
|
+
index:
|
|
94
|
+
table_columns:
|
|
95
|
+
- { field: title }
|
|
96
|
+
- { field: status }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. Define the Composite Page
|
|
100
|
+
|
|
101
|
+
Create a page YAML that combines the presenters into zones:
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
# config/lcp_ruby/pages/employee_detail.yml
|
|
105
|
+
page:
|
|
106
|
+
name: employee_detail
|
|
107
|
+
model: employee
|
|
108
|
+
slug: employees
|
|
109
|
+
zones:
|
|
110
|
+
- name: header
|
|
111
|
+
presenter: employee_detail
|
|
112
|
+
area: main
|
|
113
|
+
|
|
114
|
+
- name: leave_requests
|
|
115
|
+
presenter: leave_requests_index
|
|
116
|
+
area: tabs
|
|
117
|
+
label_key: pages.employee_detail.tabs.leave_requests
|
|
118
|
+
scope_context:
|
|
119
|
+
employee_id: ":record.id"
|
|
120
|
+
|
|
121
|
+
- name: trainings
|
|
122
|
+
presenter: trainings_index
|
|
123
|
+
area: tabs
|
|
124
|
+
label_key: pages.employee_detail.tabs.trainings
|
|
125
|
+
scope_context:
|
|
126
|
+
employee_id: ":record.id"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. Set Up Permissions
|
|
130
|
+
|
|
131
|
+
Each zone's presenter model needs permissions. If a role has no presenter access for a zone's model, that zone (and its tab) is hidden:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
# config/lcp_ruby/permissions/employee.yml
|
|
135
|
+
permissions:
|
|
136
|
+
model: employee
|
|
137
|
+
roles:
|
|
138
|
+
admin:
|
|
139
|
+
crud: [index, show, create, update, destroy]
|
|
140
|
+
presenters: all
|
|
141
|
+
fields:
|
|
142
|
+
readable: all
|
|
143
|
+
writable: all
|
|
144
|
+
viewer:
|
|
145
|
+
crud: [index, show]
|
|
146
|
+
presenters: all
|
|
147
|
+
fields:
|
|
148
|
+
readable: all
|
|
149
|
+
|
|
150
|
+
# config/lcp_ruby/permissions/leave_request.yml
|
|
151
|
+
permissions:
|
|
152
|
+
model: leave_request
|
|
153
|
+
roles:
|
|
154
|
+
admin:
|
|
155
|
+
crud: [index, show, create, update, destroy]
|
|
156
|
+
presenters: all
|
|
157
|
+
fields:
|
|
158
|
+
readable: all
|
|
159
|
+
writable: all
|
|
160
|
+
viewer:
|
|
161
|
+
crud: [index, show]
|
|
162
|
+
presenters: all
|
|
163
|
+
fields:
|
|
164
|
+
readable: all
|
|
165
|
+
|
|
166
|
+
# config/lcp_ruby/permissions/training.yml
|
|
167
|
+
permissions:
|
|
168
|
+
model: training
|
|
169
|
+
roles:
|
|
170
|
+
admin:
|
|
171
|
+
crud: [index, show]
|
|
172
|
+
presenters: all
|
|
173
|
+
fields:
|
|
174
|
+
readable: all
|
|
175
|
+
viewer:
|
|
176
|
+
crud: [index]
|
|
177
|
+
presenters: [] # No presenter access -> training tab hidden for viewers
|
|
178
|
+
fields:
|
|
179
|
+
readable: all
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
In this example, users with the `viewer` role see the employee detail and leave requests tab, but the trainings tab is hidden because `presenters: []` denies presenter access.
|
|
183
|
+
|
|
184
|
+
### 5. Add Translations (optional)
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
# config/locales/en.yml
|
|
188
|
+
en:
|
|
189
|
+
pages:
|
|
190
|
+
employee_detail:
|
|
191
|
+
tabs:
|
|
192
|
+
leave_requests: "Leave Requests"
|
|
193
|
+
trainings: "Trainings"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
If translations are missing, tabs fall back to the humanized zone name (e.g., "Leave requests").
|
|
197
|
+
|
|
198
|
+
## Areas
|
|
199
|
+
|
|
200
|
+
Composite pages use **semantic layout** with four named areas:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
+---------------------------+----------+
|
|
204
|
+
| main | sidebar |
|
|
205
|
+
+---------------------------+----------+
|
|
206
|
+
| tabs: [tab1] [tab2] |
|
|
207
|
+
| +-----------------------------------+|
|
|
208
|
+
| | active tab content ||
|
|
209
|
+
| +-----------------------------------+|
|
|
210
|
+
+---------------------------------------+
|
|
211
|
+
| below |
|
|
212
|
+
+---------------------------------------+
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
| Area | Description | Typical use |
|
|
216
|
+
|------|-------------|-------------|
|
|
217
|
+
| `main` | Primary record details | Show presenter (header section) |
|
|
218
|
+
| `sidebar` | Side panel | Notes, quick stats, related links |
|
|
219
|
+
| `tabs` | Tabbed content with tab bar | Related record tables (leave requests, trainings) |
|
|
220
|
+
| `below` | Full-width area below tabs | Activity log, audit trail |
|
|
221
|
+
|
|
222
|
+
Zones default to `area: main` if not specified.
|
|
223
|
+
|
|
224
|
+
## scope_context
|
|
225
|
+
|
|
226
|
+
The `scope_context` attribute scopes a zone's data query to the parent record. Without it, a tab would show all records from the model, not just those related to the current employee.
|
|
227
|
+
|
|
228
|
+
Each key is a column name on the zone's model, and each value is a dynamic reference or static value:
|
|
229
|
+
|
|
230
|
+
```yaml
|
|
231
|
+
# Most common: scope by parent record ID
|
|
232
|
+
scope_context:
|
|
233
|
+
employee_id: ":record.id"
|
|
234
|
+
|
|
235
|
+
# Scope by a field on the parent record
|
|
236
|
+
scope_context:
|
|
237
|
+
department_id: ":record.department_id"
|
|
238
|
+
|
|
239
|
+
# Scope to the current user
|
|
240
|
+
scope_context:
|
|
241
|
+
assigned_to_id: ":current_user.id"
|
|
242
|
+
|
|
243
|
+
# Multiple conditions
|
|
244
|
+
scope_context:
|
|
245
|
+
employee_id: ":record.id"
|
|
246
|
+
year: ":current_year"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**Available dynamic references:**
|
|
250
|
+
|
|
251
|
+
| Reference | Resolves to |
|
|
252
|
+
|-----------|-------------|
|
|
253
|
+
| `:record.id` | Parent record's `id` |
|
|
254
|
+
| `:record.<field>` | Field value from the parent record (single-level dot-path only) |
|
|
255
|
+
| `:current_user` | The full `current_user` object (useful with `filter_*` interceptors) |
|
|
256
|
+
| `:current_user.id` | Current user's `id` |
|
|
257
|
+
| `:selection_id` | Selected record's `id` from `?selected=N` (master-detail only) |
|
|
258
|
+
| `:current_year` | Current year (integer) |
|
|
259
|
+
| `:current_date` | Today's date |
|
|
260
|
+
|
|
261
|
+
Static values (without `:` prefix) pass through unchanged.
|
|
262
|
+
|
|
263
|
+
**Dot-path depth:** Only single-level paths are supported (e.g., `:record.department_id`). Multi-level paths like `:record.department.company_id` raise an error.
|
|
264
|
+
|
|
265
|
+
**Scope application:** For each `scope_context` key, the platform first checks if the zone's model defines a `filter_<key>` class method. If found, that method is called (both 2-arg `(scope, value)` and 3-arg `(scope, value, evaluator)` signatures are supported). Otherwise, a plain `where(key => value)` clause is applied. If neither a filter method nor a column exists, a warning is logged and the key is skipped.
|
|
266
|
+
|
|
267
|
+
## Tab Navigation
|
|
268
|
+
|
|
269
|
+
Tabs use **Turbo Frames** for fast, no-full-page-reload switching. Each tab's content is loaded from a dedicated zone endpoint:
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
GET /employees/123/zones/leave_requests → leave requests tab content
|
|
273
|
+
GET /employees/123/zones/trainings → trainings tab content
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The URL updates to `?tab=<zone_name>` via `history.replaceState` for bookmarkability:
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
/employees/123 → first tab active (leave_requests)
|
|
280
|
+
/employees/123?tab=trainings → trainings tab active
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Tab content is always loaded from the zone endpoint (including the initially active tab). Links inside the tab content (pagination, sort) stay within the Turbo Frame.
|
|
284
|
+
|
|
285
|
+
### Zone Endpoint
|
|
286
|
+
|
|
287
|
+
The zone endpoint (`GET /:slug/:id/zones/:zone_name`) renders a single zone's content as a `<turbo-frame>` fragment. It supports pagination and sorting:
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
/employees/123/zones/leave_requests?page=2
|
|
291
|
+
/employees/123/zones/leave_requests?sort=start_date&direction=desc
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Reloading Zones from Dialogs
|
|
295
|
+
|
|
296
|
+
After a dialog action, use `on_success: reload_zone` or `on_success: reload_zones` to refresh specific zones without reloading the entire page:
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
actions:
|
|
300
|
+
single:
|
|
301
|
+
- name: add_leave
|
|
302
|
+
type: dialog
|
|
303
|
+
dialog:
|
|
304
|
+
page: leave_request_form
|
|
305
|
+
on_success: reload_zone
|
|
306
|
+
reload_zone: leave_requests
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
See the [Dialogs Guide](dialogs.md#zone-reload-after-dialog) for details.
|
|
310
|
+
|
|
311
|
+
## Adding a Sidebar
|
|
312
|
+
|
|
313
|
+
The sidebar area renders alongside the main content. Both presenter and widget zones work in the sidebar:
|
|
314
|
+
|
|
315
|
+
```yaml
|
|
316
|
+
page:
|
|
317
|
+
name: employee_detail
|
|
318
|
+
model: employee
|
|
319
|
+
slug: employees
|
|
320
|
+
zones:
|
|
321
|
+
- name: header
|
|
322
|
+
presenter: employee_detail
|
|
323
|
+
area: main
|
|
324
|
+
|
|
325
|
+
- name: leave_count
|
|
326
|
+
type: widget
|
|
327
|
+
area: sidebar
|
|
328
|
+
widget:
|
|
329
|
+
type: kpi_card
|
|
330
|
+
model: leave_request
|
|
331
|
+
aggregate: count
|
|
332
|
+
label_key: pages.employee_detail.sidebar.leave_count
|
|
333
|
+
scope_context:
|
|
334
|
+
employee_id: ":record.id"
|
|
335
|
+
|
|
336
|
+
- name: leave_requests
|
|
337
|
+
presenter: leave_requests_index
|
|
338
|
+
area: tabs
|
|
339
|
+
label_key: pages.employee_detail.tabs.leave
|
|
340
|
+
scope_context:
|
|
341
|
+
employee_id: ":record.id"
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
The sidebar renders alongside the main area (2:1 column ratio by default). On screens narrower than 768px, it stacks below the main content.
|
|
345
|
+
|
|
346
|
+
### Custom Main/Sidebar Proportions
|
|
347
|
+
|
|
348
|
+
Use the `width` attribute (1–12) to control the column ratio:
|
|
349
|
+
|
|
350
|
+
```yaml
|
|
351
|
+
zones:
|
|
352
|
+
- name: header
|
|
353
|
+
presenter: employee_detail
|
|
354
|
+
area: main
|
|
355
|
+
width: 9 # 9 of 12 columns
|
|
356
|
+
|
|
357
|
+
- name: quick_stats
|
|
358
|
+
type: widget
|
|
359
|
+
area: sidebar
|
|
360
|
+
width: 3 # 3 of 12 columns
|
|
361
|
+
widget:
|
|
362
|
+
type: kpi_card
|
|
363
|
+
model: leave_request
|
|
364
|
+
aggregate: count
|
|
365
|
+
scope_context:
|
|
366
|
+
employee_id: ":record.id"
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
When both zones specify `width`, the layout uses `9fr 3fr` instead of the default `8fr 4fr`.
|
|
370
|
+
|
|
371
|
+
## Zone Filtering
|
|
372
|
+
|
|
373
|
+
Each zone can have its own search/filtering UI, configured via the `filters` attribute:
|
|
374
|
+
|
|
375
|
+
| Value | Description |
|
|
376
|
+
|-------|-------------|
|
|
377
|
+
| `quick` | Quick search text input (default for tab zones) |
|
|
378
|
+
| `full` | Quick search + full advanced filter pipeline (Ransack, custom filters, custom fields) |
|
|
379
|
+
| `none` | No filtering (default for main, sidebar, below zones) |
|
|
380
|
+
|
|
381
|
+
```yaml
|
|
382
|
+
zones:
|
|
383
|
+
- name: leave_requests
|
|
384
|
+
presenter: leave_requests_index
|
|
385
|
+
area: tabs
|
|
386
|
+
label_key: pages.employee_detail.tabs.leave
|
|
387
|
+
scope_context:
|
|
388
|
+
employee_id: ":record.id"
|
|
389
|
+
filters: quick # default for tabs — can be omitted
|
|
390
|
+
|
|
391
|
+
- name: trainings
|
|
392
|
+
presenter: trainings_index
|
|
393
|
+
area: tabs
|
|
394
|
+
label_key: pages.employee_detail.tabs.trainings
|
|
395
|
+
scope_context:
|
|
396
|
+
employee_id: ":record.id"
|
|
397
|
+
filters: full # enable advanced filtering for this tab
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
The search form renders inside the zone's Turbo Frame, so filtering does not reload the page. The search query is preserved across pagination and sorting.
|
|
401
|
+
|
|
402
|
+
**Widget zones** always use `filters: none` — setting a different value produces a validation error.
|
|
403
|
+
|
|
404
|
+
## Record Click Behavior
|
|
405
|
+
|
|
406
|
+
By default, clicking a row in a presenter zone table navigates to the record's show page. Use `record_click` to change this:
|
|
407
|
+
|
|
408
|
+
```yaml
|
|
409
|
+
zones:
|
|
410
|
+
- name: leave_requests
|
|
411
|
+
presenter: leave_requests_index
|
|
412
|
+
area: tabs
|
|
413
|
+
scope_context:
|
|
414
|
+
employee_id: ":record.id"
|
|
415
|
+
record_click: dialog # open in modal without leaving the page
|
|
416
|
+
|
|
417
|
+
- name: audit_log
|
|
418
|
+
presenter: audit_entries
|
|
419
|
+
area: below
|
|
420
|
+
scope_context:
|
|
421
|
+
record_id: ":record.id"
|
|
422
|
+
record_click: none # rows are not clickable
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
| Value | Behavior |
|
|
426
|
+
|-------|----------|
|
|
427
|
+
| `navigate` | Full-page navigation to record show page (default) |
|
|
428
|
+
| `dialog` | Opens record show in a modal dialog — keeps user on the composite page |
|
|
429
|
+
| `none` | No click action — rows are plain, not clickable |
|
|
430
|
+
|
|
431
|
+
**`dialog` mode** is especially useful for tab zones on composite pages — users can view related record details without losing their place. The dialog renders the record's show sections in a read-only view.
|
|
432
|
+
|
|
433
|
+
**Slug requirement:** Both `navigate` and `dialog` require the zone's presenter to have a routable slug. If no slug exists, rows degrade to non-clickable. A boot-time warning is produced in this case.
|
|
434
|
+
|
|
435
|
+
## Default Scope
|
|
436
|
+
|
|
437
|
+
The `default_scope` attribute applies a named scope to the zone's query before the `scope` attribute. Both are cumulative:
|
|
438
|
+
|
|
439
|
+
```yaml
|
|
440
|
+
zones:
|
|
441
|
+
- name: recent_active_tasks
|
|
442
|
+
presenter: tasks_index
|
|
443
|
+
area: tabs
|
|
444
|
+
scope_context:
|
|
445
|
+
employee_id: ":record.id"
|
|
446
|
+
default_scope: active # applied first: only active records
|
|
447
|
+
scope: recent # applied second: most recent of the active ones
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
This is useful when you want a base filter that always applies (e.g., `active`, `published`) while allowing the `scope` attribute to further narrow the dataset.
|
|
451
|
+
|
|
452
|
+
## Conditional Zone Visibility
|
|
453
|
+
|
|
454
|
+
Use `visible_when` to restrict zone visibility:
|
|
455
|
+
|
|
456
|
+
```yaml
|
|
457
|
+
zones:
|
|
458
|
+
- name: audit_log
|
|
459
|
+
presenter: audit_entries
|
|
460
|
+
area: below
|
|
461
|
+
scope_context:
|
|
462
|
+
record_id: ":record.id"
|
|
463
|
+
visible_when:
|
|
464
|
+
role: admin
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
This supports the same condition syntax as presenter `visible_when` — role shortcuts and full condition objects.
|
|
468
|
+
|
|
469
|
+
## Claimed Presenters
|
|
470
|
+
|
|
471
|
+
Presenters used as zones in a composite page are "claimed" — they do not get auto-generated pages. This prevents duplicate routes (e.g., `/leave-requests` would not exist separately since `leave_requests_index` is embedded in the employee detail page).
|
|
472
|
+
|
|
473
|
+
If you need the presenter to also be accessible as a standalone page, create a separate page definition for it.
|
|
474
|
+
|
|
475
|
+
## Index Presenter Override
|
|
476
|
+
|
|
477
|
+
When a composite page owns a slug (e.g., `slug: companies`), both the index and show actions route through it. The main zone typically uses a show-only presenter with no index columns. To keep the index table working, specify `index_presenter:` — a separate presenter with index columns for the index action:
|
|
478
|
+
|
|
479
|
+
```yaml
|
|
480
|
+
page:
|
|
481
|
+
name: company_detail
|
|
482
|
+
model: company
|
|
483
|
+
slug: companies
|
|
484
|
+
index_presenter: company # Used for GET /companies (index)
|
|
485
|
+
zones:
|
|
486
|
+
- name: company_header
|
|
487
|
+
presenter: company_show_zone # Used for GET /companies/:id (show)
|
|
488
|
+
area: main
|
|
489
|
+
# ...sidebar, tabs, below zones...
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
The `index_presenter` must have `index.columns` defined and use the same model as the page. It is only used for the index action — show, edit, and other actions use the main zone presenter.
|
|
493
|
+
|
|
494
|
+
## Form Zones
|
|
495
|
+
|
|
496
|
+
A form zone embeds an edit form in the composite page's main area instead of a read-only show view. After successful save, the user stays on the same composite page.
|
|
497
|
+
|
|
498
|
+
```yaml
|
|
499
|
+
page:
|
|
500
|
+
name: order_detail
|
|
501
|
+
model: order
|
|
502
|
+
slug: orders
|
|
503
|
+
zones:
|
|
504
|
+
- name: order_form
|
|
505
|
+
presenter: order_edit
|
|
506
|
+
area: main
|
|
507
|
+
submit_redirect: self
|
|
508
|
+
|
|
509
|
+
- name: order_lines
|
|
510
|
+
presenter: order_lines_index
|
|
511
|
+
area: below
|
|
512
|
+
scope_context:
|
|
513
|
+
order_id: ":record.id"
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
The form zone's presenter must have `form.sections` defined. Users need both `show` and `update` permissions. On validation error, the entire composite page re-renders with the form showing errors while other zones (sidebar, tabs, below) remain intact.
|
|
517
|
+
|
|
518
|
+
## Record Source Zones
|
|
519
|
+
|
|
520
|
+
A `record_source` zone loads a single record by ID from a URL parameter. This is useful for dialog composite pages where a sidebar shows a related record.
|
|
521
|
+
|
|
522
|
+
```yaml
|
|
523
|
+
zones:
|
|
524
|
+
- name: source_item
|
|
525
|
+
presenter: inventory_item_show
|
|
526
|
+
area: sidebar
|
|
527
|
+
width: 4
|
|
528
|
+
record_source:
|
|
529
|
+
param: source_id # loads from ?source_id=N
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
The zone renders the record's show sections. When the parameter is missing or the record is not found, the zone is hidden. `record_source` is mutually exclusive with `scope_context`.
|
|
533
|
+
|
|
534
|
+
## Validation
|
|
535
|
+
|
|
536
|
+
The configuration validator checks composite page definitions at boot:
|
|
537
|
+
|
|
538
|
+
- `scope_context` keys must be valid columns on the zone's model (warning)
|
|
539
|
+
- Dynamic references must use recognized formats (`:record.id`, `:record.<field>`, etc.) (warning)
|
|
540
|
+
- `:record.<field>` references are validated against the page's primary model (warning)
|
|
541
|
+
- Tab zones without `label_key` produce a warning (tabs fall back to humanized zone name, but explicit labels are recommended)
|
|
542
|
+
- Main zone must not be an index presenter when tabs are present (error — prevents query parameter collisions)
|
|
543
|
+
- Main zone presenter model should match the page's `model:` attribute (warning)
|
|
544
|
+
- Max zones per page: warning >10, error >20 (auto-generated pages are exempt)
|
|
545
|
+
- Zone `width` must be 1–12 when specified (error)
|
|
546
|
+
- Zone `default_scope` must exist as a named scope on the zone's model (error)
|
|
547
|
+
- Widget zones cannot have `filters` other than `none` (error)
|
|
548
|
+
- `record_click` != `none` with no routable slug produces a warning
|
|
549
|
+
- At most one form zone per page, must be in `main` area, presenter must have form sections (error)
|
|
550
|
+
- `record_source` and `scope_context` are mutually exclusive (error, validated in `ZoneDefinition`)
|
|
551
|
+
- `index_presenter` must reference an existing presenter with index columns and the same model (error)
|
|
552
|
+
- `record_source` zone presenter should have a show layout (warning)
|
|
553
|
+
- Unreachable presenters (no slug, not used in any zone, not dialog-only) produce a boot-time warning
|
|
554
|
+
|
|
555
|
+
Run validation manually:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
bundle exec rake lcp_ruby:validate
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
## Index-Level Composite Pages
|
|
562
|
+
|
|
563
|
+
Index composite pages combine an index table (main area) with sidebar or below widget zones. They provide the same features as a standard index page — search, filtering, sorting, pagination — while adding contextual widgets like KPI cards alongside the table.
|
|
564
|
+
|
|
565
|
+
```yaml
|
|
566
|
+
page:
|
|
567
|
+
name: orders_overview
|
|
568
|
+
model: order
|
|
569
|
+
slug: orders
|
|
570
|
+
zones:
|
|
571
|
+
- name: orders_table
|
|
572
|
+
presenter: orders_index # must have table_columns
|
|
573
|
+
area: main
|
|
574
|
+
|
|
575
|
+
- name: total_orders
|
|
576
|
+
type: widget
|
|
577
|
+
area: sidebar
|
|
578
|
+
widget:
|
|
579
|
+
type: kpi_card
|
|
580
|
+
model: order
|
|
581
|
+
aggregate: count
|
|
582
|
+
label_key: lcp_ruby.orders.total
|
|
583
|
+
|
|
584
|
+
- name: revenue
|
|
585
|
+
type: widget
|
|
586
|
+
area: sidebar
|
|
587
|
+
widget:
|
|
588
|
+
type: kpi_card
|
|
589
|
+
model: order
|
|
590
|
+
aggregate: sum
|
|
591
|
+
aggregate_field: amount
|
|
592
|
+
format: currency
|
|
593
|
+
label_key: lcp_ruby.orders.revenue
|
|
594
|
+
|
|
595
|
+
- name: help_text
|
|
596
|
+
type: widget
|
|
597
|
+
area: below
|
|
598
|
+
widget:
|
|
599
|
+
type: text
|
|
600
|
+
content_key: lcp_ruby.orders.help
|
|
601
|
+
visible_when:
|
|
602
|
+
role: admin
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
A page is detected as an index composite when its main zone presenter has `table_columns` defined.
|
|
606
|
+
|
|
607
|
+
**Constraints:**
|
|
608
|
+
- **No tab zones.** Index composites cannot have `area: tabs` zones because tabs use query parameters (`page`, `sort`, `qs`) that collide with the index table's parameters.
|
|
609
|
+
- **No `:record.id` in `scope_context`.** Index composites render a list, not a single record. Use model-level scopes or `:current_user.id` instead.
|
|
610
|
+
|
|
611
|
+
**Differences from dashboards:**
|
|
612
|
+
|
|
613
|
+
| | Index Composite | Dashboard |
|
|
614
|
+
|---|---|---|
|
|
615
|
+
| **Layout** | `semantic` (main + sidebar/below) | `grid` (explicit CSS grid positions) |
|
|
616
|
+
| **Model** | Model-bound — permissions, CRUD actions apply | Standalone — no model, widget-only |
|
|
617
|
+
| **Main area** | Full index table with search, filters, pagination | No index — only widgets |
|
|
618
|
+
| **URL** | `/orders` (model slug) | `/dashboard` (page slug) |
|
|
619
|
+
|
|
620
|
+
## Master-Detail Pages
|
|
621
|
+
|
|
622
|
+
A master-detail page puts a selectable list (or tree) in the left sidebar. Clicking a row loads its detail in the main area on the right, with tabs and widgets scoped to the selected record.
|
|
623
|
+
|
|
624
|
+
### 1. Create Presenters
|
|
625
|
+
|
|
626
|
+
You need a selection zone presenter (index columns for the sidebar list) and a detail zone presenter (show sections for the main area):
|
|
627
|
+
|
|
628
|
+
```ruby
|
|
629
|
+
# config/lcp_ruby/presenters/dept_list_zone.rb
|
|
630
|
+
define_presenter :dept_list_zone do
|
|
631
|
+
model :department
|
|
632
|
+
|
|
633
|
+
index do
|
|
634
|
+
tree_view true # render as collapsible tree (if model is a tree)
|
|
635
|
+
default_expanded "all"
|
|
636
|
+
column :name
|
|
637
|
+
end
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
# config/lcp_ruby/presenters/dept_detail_zone.rb
|
|
641
|
+
define_presenter :dept_detail_zone do
|
|
642
|
+
model :department
|
|
643
|
+
|
|
644
|
+
show do
|
|
645
|
+
section "Details", columns: 2 do
|
|
646
|
+
field :name, renderer: :heading
|
|
647
|
+
field :code
|
|
648
|
+
field "parent.name", label: "Parent"
|
|
649
|
+
end
|
|
650
|
+
end
|
|
651
|
+
end
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### 2. Define the Page
|
|
655
|
+
|
|
656
|
+
```yaml
|
|
657
|
+
# config/lcp_ruby/pages/department_explorer.yml
|
|
658
|
+
page:
|
|
659
|
+
name: department_explorer
|
|
660
|
+
model: department
|
|
661
|
+
slug: department-explorer
|
|
662
|
+
zones:
|
|
663
|
+
# Sidebar: selection list (left side)
|
|
664
|
+
- name: department_list
|
|
665
|
+
presenter: dept_list_zone
|
|
666
|
+
area: sidebar
|
|
667
|
+
width: 3 # sidebar proportion (1-12)
|
|
668
|
+
selection: single # enables click-to-select
|
|
669
|
+
record_click: none # selection handles clicks, not navigation
|
|
670
|
+
|
|
671
|
+
# Main: selected record detail (right side)
|
|
672
|
+
- name: department_detail
|
|
673
|
+
presenter: dept_detail_zone
|
|
674
|
+
area: main
|
|
675
|
+
width: 9 # main proportion (1-12)
|
|
676
|
+
record_source: selection # loads from ?selected=N
|
|
677
|
+
|
|
678
|
+
# Tab: scoped to the selected department
|
|
679
|
+
- name: employees
|
|
680
|
+
presenter: dept_employees_zone
|
|
681
|
+
area: tabs
|
|
682
|
+
label_key: lcp_ruby.pages.dept_explorer.tabs.employees
|
|
683
|
+
scope_context:
|
|
684
|
+
department_id: ":selection_id"
|
|
685
|
+
|
|
686
|
+
# Below widget: scoped to the selected department
|
|
687
|
+
- name: employee_kpi
|
|
688
|
+
type: widget
|
|
689
|
+
area: below
|
|
690
|
+
widget:
|
|
691
|
+
type: kpi_card
|
|
692
|
+
model: employee
|
|
693
|
+
aggregate: count
|
|
694
|
+
label_key: lcp_ruby.pages.dept_explorer.employee_count
|
|
695
|
+
scope_context:
|
|
696
|
+
department_id: ":selection_id"
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### How It Works
|
|
700
|
+
|
|
701
|
+
1. **No selection** — sidebar shows all records, main area shows "Select a record to view details", tabs and widgets are hidden.
|
|
702
|
+
2. **User clicks a row** — URL updates to `?selected=N`, page reloads. Sidebar highlights the selected row, main shows detail, tabs and widgets are scoped to the selection.
|
|
703
|
+
3. **Clicking another row** — selection changes, everything updates.
|
|
704
|
+
|
|
705
|
+
### Configuring the Sidebar
|
|
706
|
+
|
|
707
|
+
**Fields:** Controlled by the selection zone presenter's `index` columns. Add/remove columns to change what appears in the sidebar list.
|
|
708
|
+
|
|
709
|
+
**Width:** Use `width` on the sidebar and main zones. Values represent a fraction ratio (1–12 scale). For example, `width: 3` + `width: 9` gives a 1:3 sidebar-to-main ratio. `width: 4` + `width: 8` gives 1:2.
|
|
710
|
+
|
|
711
|
+
**Tree vs. flat:** If the model is a tree and the presenter uses `tree_view: true`, the sidebar renders a collapsible tree hierarchy. Otherwise it renders a flat table.
|
|
712
|
+
|
|
713
|
+
**Actions column:** Automatically hidden in the selection sidebar to save space.
|
|
714
|
+
|
|
715
|
+
### Key Differences from Index Composites
|
|
716
|
+
|
|
717
|
+
| | Index Composite | Master-Detail |
|
|
718
|
+
|---|---|---|
|
|
719
|
+
| **Sidebar** | Widget zones (KPIs, text) | Selection zone (clickable list/tree) |
|
|
720
|
+
| **Main area** | Index table | Detail of selected record |
|
|
721
|
+
| **Tabs** | Not supported | Supported, scoped to selection |
|
|
722
|
+
| **URL** | `/orders` | `/departments?selected=N` |
|
|
723
|
+
| **Layout** | Sidebar right | Sidebar left |
|
|
724
|
+
|
|
725
|
+
## Composite Dialog Pages
|
|
726
|
+
|
|
727
|
+
A composite dialog page renders multiple zones inside a modal — e.g., a transfer dialog with a form in the main area and an item preview in the sidebar.
|
|
728
|
+
|
|
729
|
+
```yaml
|
|
730
|
+
page:
|
|
731
|
+
name: transfer_dialog
|
|
732
|
+
model: inventory_transfer
|
|
733
|
+
dialog:
|
|
734
|
+
size: large
|
|
735
|
+
zones:
|
|
736
|
+
- name: transfer_form
|
|
737
|
+
presenter: transfer_form
|
|
738
|
+
area: main
|
|
739
|
+
width: 8
|
|
740
|
+
|
|
741
|
+
- name: source_item
|
|
742
|
+
presenter: item_show
|
|
743
|
+
area: sidebar
|
|
744
|
+
width: 4
|
|
745
|
+
record_source:
|
|
746
|
+
param: source_id
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
Unlike regular composite pages, all zones in a composite dialog are **rendered inline in a single response** — there is no lazy loading or Turbo Frame splitting. Tab zones (if any) are rendered as stacked sections, not as switchable tabs.
|
|
750
|
+
|
|
751
|
+
**Constraints:**
|
|
752
|
+
- At most one form zone per composite dialog (same rule as regular composites)
|
|
753
|
+
- No `:record.id` in `scope_context` (dialog-only pages have no primary record)
|
|
754
|
+
- Use `record_source` to load related records from URL params (e.g., `?source_id=42`)
|
|
755
|
+
|
|
756
|
+
See the [Dialogs Guide](dialogs.md#composite-dialog-pages) for triggering actions and workflows.
|
|
757
|
+
|
|
758
|
+
## Dialog Nesting
|
|
759
|
+
|
|
760
|
+
Dialogs support **max 2 levels** of nesting. When a dialog action is triggered while a dialog is already open, a nested dialog opens on top of the primary dialog.
|
|
761
|
+
|
|
762
|
+
**Example workflow:**
|
|
763
|
+
1. User clicks "Edit Order" → primary dialog opens with order form
|
|
764
|
+
2. Inside the order form, user clicks "Add Contact" → nested dialog opens on top
|
|
765
|
+
3. User creates the contact, nested dialog closes → primary dialog stays open
|
|
766
|
+
|
|
767
|
+
**On success behavior in nested dialogs:**
|
|
768
|
+
|
|
769
|
+
| `on_success` | Behavior |
|
|
770
|
+
|---|---|
|
|
771
|
+
| `close` | Closes only the nested dialog |
|
|
772
|
+
| `reload` | Re-fetches the primary dialog content |
|
|
773
|
+
| `reload_zone` / `reload_zones` | Reloads zones on the page behind both dialogs |
|
|
774
|
+
|
|
775
|
+
**Constraints:**
|
|
776
|
+
- Maximum 2 levels. Attempting to open a 3rd dialog produces a console warning.
|
|
777
|
+
- Nested dialogs use single-zone auto-pages (not composite dialogs).
|
|
778
|
+
|
|
779
|
+
## Relationship to Dashboards
|
|
780
|
+
|
|
781
|
+
Both dashboards and composite pages use the Pages infrastructure, but they serve different purposes:
|
|
782
|
+
|
|
783
|
+
| | Dashboards | Composite Pages | Index Composites |
|
|
784
|
+
|---|---|---|---|
|
|
785
|
+
| **Layout** | `grid` | `semantic` | `semantic` |
|
|
786
|
+
| **Model** | Standalone (no model) | Record-bound | Model-bound |
|
|
787
|
+
| **Main area** | Widgets only | Show/form presenter | Index table |
|
|
788
|
+
| **Zones** | Widgets + presenters | Presenters + widgets | Index + widgets |
|
|
789
|
+
| **URL** | `/dashboard` | `/employees/123` | `/orders` |
|
|
790
|
+
| **Tabs** | No | Yes | No |
|
|
791
|
+
| **Filters** | No | Per zone | Index-level + no per-zone |
|
|
792
|
+
|
|
793
|
+
## Global Parameters
|
|
794
|
+
|
|
795
|
+
Global parameters add filter controls above the page content that apply to all zones at once. They are useful when multiple zones on a page share a common filter dimension (e.g., department, status, date range).
|
|
796
|
+
|
|
797
|
+
**Restriction:** Parameters are only supported on **standalone pages** (dashboards) and **index composite pages**. Show-level composite pages (record detail with tabs) cannot use parameters because the record itself already scopes the zones.
|
|
798
|
+
|
|
799
|
+
### Step 1: Define the Models
|
|
800
|
+
|
|
801
|
+
For this example, we have an orders index page where users can filter by department and by status:
|
|
802
|
+
|
|
803
|
+
```yaml
|
|
804
|
+
# config/lcp_ruby/models/department.yml
|
|
805
|
+
model:
|
|
806
|
+
name: department
|
|
807
|
+
fields:
|
|
808
|
+
- { name: name, type: string }
|
|
809
|
+
|
|
810
|
+
# config/lcp_ruby/models/order.yml
|
|
811
|
+
model:
|
|
812
|
+
name: order
|
|
813
|
+
fields:
|
|
814
|
+
- { name: department_id, type: integer }
|
|
815
|
+
- { name: amount, type: decimal }
|
|
816
|
+
- { name: status, type: enum, enum_values: [pending, confirmed, shipped, cancelled] }
|
|
817
|
+
associations:
|
|
818
|
+
- { type: belongs_to, name: department }
|
|
819
|
+
scopes:
|
|
820
|
+
- { name: active, where: { status: [pending, confirmed, shipped] } }
|
|
821
|
+
- { name: pending, where: { status: pending } }
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### Step 2: Create the Presenter
|
|
825
|
+
|
|
826
|
+
```yaml
|
|
827
|
+
# config/lcp_ruby/presenters/orders_index.yml
|
|
828
|
+
presenter:
|
|
829
|
+
name: orders_index
|
|
830
|
+
model: order
|
|
831
|
+
index:
|
|
832
|
+
table_columns:
|
|
833
|
+
- { field: department_id, input_type: association_select }
|
|
834
|
+
- { field: amount }
|
|
835
|
+
- { field: status }
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
### Step 3: Define the Page with Parameters
|
|
839
|
+
|
|
840
|
+
```yaml
|
|
841
|
+
# config/lcp_ruby/pages/orders_overview.yml
|
|
842
|
+
page:
|
|
843
|
+
name: orders_overview
|
|
844
|
+
model: order
|
|
845
|
+
slug: orders
|
|
846
|
+
parameters:
|
|
847
|
+
- name: department
|
|
848
|
+
type: association_select
|
|
849
|
+
model: department
|
|
850
|
+
field: department_id
|
|
851
|
+
|
|
852
|
+
- name: status_scope
|
|
853
|
+
type: scope
|
|
854
|
+
scope: active
|
|
855
|
+
default: active
|
|
856
|
+
options:
|
|
857
|
+
- { value: active }
|
|
858
|
+
- { value: pending }
|
|
859
|
+
zones:
|
|
860
|
+
- name: orders_list
|
|
861
|
+
presenter: orders_index
|
|
862
|
+
area: main
|
|
863
|
+
width: 9
|
|
864
|
+
|
|
865
|
+
- name: order_count
|
|
866
|
+
type: widget
|
|
867
|
+
widget:
|
|
868
|
+
type: kpi_card
|
|
869
|
+
model: order
|
|
870
|
+
aggregate: count
|
|
871
|
+
label_key: lcp_ruby.orders.total
|
|
872
|
+
area: sidebar
|
|
873
|
+
width: 3
|
|
874
|
+
|
|
875
|
+
- name: revenue
|
|
876
|
+
type: widget
|
|
877
|
+
widget:
|
|
878
|
+
type: kpi_card
|
|
879
|
+
model: order
|
|
880
|
+
aggregate: sum
|
|
881
|
+
aggregate_field: amount
|
|
882
|
+
format: currency
|
|
883
|
+
label_key: lcp_ruby.orders.revenue
|
|
884
|
+
area: sidebar
|
|
885
|
+
width: 3
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
### How Parameters Work
|
|
889
|
+
|
|
890
|
+
Each parameter definition creates a filter control rendered above the page content. When the user selects a value, the page reloads with the parameter as a query string (e.g., `?department=5&status_scope=pending`). The resolved parameter values are applied to **every zone** on the page — both the main index table and all sidebar/below widgets.
|
|
891
|
+
|
|
892
|
+
**Parameter types:**
|
|
893
|
+
|
|
894
|
+
| Type | Renders as | Applies as |
|
|
895
|
+
|------|-----------|------------|
|
|
896
|
+
| `association_select` | Select dropdown populated from the `model` | `WHERE field = value` on each zone's query |
|
|
897
|
+
| `scope` | Select dropdown from the `options` list | Calls the named scope on each zone's model |
|
|
898
|
+
| `enum_select` | Select dropdown from the field's enum values | `WHERE field = value` on each zone's query |
|
|
899
|
+
| `date_range` | Two date inputs (from/to) | `WHERE field BETWEEN from AND to` on each zone's query |
|
|
900
|
+
|
|
901
|
+
**Required attributes by type:**
|
|
902
|
+
|
|
903
|
+
- `association_select` — `model` (source model for the dropdown) and `field` (FK column to filter on)
|
|
904
|
+
- `scope` — `scope` (the named scope to call); optionally `options` (list of allowed scope names) and `default` (pre-selected value)
|
|
905
|
+
- `enum_select` — `field` (the enum column to filter on)
|
|
906
|
+
- `date_range` — `field` (the date/datetime column to filter on)
|
|
907
|
+
|
|
908
|
+
**Scope dispatch:** For `association_select` and `enum_select`, the platform first checks if the zone's model defines a `filter_<field>` class method. If found, that method is called instead of a plain `WHERE` clause. This lets you customize filtering per model (e.g., filtering through a join table).
|
|
909
|
+
|
|
910
|
+
### Step 4: Add Translations (optional)
|
|
911
|
+
|
|
912
|
+
```yaml
|
|
913
|
+
# config/locales/en.yml
|
|
914
|
+
en:
|
|
915
|
+
lcp_ruby:
|
|
916
|
+
orders:
|
|
917
|
+
total: "Total Orders"
|
|
918
|
+
revenue: "Revenue"
|
|
919
|
+
pages:
|
|
920
|
+
orders_overview:
|
|
921
|
+
parameters:
|
|
922
|
+
department: "Department"
|
|
923
|
+
status_scope: "Status"
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
If `label_key` is set on the parameter definition, it takes priority. Otherwise, the parameter label falls back to the humanized name.
|
|
927
|
+
|
|
928
|
+
## Embedding External Dashboards
|
|
929
|
+
|
|
930
|
+
The embed widget renders an external dashboard (Metabase, Grafana, or a custom provider) inside an iframe within a page zone.
|
|
931
|
+
|
|
932
|
+
### Step 1: Create a Provider Class
|
|
933
|
+
|
|
934
|
+
If the built-in `Metabase` and `Grafana` providers do not cover your needs, create a custom provider by extending `LcpRuby::EmbedProviders::Base`:
|
|
935
|
+
|
|
936
|
+
```ruby
|
|
937
|
+
# app/embed_providers/power_bi_provider.rb
|
|
938
|
+
class PowerBiProvider < LcpRuby::EmbedProviders::Base
|
|
939
|
+
attr_reader :site_url
|
|
940
|
+
|
|
941
|
+
def initialize(site_url:, workspace_id:)
|
|
942
|
+
@site_url = site_url.chomp("/")
|
|
943
|
+
@workspace_id = workspace_id
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
def embed_url(resource_type:, resource_id:, params: {}, user: nil)
|
|
947
|
+
path = case resource_type.to_s
|
|
948
|
+
when "report"
|
|
949
|
+
"/reportEmbed?reportId=#{resource_id}&groupId=#{@workspace_id}"
|
|
950
|
+
when "dashboard"
|
|
951
|
+
"/dashboardEmbed?dashboardId=#{resource_id}&groupId=#{@workspace_id}"
|
|
952
|
+
else
|
|
953
|
+
"/reportEmbed?reportId=#{resource_id}&groupId=#{@workspace_id}"
|
|
954
|
+
end
|
|
955
|
+
|
|
956
|
+
url = "#{@site_url}#{path}"
|
|
957
|
+
|
|
958
|
+
# Pass through any additional parameters
|
|
959
|
+
query_parts = params.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }
|
|
960
|
+
url += "&#{query_parts.join('&')}" if query_parts.any?
|
|
961
|
+
url
|
|
962
|
+
end
|
|
963
|
+
end
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
The `site_url` reader is required — the platform uses it to derive CSP frame-src origins. The `embed_url` method receives the resource type, resource ID, optional params hash, and the current user (for signed or authenticated embeds).
|
|
967
|
+
|
|
968
|
+
### Step 2: Register the Provider
|
|
969
|
+
|
|
970
|
+
Register the provider instance in your Rails initializer:
|
|
971
|
+
|
|
972
|
+
```ruby
|
|
973
|
+
# config/initializers/lcp_ruby.rb
|
|
974
|
+
LcpRuby.configure do |config|
|
|
975
|
+
# Built-in Metabase provider
|
|
976
|
+
config.register_embed_provider(
|
|
977
|
+
"metabase",
|
|
978
|
+
LcpRuby::EmbedProviders::Metabase.new(
|
|
979
|
+
site_url: ENV["METABASE_URL"],
|
|
980
|
+
secret_key: ENV["METABASE_SECRET_KEY"]
|
|
981
|
+
)
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
# Built-in Grafana provider
|
|
985
|
+
config.register_embed_provider(
|
|
986
|
+
"grafana",
|
|
987
|
+
LcpRuby::EmbedProviders::Grafana.new(
|
|
988
|
+
site_url: ENV["GRAFANA_URL"],
|
|
989
|
+
api_token: ENV["GRAFANA_TOKEN"]
|
|
990
|
+
)
|
|
991
|
+
)
|
|
992
|
+
|
|
993
|
+
# Custom provider
|
|
994
|
+
config.register_embed_provider(
|
|
995
|
+
"power_bi",
|
|
996
|
+
PowerBiProvider.new(
|
|
997
|
+
site_url: "https://app.powerbi.com",
|
|
998
|
+
workspace_id: ENV["POWERBI_WORKSPACE_ID"]
|
|
999
|
+
)
|
|
1000
|
+
)
|
|
1001
|
+
end
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
The first argument is the provider name referenced in page YAML. You can register multiple providers.
|
|
1005
|
+
|
|
1006
|
+
### Step 3: Define the Page with an Embed Zone
|
|
1007
|
+
|
|
1008
|
+
```yaml
|
|
1009
|
+
# config/lcp_ruby/pages/analytics.yml
|
|
1010
|
+
page:
|
|
1011
|
+
name: analytics
|
|
1012
|
+
slug: analytics
|
|
1013
|
+
layout: grid
|
|
1014
|
+
zones:
|
|
1015
|
+
- name: sales_dashboard
|
|
1016
|
+
type: widget
|
|
1017
|
+
widget:
|
|
1018
|
+
type: embed
|
|
1019
|
+
provider: metabase
|
|
1020
|
+
resource_type: dashboard
|
|
1021
|
+
resource_id: 42
|
|
1022
|
+
height: "600px"
|
|
1023
|
+
params:
|
|
1024
|
+
date_filter: "past30days"
|
|
1025
|
+
position: { row: 1, col: 1, width: 12, height: 3 }
|
|
1026
|
+
|
|
1027
|
+
- name: server_metrics
|
|
1028
|
+
type: widget
|
|
1029
|
+
widget:
|
|
1030
|
+
type: embed
|
|
1031
|
+
provider: grafana
|
|
1032
|
+
resource_type: dashboard
|
|
1033
|
+
resource_id: "abc123"
|
|
1034
|
+
height: "400px"
|
|
1035
|
+
position: { row: 4, col: 1, width: 6, height: 2 }
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
**Required widget attributes:** `provider` (registered provider name), `resource_type` (provider-specific, e.g., "dashboard", "question", "panel"), `resource_id` (the identifier in the external system). Optional: `height` (iframe height, defaults to "400px"), `params` (extra query parameters forwarded to the provider's `embed_url`).
|
|
1039
|
+
|
|
1040
|
+
If the named provider is not registered, the zone logs a warning and is hidden gracefully.
|
|
1041
|
+
|
|
1042
|
+
Embed zones work in both `grid` layout (dashboards) and `semantic` layout (composite page sidebar or below areas).
|
|
1043
|
+
|
|
1044
|
+
### Step 4: Configure Content Security Policy
|
|
1045
|
+
|
|
1046
|
+
Browsers block iframes from external origins by default. Use `LcpRuby.embed_csp_sources` to retrieve the list of origins derived from all registered providers, then add them to your CSP:
|
|
1047
|
+
|
|
1048
|
+
```ruby
|
|
1049
|
+
# config/initializers/content_security_policy.rb
|
|
1050
|
+
Rails.application.config.content_security_policy do |policy|
|
|
1051
|
+
policy.frame_src :self, *LcpRuby.embed_csp_sources
|
|
1052
|
+
end
|
|
1053
|
+
```
|
|
1054
|
+
|
|
1055
|
+
`embed_csp_sources` returns an array of origin strings (e.g., `["https://metabase.example.com", "https://grafana.example.com"]`) extracted from each provider's `site_url`. Non-standard ports are preserved. Duplicate origins are removed automatically.
|
|
1056
|
+
|
|
1057
|
+
## Auto-Refresh with depends_on
|
|
1058
|
+
|
|
1059
|
+
The `depends_on` attribute on a zone causes it to automatically refresh when records of a specified model are created, updated, or destroyed. This keeps sidebar KPI cards, list widgets, and presenter zones up to date without a full page reload.
|
|
1060
|
+
|
|
1061
|
+
### Step 1: Define the Page with depends_on
|
|
1062
|
+
|
|
1063
|
+
Consider a task management page where a KPI card in the sidebar shows the total task count. When a user creates or deletes a task from the main table, the KPI should update automatically:
|
|
1064
|
+
|
|
1065
|
+
```yaml
|
|
1066
|
+
# config/lcp_ruby/pages/tasks_overview.yml
|
|
1067
|
+
page:
|
|
1068
|
+
name: tasks_overview
|
|
1069
|
+
model: do_task
|
|
1070
|
+
slug: do-tasks
|
|
1071
|
+
zones:
|
|
1072
|
+
- name: tasks_list
|
|
1073
|
+
presenter: do_tasks_index
|
|
1074
|
+
area: main
|
|
1075
|
+
width: 9
|
|
1076
|
+
|
|
1077
|
+
- name: task_count
|
|
1078
|
+
type: widget
|
|
1079
|
+
widget:
|
|
1080
|
+
type: kpi_card
|
|
1081
|
+
model: do_task
|
|
1082
|
+
aggregate: count
|
|
1083
|
+
label_key: lcp_ruby.tasks.total_count
|
|
1084
|
+
area: sidebar
|
|
1085
|
+
width: 3
|
|
1086
|
+
depends_on: [do_task]
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
The `depends_on` attribute accepts an array of model names. When any of those models change, the zone re-renders with fresh data.
|
|
1090
|
+
|
|
1091
|
+
### How It Works
|
|
1092
|
+
|
|
1093
|
+
1. **Turbo Stream subscription.** When the page loads, it renders a `<turbo-stream-source>` tag for each unique model name referenced by any zone's `depends_on`. The tag subscribes to the ActionCable channel `lcp_model_<model_name>`.
|
|
1094
|
+
|
|
1095
|
+
2. **Broadcast on change.** After every successful `create`, `update`, or `destroy` action in the `ResourcesController`, the controller calls `broadcast_refresh_to("lcp_model_<model_name>")`. This sends a Turbo Stream refresh signal to all subscribed clients.
|
|
1096
|
+
|
|
1097
|
+
3. **Zone refresh.** The browser receives the broadcast and Turbo automatically refreshes the page. Since the zone content is rendered server-side, the KPI card (or list widget, or presenter zone) recalculates its data on the next render.
|
|
1098
|
+
|
|
1099
|
+
### Step 2: Ensure ActionCable Is Available
|
|
1100
|
+
|
|
1101
|
+
`depends_on` requires ActionCable and `turbo-rails`. In a standard Rails 7+ application these are included by default. Verify that your setup includes:
|
|
1102
|
+
|
|
1103
|
+
- `gem "turbo-rails"` in your Gemfile
|
|
1104
|
+
- ActionCable configured (check `config/cable.yml`)
|
|
1105
|
+
- The Turbo JavaScript is loaded (typically via `importmap` or `jsbundling`)
|
|
1106
|
+
|
|
1107
|
+
### Graceful Degradation
|
|
1108
|
+
|
|
1109
|
+
If ActionCable is not available (e.g., the `ActionCable` constant is not defined), the Turbo Stream subscription tags are simply not rendered. The page works normally — zones display their data on load — but they will not auto-refresh when records change. Users would need to manually reload the page to see updated values.
|
|
1110
|
+
|
|
1111
|
+
The `broadcast_model_change` method in the controller also degrades gracefully: if `Turbo::StreamsChannel` is not defined or the broadcast fails for any reason, the error is logged as a warning and the create/update/destroy action still completes successfully.
|
|
1112
|
+
|
|
1113
|
+
### Constraints
|
|
1114
|
+
|
|
1115
|
+
- **Text widget zones** cannot use `depends_on` (they render static i18n content, so refreshing them has no effect). The validator raises an error if you try.
|
|
1116
|
+
- `depends_on` works on **kpi_card**, **list**, **chart**, **embed**, and **presenter** zones.
|
|
1117
|
+
- The broadcast is page-global — all clients viewing any page that subscribes to the same model channel will receive the refresh. This is intentional: if two users are viewing the same orders overview, both see updates in real time.
|
|
1118
|
+
- Multiple model names are supported: `depends_on: [order, payment]` subscribes to both `lcp_model_order` and `lcp_model_payment` channels.
|
|
1119
|
+
|
|
1120
|
+
### Example: Composite Show Page with Auto-Refreshing Tab Count
|
|
1121
|
+
|
|
1122
|
+
You can also use `depends_on` on show-level composite pages. For example, an employee detail page where the sidebar shows a leave request count that updates when leave requests are added via a dialog:
|
|
1123
|
+
|
|
1124
|
+
```yaml
|
|
1125
|
+
# config/lcp_ruby/pages/employee_detail.yml
|
|
1126
|
+
page:
|
|
1127
|
+
name: employee_detail
|
|
1128
|
+
model: employee
|
|
1129
|
+
slug: employees
|
|
1130
|
+
zones:
|
|
1131
|
+
- name: header
|
|
1132
|
+
presenter: employee_detail
|
|
1133
|
+
area: main
|
|
1134
|
+
|
|
1135
|
+
- name: leave_count
|
|
1136
|
+
type: widget
|
|
1137
|
+
area: sidebar
|
|
1138
|
+
widget:
|
|
1139
|
+
type: kpi_card
|
|
1140
|
+
model: leave_request
|
|
1141
|
+
aggregate: count
|
|
1142
|
+
label_key: pages.employee_detail.sidebar.leave_count
|
|
1143
|
+
scope_context:
|
|
1144
|
+
employee_id: ":record.id"
|
|
1145
|
+
depends_on: [leave_request]
|
|
1146
|
+
|
|
1147
|
+
- name: leave_requests
|
|
1148
|
+
presenter: leave_requests_index
|
|
1149
|
+
area: tabs
|
|
1150
|
+
label_key: pages.employee_detail.tabs.leave
|
|
1151
|
+
scope_context:
|
|
1152
|
+
employee_id: ":record.id"
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
When a leave request is created (from any page or dialog), the `leave_count` KPI widget refreshes automatically. The tab content is not subscribed to `depends_on` in this example — it updates through normal Turbo Frame navigation or zone reload after dialog actions.
|
|
1156
|
+
|
|
1157
|
+
## DB-Stored Pages
|
|
1158
|
+
|
|
1159
|
+
Page definitions can be stored in the database, allowing runtime creation and management of pages without editing YAML files. This is useful for letting administrators build custom dashboards or composite layouts through the platform's UI.
|
|
1160
|
+
|
|
1161
|
+
### Step 1: Run the Generator
|
|
1162
|
+
|
|
1163
|
+
```bash
|
|
1164
|
+
rails generate lcp_ruby:pages
|
|
1165
|
+
rails db:migrate
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
This creates the `page_config` model with the required database tables, a management presenter, and permissions.
|
|
1169
|
+
|
|
1170
|
+
### Step 2: Enable DB Page Source
|
|
1171
|
+
|
|
1172
|
+
Add the configuration to your initializer:
|
|
1173
|
+
|
|
1174
|
+
```ruby
|
|
1175
|
+
# config/initializers/lcp_ruby.rb
|
|
1176
|
+
LcpRuby.configure do |config|
|
|
1177
|
+
config.page_source = :model
|
|
1178
|
+
end
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
### Step 3: Manage Pages at /page-configs
|
|
1182
|
+
|
|
1183
|
+
Start the server and navigate to `/page-configs`. The generated management UI provides a standard CRUD interface for page definitions.
|
|
1184
|
+
|
|
1185
|
+
### Step 4: Create a Page Definition
|
|
1186
|
+
|
|
1187
|
+
Create a new page config record. The `definition` field accepts JSON using the same structure as the YAML `page:` format:
|
|
1188
|
+
|
|
1189
|
+
```json
|
|
1190
|
+
{
|
|
1191
|
+
"name": "team_dashboard",
|
|
1192
|
+
"slug": "team",
|
|
1193
|
+
"model": "employee",
|
|
1194
|
+
"zones": [
|
|
1195
|
+
{
|
|
1196
|
+
"name": "team_table",
|
|
1197
|
+
"presenter": "employees_index",
|
|
1198
|
+
"area": "main",
|
|
1199
|
+
"width": 9
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
"name": "headcount",
|
|
1203
|
+
"type": "widget",
|
|
1204
|
+
"area": "sidebar",
|
|
1205
|
+
"width": 3,
|
|
1206
|
+
"widget": {
|
|
1207
|
+
"type": "kpi_card",
|
|
1208
|
+
"model": "employee",
|
|
1209
|
+
"aggregate": "count",
|
|
1210
|
+
"label_key": "lcp_ruby.team.headcount"
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
]
|
|
1214
|
+
}
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
All page and zone attributes supported in YAML are also supported in the JSON definition — `scope_context`, `visible_when`, `filters`, `record_click`, `parameters`, `dialog`, `depends_on`, and so on.
|
|
1218
|
+
|
|
1219
|
+
### Override Priority
|
|
1220
|
+
|
|
1221
|
+
DB-stored pages take the highest priority:
|
|
1222
|
+
|
|
1223
|
+
1. **DB pages** (active records from `page_config`) — highest priority
|
|
1224
|
+
2. **YAML pages** (`config/lcp_ruby/pages/*.yml`)
|
|
1225
|
+
3. **Auto-generated pages** (created for unclaimed presenters) — lowest priority
|
|
1226
|
+
|
|
1227
|
+
If a DB page has the same `name` as a YAML page, the DB version wins. This lets administrators override static page definitions at runtime. Set `active: false` on a DB page to deactivate it and fall back to the YAML definition.
|
|
1228
|
+
|
|
1229
|
+
DB-stored pages are validated with the same rules as YAML pages. Invalid definitions are logged as warnings and skipped at boot.
|
|
1230
|
+
|
|
1231
|
+
## Custom Zones
|
|
1232
|
+
|
|
1233
|
+
When a composite page zone needs content that no presenter or widget can produce (a Gantt chart, a map, a custom React component), use `type: custom` to delegate rendering to a host-app partial.
|
|
1234
|
+
|
|
1235
|
+
### Step 1: Define the Custom Zone
|
|
1236
|
+
|
|
1237
|
+
```yaml
|
|
1238
|
+
# config/lcp_ruby/pages/employee_detail.yml
|
|
1239
|
+
page:
|
|
1240
|
+
name: employee_detail
|
|
1241
|
+
model: employee
|
|
1242
|
+
slug: employees
|
|
1243
|
+
zones:
|
|
1244
|
+
- name: header
|
|
1245
|
+
presenter: employee_show
|
|
1246
|
+
area: main
|
|
1247
|
+
- name: project_timeline
|
|
1248
|
+
type: custom
|
|
1249
|
+
partial: "employees/gantt_chart"
|
|
1250
|
+
area: tabs
|
|
1251
|
+
label_key: lcp_ruby.pages.employee_detail.tabs.timeline
|
|
1252
|
+
scope_context:
|
|
1253
|
+
employee_id: ":record.id"
|
|
1254
|
+
- name: quick_stats
|
|
1255
|
+
type: custom
|
|
1256
|
+
partial: "employees/stats_sidebar"
|
|
1257
|
+
area: sidebar
|
|
1258
|
+
eager: true
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
### Step 2: Create the Host-App Partial
|
|
1262
|
+
|
|
1263
|
+
```erb
|
|
1264
|
+
<%# app/views/employees/gantt_chart.html.erb %>
|
|
1265
|
+
<div data-controller="gantt"
|
|
1266
|
+
data-gantt-employee-id-value="<%= record.id %>">
|
|
1267
|
+
Loading timeline...
|
|
1268
|
+
</div>
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
The partial receives these locals: `record`, `presenter`, `evaluator`, `zone`, `scope_context_values`, `scoped_records`, `selection_id`, `current_user`, `page_definition`, `params`.
|
|
1272
|
+
|
|
1273
|
+
### Key Options
|
|
1274
|
+
|
|
1275
|
+
- **`eager: true`** — Renders the zone inline during the initial page load instead of via a lazy Turbo Frame. Use for lightweight sidebar/below content or zones that need inline `<script>` execution.
|
|
1276
|
+
- **`apply_scope_context: false`** — Only passes `scope_context_values` (resolved hash) without building `scoped_records`. Use when the partial needs full control over its queries.
|
|
1277
|
+
- **`model: "note"`** — Specifies a different model for `scoped_records` (cross-model zones).
|
|
1278
|
+
|
|
1279
|
+
### Turbo Frame Constraint
|
|
1280
|
+
|
|
1281
|
+
Lazy-loaded custom zones (default `eager: false`) 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 zones that require immediate script execution.
|
|
1282
|
+
|
|
1283
|
+
See [Rendering Extension Points](rendering-extension-points.md) for the full comparison of custom render blocks vs. view slots.
|
|
1284
|
+
|
|
1285
|
+
## What's Next
|
|
1286
|
+
|
|
1287
|
+
- [Pages Reference](../reference/pages.md) — Full attribute reference for pages and zones
|
|
1288
|
+
- [Dashboards Guide](dashboards.md) — Standalone grid pages with KPI widgets
|
|
1289
|
+
- [Dialogs Guide](dialogs.md) — Modal dialog pages and composite dialogs
|
|
1290
|
+
- [View Groups Guide](view-groups.md) — Navigation menu and view switching
|