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,3085 @@
|
|
|
1
|
+
puts "Seeding showcase data..."
|
|
2
|
+
|
|
3
|
+
# Clear existing data so seeds are re-runnable (children before parents)
|
|
4
|
+
# Also reset auto-increment counters so IDs start from 1 (demo_path links use hardcoded IDs)
|
|
5
|
+
connection = ActiveRecord::Base.connection
|
|
6
|
+
%w[
|
|
7
|
+
showcase_grade showcase_student showcase_school_class
|
|
8
|
+
saved_filter feature pipeline_stage pipeline showcase_batch_task showcase_item_class showcase_recipe showcase_positioning showcase_userstamps
|
|
9
|
+
showcase_sequence gapfree_sequence
|
|
10
|
+
showcase_job_execution
|
|
11
|
+
workflow_approval_task workflow_approval_step workflow_approval_request
|
|
12
|
+
workflow_audit_log showcase_workflow
|
|
13
|
+
showcase_audit_log showcase_audited_record page_config
|
|
14
|
+
showcase_custom_render
|
|
15
|
+
showcase_person showcase_organization showcase_contact
|
|
16
|
+
showcase_memo showcase_report
|
|
17
|
+
showcase_soft_delete_item showcase_soft_delete showcase_aggregate_item showcase_aggregate showcase_aggregate_company
|
|
18
|
+
showcase_virtual_field showcase_extensibility permission_config role showcase_permission
|
|
19
|
+
showcase_attachment custom_field_definition employee_skill project showcase_search showcase_array
|
|
20
|
+
showcase_hr_employee showcase_division showcase_business_unit
|
|
21
|
+
showcase_announcement
|
|
22
|
+
employee skill department showcase_form comment article_tag tag article
|
|
23
|
+
author category showcase_model showcase_field
|
|
24
|
+
group_role_mapping group_membership group
|
|
25
|
+
showcase_condition_task showcase_condition_threshold showcase_condition showcase_condition_category
|
|
26
|
+
lcp_error_log
|
|
27
|
+
export_profile export_log
|
|
28
|
+
].each do |model_name|
|
|
29
|
+
next unless LcpRuby.registry.registered?(model_name)
|
|
30
|
+
model = LcpRuby.registry.model_for(model_name)
|
|
31
|
+
model.delete_all
|
|
32
|
+
connection.execute("DELETE FROM sqlite_sequence WHERE name='#{model.table_name}'") if connection.adapter_name == "SQLite"
|
|
33
|
+
end
|
|
34
|
+
puts " Cleared existing seed data"
|
|
35
|
+
|
|
36
|
+
# Create default admin user (independent of authentication mode — showcase
|
|
37
|
+
# always provides a local admin login for demos)
|
|
38
|
+
if defined?(LcpRuby::User)
|
|
39
|
+
LcpRuby::User.find_or_create_by!(email: "admin@example.com") do |u|
|
|
40
|
+
u.name = "Admin User"
|
|
41
|
+
u.password = "password123"
|
|
42
|
+
u.password_confirmation = "password123"
|
|
43
|
+
u.lcp_role = [ "admin" ]
|
|
44
|
+
u.profile_data = {
|
|
45
|
+
theme: "dark",
|
|
46
|
+
locale: "en",
|
|
47
|
+
timezone: "America/New_York",
|
|
48
|
+
density: "comfortable",
|
|
49
|
+
border_radius: "rounded"
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
puts " Created admin user: admin@example.com / password123"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# ── Inherits-from cascade vignette ───────────────────────────────────────
|
|
56
|
+
# Placed early so it survives even if a later (unrelated) seed block fails.
|
|
57
|
+
# School registry: school_class → student → grade. Permission YAML files
|
|
58
|
+
# under config/lcp_ruby/permissions/showcase_{school_class,student,grade}.yml
|
|
59
|
+
# demonstrate the `inherits_from:` declarative cascade — see
|
|
60
|
+
# docs/guides/inherited-permissions.md for the walkthrough.
|
|
61
|
+
#
|
|
62
|
+
# Seed graph (teacher_id values are hypothetical user IDs — replace with
|
|
63
|
+
# real user IDs in your local seed if you want to demo role-switching):
|
|
64
|
+
#
|
|
65
|
+
# class_alpha (teacher_id=100) class_beta (teacher_id=200)
|
|
66
|
+
# ├── alice (guardian_id=300) ├── carol (guardian_id=999)
|
|
67
|
+
# │ ├── grade(maths,5) │ └── grade(maths,3)
|
|
68
|
+
# │ └── grade(english,4)
|
|
69
|
+
# └── bob (guardian_id=999)
|
|
70
|
+
# └── grade(maths,5)
|
|
71
|
+
#
|
|
72
|
+
# What the cascade produces under different roles:
|
|
73
|
+
# - teacher (id=100): sees class_alpha, alice, bob, alice's & bob's grades
|
|
74
|
+
# - teacher (id=200): sees class_beta, carol, carol's grade
|
|
75
|
+
# - parent (id=300): sees ONLY alice and her two grades (guardian scope)
|
|
76
|
+
# - admin / viewer: sees everything
|
|
77
|
+
SchoolClassModel = LcpRuby.registry.model_for("showcase_school_class")
|
|
78
|
+
StudentModel = LcpRuby.registry.model_for("showcase_student")
|
|
79
|
+
GradeModel = LcpRuby.registry.model_for("showcase_grade")
|
|
80
|
+
|
|
81
|
+
class_alpha = SchoolClassModel.create!(name: "Class Alpha", year: 2026, teacher_id: 100)
|
|
82
|
+
class_beta = SchoolClassModel.create!(name: "Class Beta", year: 2026, teacher_id: 200)
|
|
83
|
+
|
|
84
|
+
alice = StudentModel.create!(name: "Alice", guardian_id: 300, school_class_id: class_alpha.id)
|
|
85
|
+
bob = StudentModel.create!(name: "Bob", guardian_id: 999, school_class_id: class_alpha.id)
|
|
86
|
+
carol = StudentModel.create!(name: "Carol", guardian_id: 999, school_class_id: class_beta.id)
|
|
87
|
+
|
|
88
|
+
GradeModel.create!(subject: "maths", value: 5, recorded_on: Date.today - 7, student_id: alice.id)
|
|
89
|
+
GradeModel.create!(subject: "english", value: 4, recorded_on: Date.today - 14, student_id: alice.id)
|
|
90
|
+
GradeModel.create!(subject: "maths", value: 5, recorded_on: Date.today - 7, student_id: bob.id)
|
|
91
|
+
GradeModel.create!(subject: "maths", value: 3, recorded_on: Date.today - 7, student_id: carol.id)
|
|
92
|
+
puts " Created school registry vignette (2 classes, 3 students, 4 grades)"
|
|
93
|
+
|
|
94
|
+
# Phase 1: Field Types
|
|
95
|
+
FieldModel = LcpRuby.registry.model_for("showcase_field")
|
|
96
|
+
|
|
97
|
+
[
|
|
98
|
+
{
|
|
99
|
+
title: "Product Launch Campaign",
|
|
100
|
+
description: "A comprehensive marketing campaign for our new product line featuring advanced targeting and multi-channel distribution.",
|
|
101
|
+
count: 42,
|
|
102
|
+
rating_value: 4.5,
|
|
103
|
+
price: 1299.99,
|
|
104
|
+
is_active: true,
|
|
105
|
+
start_date: Date.today + 7,
|
|
106
|
+
event_time: Time.current + 3.days,
|
|
107
|
+
status: "active",
|
|
108
|
+
priority: "high",
|
|
109
|
+
metadata: { category: "marketing", tags: [ "launch", "q1" ] }.to_json,
|
|
110
|
+
external_id: SecureRandom.uuid,
|
|
111
|
+
email: "campaign@example.com",
|
|
112
|
+
phone: "+1 (555) 123-4567",
|
|
113
|
+
website: "https://example.com/campaigns",
|
|
114
|
+
brand_color: "#3498db"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
title: "Q4 Budget Review",
|
|
118
|
+
description: "Annual budget review for Q4 fiscal year.",
|
|
119
|
+
count: 156,
|
|
120
|
+
rating_value: 3.0,
|
|
121
|
+
price: 50000.00,
|
|
122
|
+
is_active: true,
|
|
123
|
+
start_date: Date.today - 30,
|
|
124
|
+
event_time: Time.current - 2.weeks,
|
|
125
|
+
status: "active",
|
|
126
|
+
priority: "critical",
|
|
127
|
+
metadata: { department: "finance" }.to_json,
|
|
128
|
+
external_id: SecureRandom.uuid,
|
|
129
|
+
email: "finance@example.com",
|
|
130
|
+
phone: "+44 20 7946 0958",
|
|
131
|
+
website: "https://finance.example.com",
|
|
132
|
+
brand_color: "#e74c3c"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
title: "Draft Proposal",
|
|
136
|
+
description: nil,
|
|
137
|
+
count: 0,
|
|
138
|
+
rating_value: nil,
|
|
139
|
+
price: nil,
|
|
140
|
+
is_active: false,
|
|
141
|
+
start_date: nil,
|
|
142
|
+
event_time: nil,
|
|
143
|
+
status: "draft",
|
|
144
|
+
priority: "low",
|
|
145
|
+
metadata: nil,
|
|
146
|
+
external_id: nil,
|
|
147
|
+
email: nil,
|
|
148
|
+
phone: nil,
|
|
149
|
+
website: nil,
|
|
150
|
+
brand_color: nil
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
title: "Archived Project Alpha",
|
|
154
|
+
description: "This project was completed and archived last quarter.",
|
|
155
|
+
count: 999,
|
|
156
|
+
rating_value: 5.0,
|
|
157
|
+
price: 100.50,
|
|
158
|
+
is_active: false,
|
|
159
|
+
start_date: Date.today - 90,
|
|
160
|
+
event_time: Time.current - 3.months,
|
|
161
|
+
status: "archived",
|
|
162
|
+
priority: "medium",
|
|
163
|
+
metadata: { archived_by: "admin", reason: "completed" }.to_json,
|
|
164
|
+
external_id: SecureRandom.uuid,
|
|
165
|
+
email: "alpha@example.com",
|
|
166
|
+
phone: "555-0000",
|
|
167
|
+
website: "http://alpha.example.com",
|
|
168
|
+
brand_color: "#2ecc71"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
title: "Deleted Record Example",
|
|
172
|
+
description: "This demonstrates a deleted status record that still appears in lists.",
|
|
173
|
+
count: -1,
|
|
174
|
+
rating_value: 0.5,
|
|
175
|
+
price: 0.01,
|
|
176
|
+
is_active: false,
|
|
177
|
+
start_date: Date.today - 365,
|
|
178
|
+
event_time: Time.current - 1.year,
|
|
179
|
+
status: "deleted",
|
|
180
|
+
priority: "low",
|
|
181
|
+
metadata: { deleted_at: Time.current.iso8601 }.to_json,
|
|
182
|
+
external_id: SecureRandom.uuid,
|
|
183
|
+
email: "deleted@example.com",
|
|
184
|
+
phone: nil,
|
|
185
|
+
website: nil,
|
|
186
|
+
brand_color: "#95a5a6"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
title: "International Event",
|
|
190
|
+
description: "Multi-language conference with speakers from 20 countries. Features include live translation, virtual attendance, and networking sessions.",
|
|
191
|
+
count: 2500,
|
|
192
|
+
rating_value: 4.8,
|
|
193
|
+
price: 75000.00,
|
|
194
|
+
is_active: true,
|
|
195
|
+
start_date: Date.today + 60,
|
|
196
|
+
event_time: Time.current + 2.months,
|
|
197
|
+
status: "active",
|
|
198
|
+
priority: "critical",
|
|
199
|
+
metadata: { countries: 20, languages: [ "en", "de", "fr", "es", "ja" ] }.to_json,
|
|
200
|
+
external_id: SecureRandom.uuid,
|
|
201
|
+
email: "events@globalconf.example.com",
|
|
202
|
+
phone: "+49 30 12345678",
|
|
203
|
+
website: "https://globalconf.example.com",
|
|
204
|
+
brand_color: "#9b59b6"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
title: "Weekly Status Report",
|
|
208
|
+
description: "Standard weekly update.",
|
|
209
|
+
count: 52,
|
|
210
|
+
rating_value: 2.0,
|
|
211
|
+
price: 0.00,
|
|
212
|
+
is_active: true,
|
|
213
|
+
start_date: Date.today,
|
|
214
|
+
event_time: Time.current,
|
|
215
|
+
status: "active",
|
|
216
|
+
priority: "medium",
|
|
217
|
+
metadata: { frequency: "weekly" }.to_json,
|
|
218
|
+
external_id: SecureRandom.uuid,
|
|
219
|
+
email: "reports@example.com",
|
|
220
|
+
phone: "+1-800-555-0199",
|
|
221
|
+
website: "https://reports.example.com/weekly",
|
|
222
|
+
brand_color: "#f39c12"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
title: "High-Priority Security Audit",
|
|
226
|
+
description: "Mandatory security compliance audit required by regulatory framework. All systems must pass penetration testing.",
|
|
227
|
+
count: 7,
|
|
228
|
+
rating_value: 1.0,
|
|
229
|
+
price: 25000.00,
|
|
230
|
+
is_active: true,
|
|
231
|
+
start_date: Date.today + 14,
|
|
232
|
+
event_time: Time.current + 2.weeks,
|
|
233
|
+
status: "draft",
|
|
234
|
+
priority: "critical",
|
|
235
|
+
metadata: { compliance: "SOC2", systems: [ "api", "web", "mobile" ] }.to_json,
|
|
236
|
+
external_id: SecureRandom.uuid,
|
|
237
|
+
email: "security@example.com",
|
|
238
|
+
phone: "+1 555 987 6543",
|
|
239
|
+
website: "https://security.example.com",
|
|
240
|
+
brand_color: "#c0392b"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
title: "Team Building Activity",
|
|
244
|
+
description: "Outdoor team building event for the engineering department.",
|
|
245
|
+
count: 35,
|
|
246
|
+
rating_value: 3.5,
|
|
247
|
+
price: 1500.00,
|
|
248
|
+
is_active: true,
|
|
249
|
+
start_date: Date.today + 21,
|
|
250
|
+
event_time: Time.current + 3.weeks,
|
|
251
|
+
status: "active",
|
|
252
|
+
priority: "low",
|
|
253
|
+
metadata: nil,
|
|
254
|
+
external_id: SecureRandom.uuid,
|
|
255
|
+
email: "hr@example.com",
|
|
256
|
+
phone: "+1 (555) 456-7890",
|
|
257
|
+
website: nil,
|
|
258
|
+
brand_color: "#1abc9c"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
title: "API v2 Migration",
|
|
262
|
+
description: "Migrate all services from API v1 to v2. This involves updating authentication, rate limiting, and response formats.",
|
|
263
|
+
count: 128,
|
|
264
|
+
rating_value: 4.0,
|
|
265
|
+
price: 0.00,
|
|
266
|
+
is_active: true,
|
|
267
|
+
start_date: Date.today - 7,
|
|
268
|
+
event_time: Time.current - 1.week,
|
|
269
|
+
status: "active",
|
|
270
|
+
priority: "high",
|
|
271
|
+
metadata: { version: "2.0", breaking_changes: true, affected_services: 12 }.to_json,
|
|
272
|
+
external_id: SecureRandom.uuid,
|
|
273
|
+
email: "api-team@example.com",
|
|
274
|
+
phone: nil,
|
|
275
|
+
website: "https://api.example.com/v2/docs",
|
|
276
|
+
brand_color: "#2c3e50"
|
|
277
|
+
}
|
|
278
|
+
].each do |attrs|
|
|
279
|
+
FieldModel.create!(attrs)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
puts " Created #{FieldModel.count} showcase_field records"
|
|
283
|
+
|
|
284
|
+
# Phase 2: Model Features
|
|
285
|
+
ModelModel = LcpRuby.registry.model_for("showcase_model")
|
|
286
|
+
|
|
287
|
+
[
|
|
288
|
+
{ name: "Alpha Project", code: "alpha-001", status: "active", amount: 5000.00, max_value: 100, min_value: 10, email: "alpha@example.com", phone: "+1 555 111 2222", website: "https://alpha.example.com", tags_json: { priority: "high" }.to_json },
|
|
289
|
+
{ name: "Beta Release", code: "beta-release", status: "active", amount: 12500.50, max_value: 200, min_value: 50, email: "beta@example.com", tags_json: { version: "2.0" }.to_json },
|
|
290
|
+
{ name: "Draft Spec", code: "draft-spec", status: "draft", amount: nil, max_value: 50, min_value: 0, tags_json: nil },
|
|
291
|
+
{ name: "Completed Task", code: "done-task", status: "completed", amount: 1000.00, max_value: 10, min_value: 1, email: "done@example.com", website: "https://done.example.com" },
|
|
292
|
+
{ name: "Cancelled Order", code: "cancelled-123", status: "cancelled", amount: 750.00, max_value: 500, min_value: 100, phone: "+44 20 1234 5678" },
|
|
293
|
+
{ name: " Whitespace Test ", code: " UPPER_case ", status: "draft", amount: nil, max_value: 999, min_value: 1 },
|
|
294
|
+
{ name: "Edge Case Zero", code: "zero-case", status: "active", amount: 0.00, max_value: 0, min_value: -10 },
|
|
295
|
+
{ name: "Large Values", code: "large-vals", status: "active", amount: 99999999.99, max_value: 9999, min_value: 1, email: "large@example.com", website: "http://large.example.com" }
|
|
296
|
+
].each do |attrs|
|
|
297
|
+
ModelModel.create!(attrs)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
puts " Created #{ModelModel.count} showcase_model records"
|
|
301
|
+
|
|
302
|
+
# Phase 3: Associations & Nested Forms
|
|
303
|
+
AuthorModel = LcpRuby.registry.model_for("author")
|
|
304
|
+
CategoryModel = LcpRuby.registry.model_for("category")
|
|
305
|
+
TagModel = LcpRuby.registry.model_for("tag")
|
|
306
|
+
ArticleModel = LcpRuby.registry.model_for("article")
|
|
307
|
+
CommentModel = LcpRuby.registry.model_for("comment")
|
|
308
|
+
ArticleTagModel = LcpRuby.registry.model_for("article_tag")
|
|
309
|
+
|
|
310
|
+
# Authors
|
|
311
|
+
authors = [
|
|
312
|
+
AuthorModel.create!(name: "Alice Chen", email: "alice@example.com", bio: "Senior technical writer with 10 years of experience."),
|
|
313
|
+
AuthorModel.create!(name: "Bob Martinez", email: "bob@example.com", bio: "Staff engineer and occasional blogger."),
|
|
314
|
+
AuthorModel.create!(name: "Carol Williams", email: "carol@example.com", bio: "Product manager passionate about documentation."),
|
|
315
|
+
AuthorModel.create!(name: "David Kim", email: "david@example.com", bio: "Open source contributor and educator."),
|
|
316
|
+
AuthorModel.create!(name: "Eva Novak", email: "eva@example.com", bio: "DevOps engineer and automation enthusiast.")
|
|
317
|
+
]
|
|
318
|
+
puts " Created #{authors.size} authors"
|
|
319
|
+
|
|
320
|
+
# Categories (4 levels)
|
|
321
|
+
tech = CategoryModel.create!(name: "Technology", description: "All technology related articles.")
|
|
322
|
+
web = CategoryModel.create!(name: "Web Development", description: "Frontend and backend web technologies.", parent_id: tech.id)
|
|
323
|
+
mobile = CategoryModel.create!(name: "Mobile Development", description: "iOS and Android development.", parent_id: tech.id)
|
|
324
|
+
frontend = CategoryModel.create!(name: "Frontend", description: "React, Vue, Angular and more.", parent_id: web.id)
|
|
325
|
+
backend = CategoryModel.create!(name: "Backend", description: "APIs, databases, and server-side.", parent_id: web.id)
|
|
326
|
+
react_eco = CategoryModel.create!(name: "React Ecosystem", description: "React, Next.js, Remix, and related libraries.", parent_id: frontend.id)
|
|
327
|
+
css_styling = CategoryModel.create!(name: "CSS & Styling", description: "CSS frameworks, Tailwind, PostCSS, and design systems.", parent_id: frontend.id)
|
|
328
|
+
|
|
329
|
+
business = CategoryModel.create!(name: "Business", description: "Business strategy and management.")
|
|
330
|
+
startup = CategoryModel.create!(name: "Startups", description: "Startup ecosystem and entrepreneurship.", parent_id: business.id)
|
|
331
|
+
enterprise = CategoryModel.create!(name: "Enterprise", description: "Large-scale business solutions.", parent_id: business.id)
|
|
332
|
+
|
|
333
|
+
science = CategoryModel.create!(name: "Science", description: "Scientific discoveries and research.")
|
|
334
|
+
puts " Created #{CategoryModel.count} categories"
|
|
335
|
+
|
|
336
|
+
# Tags
|
|
337
|
+
tag_names = [
|
|
338
|
+
{ name: "Ruby", color: "#cc342d" },
|
|
339
|
+
{ name: "Rails", color: "#cc0000" },
|
|
340
|
+
{ name: "JavaScript", color: "#f7df1e" },
|
|
341
|
+
{ name: "TypeScript", color: "#3178c6" },
|
|
342
|
+
{ name: "React", color: "#61dafb" },
|
|
343
|
+
{ name: "Vue", color: "#42b883" },
|
|
344
|
+
{ name: "Python", color: "#3776ab" },
|
|
345
|
+
{ name: "Docker", color: "#2496ed" },
|
|
346
|
+
{ name: "AWS", color: "#ff9900" },
|
|
347
|
+
{ name: "DevOps", color: "#0db7ed" },
|
|
348
|
+
{ name: "Testing", color: "#8bc34a" },
|
|
349
|
+
{ name: "Performance", color: "#ff5722" },
|
|
350
|
+
{ name: "Security", color: "#f44336" },
|
|
351
|
+
{ name: "Tutorial", color: "#9c27b0" },
|
|
352
|
+
{ name: "Best Practices", color: "#607d8b" }
|
|
353
|
+
]
|
|
354
|
+
tags = tag_names.map { |t| TagModel.create!(t) }
|
|
355
|
+
puts " Created #{tags.size} tags"
|
|
356
|
+
|
|
357
|
+
# Articles with comments and tags
|
|
358
|
+
articles_data = [
|
|
359
|
+
{ title: "Getting Started with Ruby on Rails", slug: "getting-started-with-ruby-on-rails", body: "A comprehensive guide to building your first Rails application.", status: "published", category: frontend, author: authors[0], tags: [ 0, 1, 13 ], comments: [ [ "Great article!", "Reader1" ], [ "Very helpful", "Reader2" ] ] },
|
|
360
|
+
{ title: "Advanced React Patterns", slug: "advanced-react-patterns", body: "Exploring compound components, render props, and hooks.", status: "published", category: frontend, author: authors[1], tags: [ 2, 4, 14 ], comments: [ [ "Love the hooks examples", "DevFan" ] ] },
|
|
361
|
+
{ title: "Building REST APIs with Rails", slug: "building-rest-apis-with-rails", body: "Best practices for designing RESTful APIs.", status: "published", category: backend, author: authors[0], tags: [ 0, 1, 14 ], comments: [ [ "What about GraphQL?", "APIUser" ], [ "Solid patterns", "BackendDev" ], [ "More examples please", "Newbie" ] ] },
|
|
362
|
+
{ title: "Docker for Development", slug: "docker-for-development", body: "Setting up Docker for local development environments.", status: "published", category: backend, author: authors[4], tags: [ 7, 9 ], comments: [] },
|
|
363
|
+
{ title: "Mobile-First Design Principles", slug: "mobile-first-design-principles", body: "Why mobile-first approach matters for modern applications.", status: "published", category: mobile, author: authors[2], tags: [ 14 ], comments: [ [ "Responsive > Mobile-first", "WebDev" ] ] },
|
|
364
|
+
{ title: "TypeScript Migration Guide", slug: "typescript-migration-guide", body: "Step by step guide to migrating JavaScript to TypeScript.", status: "draft", category: frontend, author: authors[1], tags: [ 2, 3 ], comments: [] },
|
|
365
|
+
{ title: "AWS Lambda Best Practices", slug: "aws-lambda-best-practices", body: "Optimizing serverless functions for production.", status: "published", category: backend, author: authors[4], tags: [ 8, 9, 11 ], comments: [ [ "Cold starts are still an issue", "CloudUser" ] ] },
|
|
366
|
+
{ title: "Vue 3 Composition API", slug: "vue-3-composition-api", body: "Understanding the new composition API in Vue 3.", status: "published", category: frontend, author: authors[3], tags: [ 2, 5 ], comments: [ [ "Finally!", "VueFan" ], [ "Great comparison with Options API", "Dev123" ] ] },
|
|
367
|
+
{ title: "Security Best Practices for Web Apps", slug: "security-best-practices-for-web-apps", body: "OWASP top 10 and how to protect your application.", status: "draft", category: web, author: authors[4], tags: [ 12, 14 ], comments: [] },
|
|
368
|
+
{ title: "Python for Data Science", slug: "python-for-data-science", body: "Introduction to pandas, numpy, and matplotlib.", status: "published", category: science, author: authors[3], tags: [ 6, 13 ], comments: [ [ "Can you cover scikit-learn?", "DataNerd" ] ] },
|
|
369
|
+
{ title: "Startup Metrics That Matter", slug: "startup-metrics-that-matter", body: "Key performance indicators for early-stage startups.", status: "published", category: startup, author: authors[2], tags: [ 14 ], comments: [] },
|
|
370
|
+
{ title: "Enterprise Architecture Patterns", slug: "enterprise-architecture-patterns", body: "Scaling applications for enterprise use.", status: "draft", category: enterprise, author: authors[0], tags: [ 14, 11 ], comments: [] },
|
|
371
|
+
{ title: "Testing Rails Applications", slug: "testing-rails-applications", body: "RSpec, Capybara, and factory_bot patterns.", status: "published", category: backend, author: authors[0], tags: [ 0, 1, 10 ], comments: [ [ "What about minitest?", "Tester" ], [ "Factory bot is essential", "QAEngineer" ] ] },
|
|
372
|
+
{ title: "Performance Optimization in React", slug: "performance-optimization-in-react", body: "Memoization, code splitting, and lazy loading.", status: "published", category: frontend, author: authors[1], tags: [ 2, 4, 11 ], comments: [ [ "useMemo vs useCallback?", "ReactDev" ] ] },
|
|
373
|
+
{ title: "DevOps Culture and Practices", slug: "devops-culture-and-practices", body: "Building a DevOps culture in your organization.", status: "archived", category: tech, author: authors[4], tags: [ 7, 8, 9 ], comments: [] },
|
|
374
|
+
{ title: "Server Components in Next.js", slug: "server-components-in-nextjs", body: "Understanding React Server Components and their impact on data fetching and rendering strategies.", status: "published", category: react_eco, author: authors[1], tags: [ 2, 4, 11 ], comments: [ [ "Game changer for SSR!", "NextFan" ] ] },
|
|
375
|
+
{ title: "State Management with Zustand", slug: "state-management-with-zustand", body: "A lightweight alternative to Redux for React state management.", status: "published", category: react_eco, author: authors[3], tags: [ 2, 4, 14 ], comments: [] },
|
|
376
|
+
{ title: "Tailwind CSS Best Practices", slug: "tailwind-css-best-practices", body: "Utility-first CSS patterns, custom themes, and component extraction strategies.", status: "published", category: css_styling, author: authors[2], tags: [ 14 ], comments: [ [ "Finally moved from SCSS to Tailwind", "CSSFan" ] ] }
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
articles_data.each do |data|
|
|
380
|
+
article = ArticleModel.create!(
|
|
381
|
+
title: data[:title],
|
|
382
|
+
slug: data[:slug],
|
|
383
|
+
body: data[:body],
|
|
384
|
+
status: data[:status],
|
|
385
|
+
word_count: data[:body].split.size,
|
|
386
|
+
category_id: data[:category].id,
|
|
387
|
+
author_id: data[:author].id
|
|
388
|
+
)
|
|
389
|
+
data[:tags].each { |tag_idx| ArticleTagModel.create!(article_id: article.id, tag_id: tags[tag_idx].id) }
|
|
390
|
+
data[:comments].each_with_index do |(body, author_name), idx|
|
|
391
|
+
CommentModel.create!(article_id: article.id, body: body, author_name: author_name, position: idx + 1)
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
puts " Created #{ArticleModel.count} articles, #{CommentModel.count} comments, #{ArticleTagModel.count} article-tag links"
|
|
396
|
+
|
|
397
|
+
# Extra comments on first article for pagination demo (>10 to exceed per_page 10 on article_comments_zone)
|
|
398
|
+
first_article = ArticleModel.first
|
|
399
|
+
(1..13).each do |i|
|
|
400
|
+
CommentModel.create!(
|
|
401
|
+
article_id: first_article.id,
|
|
402
|
+
body: "Discussion point #{i}: Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
|
|
403
|
+
author_name: "Reader#{i + 2}",
|
|
404
|
+
position: i + 2
|
|
405
|
+
)
|
|
406
|
+
end
|
|
407
|
+
puts " Added #{13} extra comments to '#{first_article.title}' for pagination demo (#{CommentModel.where(article_id: first_article.id).count} total)"
|
|
408
|
+
|
|
409
|
+
# Phase 4: Form Features
|
|
410
|
+
FormModel = LcpRuby.registry.model_for("showcase_form")
|
|
411
|
+
|
|
412
|
+
[
|
|
413
|
+
{ name: "Simple Form", form_type: "simple", priority: 25, satisfaction: 3, is_premium: false, reason: nil },
|
|
414
|
+
{ name: "Advanced Config", form_type: "advanced", priority: 75, satisfaction: 4, is_premium: true, reason: "VIP customer", advanced_field_1: "Custom value", advanced_field_2: "Extra data" },
|
|
415
|
+
{ name: "Special Request", form_type: "special", priority: 100, satisfaction: 5, is_premium: true, reason: "Partnership", rejection_reason: "Pending approval" },
|
|
416
|
+
{ name: "Basic Entry", form_type: "simple", priority: 50, satisfaction: 2, is_premium: false },
|
|
417
|
+
{ name: "Premium Advanced", form_type: "advanced", priority: 90, satisfaction: 5, is_premium: true, reason: "Enterprise tier", advanced_field_1: "Enterprise", config_data: { feature_flags: [ "beta", "api_v2" ] }.to_json }
|
|
418
|
+
].each do |attrs|
|
|
419
|
+
FormModel.create!(attrs)
|
|
420
|
+
end
|
|
421
|
+
puts " Created #{FormModel.count} showcase_form records"
|
|
422
|
+
|
|
423
|
+
# Phase 5: Selects
|
|
424
|
+
DeptModel = LcpRuby.registry.model_for("department")
|
|
425
|
+
SkillModel = LcpRuby.registry.model_for("skill")
|
|
426
|
+
EmpModel = LcpRuby.registry.model_for("employee")
|
|
427
|
+
EmpSkillModel = LcpRuby.registry.model_for("employee_skill")
|
|
428
|
+
ProjModel = LcpRuby.registry.model_for("project")
|
|
429
|
+
|
|
430
|
+
# Departments (3 levels)
|
|
431
|
+
eng = DeptModel.create!(name: "Engineering", code: "eng")
|
|
432
|
+
fe = DeptModel.create!(name: "Frontend", code: "eng-fe", parent_id: eng.id)
|
|
433
|
+
be = DeptModel.create!(name: "Backend", code: "eng-be", parent_id: eng.id)
|
|
434
|
+
devops = DeptModel.create!(name: "DevOps", code: "eng-devops", parent_id: eng.id)
|
|
435
|
+
react_team = DeptModel.create!(name: "React Team", code: "eng-fe-react", parent_id: fe.id)
|
|
436
|
+
api_team = DeptModel.create!(name: "API Team", code: "eng-be-api", parent_id: be.id)
|
|
437
|
+
|
|
438
|
+
design = DeptModel.create!(name: "Design", code: "design")
|
|
439
|
+
ux = DeptModel.create!(name: "UX Design", code: "design-ux", parent_id: design.id)
|
|
440
|
+
ui = DeptModel.create!(name: "UI Design", code: "design-ui", parent_id: design.id)
|
|
441
|
+
|
|
442
|
+
mgmt = DeptModel.create!(name: "Management", code: "mgmt")
|
|
443
|
+
puts " Created #{DeptModel.count} departments"
|
|
444
|
+
|
|
445
|
+
# Skills
|
|
446
|
+
skills = [
|
|
447
|
+
{ name: "Ruby", category: "technical" },
|
|
448
|
+
{ name: "JavaScript", category: "technical" },
|
|
449
|
+
{ name: "Python", category: "technical" },
|
|
450
|
+
{ name: "React", category: "technical" },
|
|
451
|
+
{ name: "Docker", category: "technical" },
|
|
452
|
+
{ name: "AWS", category: "technical" },
|
|
453
|
+
{ name: "Communication", category: "soft" },
|
|
454
|
+
{ name: "Leadership", category: "management" },
|
|
455
|
+
{ name: "Project Management", category: "management" },
|
|
456
|
+
{ name: "English", category: "language" },
|
|
457
|
+
{ name: "German", category: "language" },
|
|
458
|
+
{ name: "Testing", category: "technical" },
|
|
459
|
+
{ name: "CSS", category: "technical" },
|
|
460
|
+
{ name: "SQL", category: "technical" },
|
|
461
|
+
{ name: "Teamwork", category: "soft" }
|
|
462
|
+
].map { |s| SkillModel.create!(s) }
|
|
463
|
+
puts " Created #{skills.size} skills"
|
|
464
|
+
|
|
465
|
+
# Employees
|
|
466
|
+
employees = [
|
|
467
|
+
EmpModel.create!(name: "Jane Smith", email: "jane@example.com", role: "manager", status: "active", department_id: eng.id),
|
|
468
|
+
EmpModel.create!(name: "John Doe", email: "john@example.com", role: "developer", status: "active", department_id: fe.id),
|
|
469
|
+
EmpModel.create!(name: "Alice Brown", email: "alice.b@example.com", role: "developer", status: "active", department_id: be.id),
|
|
470
|
+
EmpModel.create!(name: "Bob Wilson", email: "bob.w@example.com", role: "developer", status: "active", department_id: react_team.id),
|
|
471
|
+
EmpModel.create!(name: "Carol Davis", email: "carol@example.com", role: "designer", status: "active", department_id: ux.id),
|
|
472
|
+
EmpModel.create!(name: "Dan Lee", email: "dan@example.com", role: "admin", status: "active", department_id: mgmt.id),
|
|
473
|
+
EmpModel.create!(name: "Eva Green", email: "eva.g@example.com", role: "developer", status: "on_leave", department_id: devops.id),
|
|
474
|
+
EmpModel.create!(name: "Frank White", email: "frank@example.com", role: "intern", status: "active", department_id: api_team.id),
|
|
475
|
+
EmpModel.create!(name: "Grace Chen", email: "grace@example.com", role: "developer", status: "active", department_id: be.id),
|
|
476
|
+
EmpModel.create!(name: "Henry Kim", email: "henry@example.com", role: "designer", status: "active", department_id: ui.id),
|
|
477
|
+
EmpModel.create!(name: "Iris Taylor", email: "iris@example.com", role: "developer", status: "terminated", department_id: fe.id),
|
|
478
|
+
EmpModel.create!(name: "Jack Brown", email: "jack@example.com", role: "developer", status: "archived", department_id: devops.id)
|
|
479
|
+
]
|
|
480
|
+
# Set mentors
|
|
481
|
+
employees[1].update!(mentor_id: employees[0].id)
|
|
482
|
+
employees[3].update!(mentor_id: employees[1].id)
|
|
483
|
+
employees[7].update!(mentor_id: employees[2].id)
|
|
484
|
+
|
|
485
|
+
# Link Dan Lee (admin-role employee) to the admin user so
|
|
486
|
+
# /showcase/my-employee-profile/me resolves for the admin login.
|
|
487
|
+
if LcpRuby.configuration.authentication == :built_in
|
|
488
|
+
admin_user = LcpRuby::User.find_by(email: "admin@example.com")
|
|
489
|
+
employees[5].update!(user_id: admin_user.id) if admin_user
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
puts " Created #{employees.size} employees"
|
|
493
|
+
|
|
494
|
+
# Employee skills
|
|
495
|
+
[ [ 0, [ 0, 6, 7, 8, 9 ] ], [ 1, [ 1, 3, 12, 9 ] ], [ 2, [ 0, 1, 13, 11 ] ], [ 3, [ 1, 3, 9 ] ], [ 4, [ 12, 6, 14 ] ], [ 5, [ 7, 8, 6, 9 ] ], [ 6, [ 4, 5, 0 ] ], [ 7, [ 0, 13, 11 ] ], [ 8, [ 0, 2, 13 ] ], [ 9, [ 12, 6 ] ] ].each do |emp_idx, skill_idxs|
|
|
496
|
+
skill_idxs.each { |si| EmpSkillModel.create!(employee_id: employees[emp_idx].id, skill_id: skills[si].id) }
|
|
497
|
+
end
|
|
498
|
+
puts " Created #{EmpSkillModel.count} employee-skill links"
|
|
499
|
+
|
|
500
|
+
# Employee profiles (has_one)
|
|
501
|
+
if LcpRuby.registry.registered?("employee_profile")
|
|
502
|
+
ProfileModel = LcpRuby.registry.model_for("employee_profile")
|
|
503
|
+
[
|
|
504
|
+
{ employee_id: employees[0].id, hire_date: "2019-03-15", salary: 95_000, date_of_birth: "1985-06-20", national_id: "850620/1234", bank_account: "CZ6508000000192000145399", phone: "+420-602-123-456", address: "Vinohradska 12, Prague 2" },
|
|
505
|
+
{ employee_id: employees[1].id, hire_date: "2020-07-01", salary: 82_000, date_of_birth: "1990-11-05", national_id: "901105/5678", bank_account: "CZ6508000000192000145400", phone: "+420-603-234-567", address: "Karlova 8, Prague 1" },
|
|
506
|
+
{ employee_id: employees[2].id, hire_date: "2021-01-10", salary: 88_000, date_of_birth: "1988-02-14", national_id: "880214/9012", phone: "+420-604-345-678" },
|
|
507
|
+
{ employee_id: employees[3].id, hire_date: "2022-04-01", salary: 75_000, date_of_birth: "1995-08-30" },
|
|
508
|
+
{ employee_id: employees[4].id, hire_date: "2020-09-15", salary: 78_000, date_of_birth: "1992-12-01", national_id: "921201/3456", phone: "+420-605-456-789", address: "Nerudova 3, Prague 1" },
|
|
509
|
+
{ employee_id: employees[5].id, hire_date: "2018-01-02", salary: 120_000, date_of_birth: "1980-04-10", national_id: "800410/7890", bank_account: "CZ6508000000192000145401", phone: "+420-601-567-890", address: "Celetna 5, Prague 1", notes: "Company founder" },
|
|
510
|
+
{ employee_id: employees[6].id, hire_date: "2021-06-01", salary: 90_000, date_of_birth: "1993-07-22" },
|
|
511
|
+
{ employee_id: employees[8].id, hire_date: "2022-11-15", salary: 85_000, date_of_birth: "1991-09-18", national_id: "910918/2345", phone: "+420-607-789-012" }
|
|
512
|
+
].each { |attrs| ProfileModel.create!(attrs) }
|
|
513
|
+
puts " Created #{ProfileModel.count} employee profiles"
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
# Employee emergency contacts (has_one)
|
|
517
|
+
if LcpRuby.registry.registered?("employee_emergency_contact")
|
|
518
|
+
EmergencyModel = LcpRuby.registry.model_for("employee_emergency_contact")
|
|
519
|
+
[
|
|
520
|
+
{ employee_id: employees[0].id, contact_name: "Peter Smith", relationship: "spouse", phone: "+420-608-111-222", email: "peter.smith@email.cz" },
|
|
521
|
+
{ employee_id: employees[1].id, contact_name: "Mary Doe", relationship: "parent", phone: "+420-609-222-333" },
|
|
522
|
+
{ employee_id: employees[2].id, contact_name: "Tom Brown", relationship: "sibling", phone: "+420-610-333-444", email: "tom.b@email.cz" },
|
|
523
|
+
{ employee_id: employees[4].id, contact_name: "Lisa Davis", relationship: "spouse", phone: "+420-611-444-555" },
|
|
524
|
+
{ employee_id: employees[5].id, contact_name: "Sarah Lee", relationship: "spouse", phone: "+420-612-555-666", email: "sarah.lee@email.cz" },
|
|
525
|
+
{ employee_id: employees[8].id, contact_name: "James Chen", relationship: "parent", phone: "+420-613-666-777" }
|
|
526
|
+
].each { |attrs| EmergencyModel.create!(attrs) }
|
|
527
|
+
puts " Created #{EmergencyModel.count} employee emergency contacts"
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# Projects
|
|
531
|
+
[
|
|
532
|
+
{ name: "Website Redesign", status: "active", department_id: fe.id, lead_id: employees[1].id },
|
|
533
|
+
{ name: "API v3", status: "active", department_id: be.id, lead_id: employees[2].id },
|
|
534
|
+
{ name: "Cloud Migration", status: "active", department_id: devops.id, lead_id: employees[6].id },
|
|
535
|
+
{ name: "Brand Refresh", status: "completed", department_id: design.id, lead_id: employees[4].id },
|
|
536
|
+
{ name: "Internal Tools", status: "active", department_id: eng.id, lead_id: employees[0].id }
|
|
537
|
+
].each { |p| ProjModel.create!(p) }
|
|
538
|
+
puts " Created #{ProjModel.count} projects"
|
|
539
|
+
|
|
540
|
+
# Phase 5b: Custom Fields
|
|
541
|
+
# Create custom field definitions on employees (all types) and projects (practical subset)
|
|
542
|
+
CfdModel = LcpRuby.registry.model_for("custom_field_definition")
|
|
543
|
+
|
|
544
|
+
# -- Employee custom fields: one of every type for showcase --
|
|
545
|
+
employee_cfds = [
|
|
546
|
+
# String: nickname with length constraints
|
|
547
|
+
{
|
|
548
|
+
target_model: "employee", field_name: "nickname", custom_type: "string",
|
|
549
|
+
label: "Nickname", section: "Personal Info", position: 0,
|
|
550
|
+
active: true, required: false, placeholder: "e.g., Johnny",
|
|
551
|
+
hint: "Short display name used in casual contexts",
|
|
552
|
+
min_length: 2, max_length: 30,
|
|
553
|
+
show_in_table: true, sortable: true, searchable: true
|
|
554
|
+
},
|
|
555
|
+
# Text: bio with longer content
|
|
556
|
+
{
|
|
557
|
+
target_model: "employee", field_name: "bio", custom_type: "text",
|
|
558
|
+
label: "Biography", section: "Personal Info", position: 1,
|
|
559
|
+
active: true, required: false, placeholder: "Brief biography...",
|
|
560
|
+
max_length: 1000, show_in_table: false, show_in_form: true, show_in_show: true
|
|
561
|
+
},
|
|
562
|
+
# Integer: years of experience with min/max
|
|
563
|
+
{
|
|
564
|
+
target_model: "employee", field_name: "years_experience", custom_type: "integer",
|
|
565
|
+
label: "Years of Experience", section: "Professional", position: 0,
|
|
566
|
+
active: true, required: false, default_value: "0",
|
|
567
|
+
hint: "Total years of professional experience",
|
|
568
|
+
min_value: 0, max_value: 50,
|
|
569
|
+
show_in_table: true, sortable: true
|
|
570
|
+
},
|
|
571
|
+
# Float: performance score
|
|
572
|
+
{
|
|
573
|
+
target_model: "employee", field_name: "performance_score", custom_type: "float",
|
|
574
|
+
label: "Performance Score", section: "Professional", position: 1,
|
|
575
|
+
active: true, required: false,
|
|
576
|
+
min_value: 0.0, max_value: 10.0,
|
|
577
|
+
show_in_table: true, sortable: true
|
|
578
|
+
},
|
|
579
|
+
# Decimal: hourly rate with precision
|
|
580
|
+
{
|
|
581
|
+
target_model: "employee", field_name: "hourly_rate", custom_type: "decimal",
|
|
582
|
+
label: "Hourly Rate (USD)", section: "Compensation", position: 0,
|
|
583
|
+
active: true, required: false,
|
|
584
|
+
hint: "Base hourly rate before taxes and deductions",
|
|
585
|
+
min_value: 0, precision: 2,
|
|
586
|
+
show_in_table: true, sortable: true
|
|
587
|
+
},
|
|
588
|
+
# Boolean: remote worker flag
|
|
589
|
+
{
|
|
590
|
+
target_model: "employee", field_name: "remote_worker", custom_type: "boolean",
|
|
591
|
+
label: "Remote Worker", section: "Work Arrangement", position: 0,
|
|
592
|
+
active: true, required: false, default_value: "false",
|
|
593
|
+
show_in_table: true
|
|
594
|
+
},
|
|
595
|
+
# Date: start date
|
|
596
|
+
{
|
|
597
|
+
target_model: "employee", field_name: "start_date", custom_type: "date",
|
|
598
|
+
label: "Start Date", section: "Employment", position: 0,
|
|
599
|
+
active: true, required: false,
|
|
600
|
+
show_in_table: true, sortable: true
|
|
601
|
+
},
|
|
602
|
+
# Datetime: last review
|
|
603
|
+
{
|
|
604
|
+
target_model: "employee", field_name: "last_review_at", custom_type: "datetime",
|
|
605
|
+
label: "Last Performance Review", section: "Employment", position: 1,
|
|
606
|
+
active: true, required: false,
|
|
607
|
+
show_in_table: false, show_in_show: true
|
|
608
|
+
},
|
|
609
|
+
# Enum: t-shirt size with custom values
|
|
610
|
+
{
|
|
611
|
+
target_model: "employee", field_name: "tshirt_size", custom_type: "enum",
|
|
612
|
+
label: "T-Shirt Size", section: "Personal Info", position: 2,
|
|
613
|
+
active: true, required: false, default_value: "M",
|
|
614
|
+
hint: "For company swag orders",
|
|
615
|
+
enum_values: [
|
|
616
|
+
{ "value" => "XS", "label" => "Extra Small" },
|
|
617
|
+
{ "value" => "S", "label" => "Small" },
|
|
618
|
+
{ "value" => "M", "label" => "Medium" },
|
|
619
|
+
{ "value" => "L", "label" => "Large" },
|
|
620
|
+
{ "value" => "XL", "label" => "Extra Large" },
|
|
621
|
+
{ "value" => "XXL", "label" => "XXL" }
|
|
622
|
+
],
|
|
623
|
+
show_in_table: true
|
|
624
|
+
},
|
|
625
|
+
# Inactive field: demonstrates that inactive fields are hidden
|
|
626
|
+
{
|
|
627
|
+
target_model: "employee", field_name: "legacy_id", custom_type: "string",
|
|
628
|
+
label: "Legacy System ID", section: "System", position: 99,
|
|
629
|
+
active: false, required: false,
|
|
630
|
+
show_in_table: false, show_in_form: false, show_in_show: false
|
|
631
|
+
}
|
|
632
|
+
]
|
|
633
|
+
|
|
634
|
+
employee_cfds.each { |attrs| CfdModel.create!(attrs) }
|
|
635
|
+
puts " Created #{employee_cfds.size} employee custom field definitions"
|
|
636
|
+
|
|
637
|
+
# -- Project custom fields: practical business fields --
|
|
638
|
+
project_cfds = [
|
|
639
|
+
# Enum: priority
|
|
640
|
+
{
|
|
641
|
+
target_model: "project", field_name: "priority", custom_type: "enum",
|
|
642
|
+
label: "Priority", section: "Project Details", position: 0,
|
|
643
|
+
active: true, required: true,
|
|
644
|
+
hint: "Determines task queue ordering",
|
|
645
|
+
enum_values: %w[low medium high critical],
|
|
646
|
+
default_value: "medium",
|
|
647
|
+
show_in_table: true, sortable: true
|
|
648
|
+
},
|
|
649
|
+
# Decimal: budget
|
|
650
|
+
{
|
|
651
|
+
target_model: "project", field_name: "budget", custom_type: "decimal",
|
|
652
|
+
label: "Budget (USD)", section: "Financials", position: 0,
|
|
653
|
+
active: true, required: false,
|
|
654
|
+
hint: "Approved budget in USD, excluding contingency",
|
|
655
|
+
min_value: 0, precision: 2,
|
|
656
|
+
show_in_table: true, sortable: true
|
|
657
|
+
},
|
|
658
|
+
# Date: deadline
|
|
659
|
+
{
|
|
660
|
+
target_model: "project", field_name: "deadline", custom_type: "date",
|
|
661
|
+
label: "Deadline", section: "Project Details", position: 1,
|
|
662
|
+
active: true, required: false,
|
|
663
|
+
show_in_table: true, sortable: true
|
|
664
|
+
},
|
|
665
|
+
# Integer: team size
|
|
666
|
+
{
|
|
667
|
+
target_model: "project", field_name: "team_size", custom_type: "integer",
|
|
668
|
+
label: "Team Size", section: "Project Details", position: 2,
|
|
669
|
+
active: true, required: false,
|
|
670
|
+
min_value: 1, max_value: 500,
|
|
671
|
+
show_in_table: true
|
|
672
|
+
},
|
|
673
|
+
# Text: notes
|
|
674
|
+
{
|
|
675
|
+
target_model: "project", field_name: "notes", custom_type: "text",
|
|
676
|
+
label: "Project Notes", section: "Documentation", position: 0,
|
|
677
|
+
active: true, required: false, placeholder: "Additional project notes...",
|
|
678
|
+
searchable: true
|
|
679
|
+
},
|
|
680
|
+
# Boolean: is_public
|
|
681
|
+
{
|
|
682
|
+
target_model: "project", field_name: "is_public", custom_type: "boolean",
|
|
683
|
+
label: "Public Project", section: "Visibility", position: 0,
|
|
684
|
+
active: true, required: false, default_value: "false",
|
|
685
|
+
show_in_table: true
|
|
686
|
+
},
|
|
687
|
+
# String: client name with search
|
|
688
|
+
{
|
|
689
|
+
target_model: "project", field_name: "client_name", custom_type: "string",
|
|
690
|
+
label: "Client Name", section: "Financials", position: 1,
|
|
691
|
+
active: true, required: false,
|
|
692
|
+
max_length: 100, searchable: true,
|
|
693
|
+
show_in_table: true
|
|
694
|
+
}
|
|
695
|
+
]
|
|
696
|
+
|
|
697
|
+
project_cfds.each { |attrs| CfdModel.create!(attrs) }
|
|
698
|
+
puts " Created #{project_cfds.size} project custom field definitions"
|
|
699
|
+
|
|
700
|
+
# Force-refresh custom field accessors after creating definitions
|
|
701
|
+
LcpRuby::CustomFields::Registry.reload!
|
|
702
|
+
employee_model_class = LcpRuby.registry.model_for("employee")
|
|
703
|
+
project_model_class = LcpRuby.registry.model_for("project")
|
|
704
|
+
employee_model_class.apply_custom_field_accessors!
|
|
705
|
+
project_model_class.apply_custom_field_accessors!
|
|
706
|
+
|
|
707
|
+
# Fill some employees with custom field data.
|
|
708
|
+
# Order matches the `employees` array created in Phase 5 above.
|
|
709
|
+
emp_custom_data = [
|
|
710
|
+
# employees[0]: Jane Smith (manager)
|
|
711
|
+
{ nickname: "JaneS", bio: "Engineering lead with a passion for clean architecture.", years_experience: 12,
|
|
712
|
+
performance_score: 9.2, hourly_rate: 85.00, remote_worker: false,
|
|
713
|
+
start_date: "2018-03-15", last_review_at: "2025-12-01 10:00:00", tshirt_size: "M" },
|
|
714
|
+
# employees[1]: John Doe (developer)
|
|
715
|
+
{ nickname: "JD", years_experience: 5, performance_score: 7.8, hourly_rate: 55.00,
|
|
716
|
+
remote_worker: true, start_date: "2021-06-01", tshirt_size: "L" },
|
|
717
|
+
# employees[2]: Alice Brown
|
|
718
|
+
{ nickname: "Ali", years_experience: 8, performance_score: 8.5, hourly_rate: 70.00,
|
|
719
|
+
remote_worker: false, start_date: "2019-09-20", tshirt_size: "S" },
|
|
720
|
+
# employees[3]: Bob Wilson
|
|
721
|
+
{ years_experience: 3, performance_score: 6.5, hourly_rate: 45.00,
|
|
722
|
+
remote_worker: true, start_date: "2023-01-10", tshirt_size: "XL" },
|
|
723
|
+
# employees[4]: Carol Davis (designer)
|
|
724
|
+
{ nickname: "Cee", years_experience: 6, performance_score: 8.0, hourly_rate: 60.00,
|
|
725
|
+
remote_worker: false, start_date: "2020-04-01", tshirt_size: "XS" },
|
|
726
|
+
# employees[5]: Dan Lee (admin)
|
|
727
|
+
{ nickname: "DanTheMan", bio: "Operations and people management.", years_experience: 15,
|
|
728
|
+
performance_score: 8.8, hourly_rate: 95.00, remote_worker: false,
|
|
729
|
+
start_date: "2015-01-01", last_review_at: "2025-11-15 14:30:00", tshirt_size: "L" }
|
|
730
|
+
]
|
|
731
|
+
|
|
732
|
+
employees.first(emp_custom_data.size).each_with_index do |emp, idx|
|
|
733
|
+
emp.assign_attributes(emp_custom_data[idx])
|
|
734
|
+
emp.save!
|
|
735
|
+
end
|
|
736
|
+
puts " Filled custom field data for #{emp_custom_data.size} employees"
|
|
737
|
+
|
|
738
|
+
# Fill projects with custom field data.
|
|
739
|
+
# ProjModel defined in Phase 5 above. Order matches creation order.
|
|
740
|
+
proj_records = ProjModel.all.to_a
|
|
741
|
+
proj_custom_data = [
|
|
742
|
+
# proj_records[0]: Website Redesign
|
|
743
|
+
{ priority: "high", budget: 50000.00, deadline: (Date.today + 90).to_s, team_size: 8,
|
|
744
|
+
notes: "Major redesign of the public-facing website.", is_public: true, client_name: "Internal" },
|
|
745
|
+
# proj_records[1]: API v3
|
|
746
|
+
{ priority: "critical", budget: 120000.00, deadline: (Date.today + 180).to_s, team_size: 12,
|
|
747
|
+
notes: "Full API rewrite with GraphQL support.", is_public: false },
|
|
748
|
+
# proj_records[2]: Cloud Migration
|
|
749
|
+
{ priority: "high", budget: 200000.00, deadline: (Date.today + 365).to_s, team_size: 6,
|
|
750
|
+
is_public: false, client_name: "Ops Team" },
|
|
751
|
+
# proj_records[3]: Brand Refresh
|
|
752
|
+
{ priority: "low", budget: 15000.00, team_size: 3, is_public: true, client_name: "Marketing" },
|
|
753
|
+
# proj_records[4]: Internal Tools
|
|
754
|
+
{ priority: "medium", budget: 30000.00, deadline: (Date.today + 60).to_s, team_size: 4,
|
|
755
|
+
notes: "Build internal dashboards and admin tools.", is_public: false }
|
|
756
|
+
]
|
|
757
|
+
|
|
758
|
+
proj_records.first(proj_custom_data.size).each_with_index do |proj, idx|
|
|
759
|
+
proj.assign_attributes(proj_custom_data[idx])
|
|
760
|
+
proj.save!
|
|
761
|
+
end
|
|
762
|
+
puts " Filled custom field data for #{proj_custom_data.size} projects"
|
|
763
|
+
|
|
764
|
+
# Phase 6: Attachments (just create empty records — files need manual upload)
|
|
765
|
+
AttachModel = LcpRuby.registry.model_for("showcase_attachment")
|
|
766
|
+
AttachModel.create!(title: "Sample Record (upload files via edit)")
|
|
767
|
+
puts " Created #{AttachModel.count} showcase_attachment records"
|
|
768
|
+
|
|
769
|
+
# Phase 7: Permissions
|
|
770
|
+
PermModel = LcpRuby.registry.model_for("showcase_permission")
|
|
771
|
+
|
|
772
|
+
[
|
|
773
|
+
{ title: "Open Task", status: "open", owner_id: 1, priority: "medium", confidential: false, public_notes: "Anyone can see this.", internal_notes: "Admin-only notes." },
|
|
774
|
+
{ title: "In Progress Item", status: "in_progress", owner_id: 1, assignee_id: 2, priority: "high", confidential: false, public_notes: "Being worked on." },
|
|
775
|
+
{ title: "Locked Record", status: "locked", owner_id: 1, priority: "critical", confidential: true, public_notes: "This record is locked.", internal_notes: "Only admin can edit." },
|
|
776
|
+
{ title: "Archived Entry", status: "archived", owner_id: 2, priority: "low", confidential: false, public_notes: "Historical record.", internal_notes: "Cannot be destroyed." },
|
|
777
|
+
{ title: "Confidential Report", status: "open", owner_id: 1, priority: "high", confidential: true, internal_notes: "Top secret information.", public_notes: "Classified." }
|
|
778
|
+
].each { |attrs| PermModel.create!(attrs) }
|
|
779
|
+
puts " Created #{PermModel.count} showcase_permission records"
|
|
780
|
+
|
|
781
|
+
# Phase 8: Roles
|
|
782
|
+
RoleModel = LcpRuby.registry.model_for("role")
|
|
783
|
+
|
|
784
|
+
[
|
|
785
|
+
{ name: "admin", label: "Administrator", description: "Full system access. Can manage all records, roles, and settings.", active: true, position: 0 },
|
|
786
|
+
{ name: "editor", label: "Editor", description: "Can create and edit records but cannot delete or manage roles.", active: true, position: 10 },
|
|
787
|
+
{ name: "viewer", label: "Viewer", description: "Read-only access to all public data.", active: true, position: 20 },
|
|
788
|
+
{ name: "owner", label: "Owner", description: "Full access scoped to own records only.", active: true, position: 30 },
|
|
789
|
+
# Used by the inherits_from cascade vignette (school registry).
|
|
790
|
+
{ name: "teacher", label: "Teacher", description: "School-registry teacher: sees own classes (teacher_id = current_user.id) and inherits visibility down to students and grades.", active: true, position: 40 },
|
|
791
|
+
{ name: "parent", label: "Parent", description: "School-registry guardian: sees own children (guardian_id = current_user.id) and inherits visibility to their grades.", active: true, position: 41 },
|
|
792
|
+
{ name: "deprecated_role", label: "Deprecated Role", description: "This role is no longer active. Kept for audit history.", active: false, position: 99 }
|
|
793
|
+
].each { |attrs| RoleModel.create!(attrs) }
|
|
794
|
+
puts " Created #{RoleModel.count} role records"
|
|
795
|
+
|
|
796
|
+
# Phase 8b: Permission Configs (DB-backed permissions)
|
|
797
|
+
PermConfigModel = LcpRuby.registry.model_for("permission_config")
|
|
798
|
+
|
|
799
|
+
[
|
|
800
|
+
{
|
|
801
|
+
target_model: "article",
|
|
802
|
+
definition: {
|
|
803
|
+
roles: {
|
|
804
|
+
admin: {
|
|
805
|
+
crud: %w[index show create update destroy],
|
|
806
|
+
fields: { readable: "all", writable: "all" },
|
|
807
|
+
actions: "all",
|
|
808
|
+
scope: "all",
|
|
809
|
+
presenters: "all"
|
|
810
|
+
},
|
|
811
|
+
editor: {
|
|
812
|
+
crud: %w[index show create update],
|
|
813
|
+
fields: { readable: "all", writable: %w[title body category_id author_id published featured] },
|
|
814
|
+
actions: { allowed: [] },
|
|
815
|
+
scope: "all",
|
|
816
|
+
presenters: "all"
|
|
817
|
+
},
|
|
818
|
+
viewer: {
|
|
819
|
+
crud: %w[index show],
|
|
820
|
+
fields: { readable: %w[title body category_id author_id published], writable: [] },
|
|
821
|
+
actions: { allowed: [] },
|
|
822
|
+
scope: "all",
|
|
823
|
+
presenters: "all"
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
default_role: "viewer"
|
|
827
|
+
}.to_json,
|
|
828
|
+
active: true,
|
|
829
|
+
notes: "DB-backed permissions for articles. Overrides the YAML _default. Editors can publish but not delete."
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
target_model: "department",
|
|
833
|
+
definition: {
|
|
834
|
+
roles: {
|
|
835
|
+
admin: {
|
|
836
|
+
crud: %w[index show create update destroy],
|
|
837
|
+
fields: { readable: "all", writable: "all" },
|
|
838
|
+
actions: "all",
|
|
839
|
+
scope: "all",
|
|
840
|
+
presenters: "all"
|
|
841
|
+
},
|
|
842
|
+
editor: {
|
|
843
|
+
crud: %w[index show update],
|
|
844
|
+
fields: { readable: "all", writable: %w[name description] },
|
|
845
|
+
actions: { allowed: [] },
|
|
846
|
+
scope: "all"
|
|
847
|
+
},
|
|
848
|
+
viewer: {
|
|
849
|
+
crud: %w[index show],
|
|
850
|
+
fields: { readable: "all", writable: [] },
|
|
851
|
+
actions: { allowed: [] },
|
|
852
|
+
scope: "all"
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
default_role: "viewer"
|
|
856
|
+
}.to_json,
|
|
857
|
+
active: true,
|
|
858
|
+
notes: "DB-backed permissions for departments. Editors can update but not create or delete."
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
target_model: "employee",
|
|
862
|
+
definition: {
|
|
863
|
+
roles: {
|
|
864
|
+
admin: {
|
|
865
|
+
crud: %w[index show create update destroy],
|
|
866
|
+
fields: { readable: "all", writable: "all" },
|
|
867
|
+
actions: "all",
|
|
868
|
+
scope: "all",
|
|
869
|
+
presenters: "all"
|
|
870
|
+
},
|
|
871
|
+
editor: {
|
|
872
|
+
crud: %w[index show create update],
|
|
873
|
+
fields: { readable: "all", writable: %w[name email role status department_id mentor_id nickname bio years_experience] },
|
|
874
|
+
actions: { allowed: [] },
|
|
875
|
+
scope: "all",
|
|
876
|
+
presenters: "all"
|
|
877
|
+
},
|
|
878
|
+
viewer: {
|
|
879
|
+
crud: %w[index show],
|
|
880
|
+
fields: { readable: %w[name email role status department_id nickname bio years_experience tshirt_size], writable: [] },
|
|
881
|
+
actions: { allowed: [] },
|
|
882
|
+
scope: "all",
|
|
883
|
+
presenters: "all"
|
|
884
|
+
}
|
|
885
|
+
},
|
|
886
|
+
default_role: "viewer",
|
|
887
|
+
field_overrides: {
|
|
888
|
+
hourly_rate: { readable_by: %w[admin], writable_by: %w[admin] },
|
|
889
|
+
performance_score: { readable_by: %w[admin editor], writable_by: %w[admin] }
|
|
890
|
+
}
|
|
891
|
+
}.to_json,
|
|
892
|
+
active: true,
|
|
893
|
+
notes: "Per-field custom field permissions demo. Viewers see basic custom fields but not salary/performance. field_overrides restrict hourly_rate to admin only."
|
|
894
|
+
},
|
|
895
|
+
{
|
|
896
|
+
target_model: "user",
|
|
897
|
+
definition: {
|
|
898
|
+
roles: {
|
|
899
|
+
admin: {
|
|
900
|
+
crud: %w[index show update],
|
|
901
|
+
fields: { readable: "all", writable: "all" },
|
|
902
|
+
scope: "all",
|
|
903
|
+
presenters: "all"
|
|
904
|
+
},
|
|
905
|
+
editor: {
|
|
906
|
+
crud: %w[show update],
|
|
907
|
+
fields: {
|
|
908
|
+
readable: %w[name email theme locale timezone density border_radius],
|
|
909
|
+
writable: %w[name email theme locale timezone density border_radius]
|
|
910
|
+
},
|
|
911
|
+
scope: { type: "field_match", field: "id", value: "current_user.id" },
|
|
912
|
+
presenters: %w[my_settings]
|
|
913
|
+
},
|
|
914
|
+
viewer: {
|
|
915
|
+
crud: %w[show update],
|
|
916
|
+
fields: {
|
|
917
|
+
readable: %w[name email theme locale timezone density border_radius],
|
|
918
|
+
writable: %w[name email theme locale timezone density border_radius]
|
|
919
|
+
},
|
|
920
|
+
scope: { type: "field_match", field: "id", value: "current_user.id" },
|
|
921
|
+
presenters: %w[my_settings]
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
default_role: "viewer"
|
|
925
|
+
}.to_json,
|
|
926
|
+
active: true,
|
|
927
|
+
notes: "Self-service user profile — non-admin roles can only see/edit their own record via the my_settings presenter. Users presenter remains admin-only."
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
target_model: "inactive_demo",
|
|
931
|
+
definition: {
|
|
932
|
+
roles: {
|
|
933
|
+
admin: {
|
|
934
|
+
crud: %w[index show create update destroy],
|
|
935
|
+
fields: { readable: "all", writable: "all" },
|
|
936
|
+
actions: "all",
|
|
937
|
+
scope: "all"
|
|
938
|
+
}
|
|
939
|
+
},
|
|
940
|
+
default_role: "admin"
|
|
941
|
+
}.to_json,
|
|
942
|
+
active: false,
|
|
943
|
+
notes: "Inactive permission config — ignored by the resolver. Demonstrates that inactive records don't affect authorization."
|
|
944
|
+
}
|
|
945
|
+
].each { |attrs| PermConfigModel.create!(attrs) }
|
|
946
|
+
puts " Created #{PermConfigModel.count} permission config records"
|
|
947
|
+
|
|
948
|
+
# Phase 8c: Groups
|
|
949
|
+
GroupModel = LcpRuby.registry.model_for("group")
|
|
950
|
+
MembershipModel = LcpRuby.registry.model_for("group_membership")
|
|
951
|
+
RoleMappingModel = LcpRuby.registry.model_for("group_role_mapping")
|
|
952
|
+
|
|
953
|
+
engineering = GroupModel.create!(
|
|
954
|
+
name: "engineering", label: "Engineering Team",
|
|
955
|
+
description: "Software engineers and technical staff responsible for product development.",
|
|
956
|
+
source: "manual", active: true
|
|
957
|
+
)
|
|
958
|
+
design = GroupModel.create!(
|
|
959
|
+
name: "design", label: "Design Team",
|
|
960
|
+
description: "UX/UI designers and product design professionals.",
|
|
961
|
+
source: "manual", active: true
|
|
962
|
+
)
|
|
963
|
+
management = GroupModel.create!(
|
|
964
|
+
name: "management", label: "Management",
|
|
965
|
+
description: "Department heads and project managers with administrative access.",
|
|
966
|
+
source: "manual", active: true
|
|
967
|
+
)
|
|
968
|
+
contractors = GroupModel.create!(
|
|
969
|
+
name: "contractors", label: "External Contractors",
|
|
970
|
+
description: "External contractors with limited read-only access.",
|
|
971
|
+
external_id: "CN=Contractors,OU=External,DC=corp,DC=com",
|
|
972
|
+
source: "api", active: true
|
|
973
|
+
)
|
|
974
|
+
legacy_team = GroupModel.create!(
|
|
975
|
+
name: "legacy_team", label: "Legacy Team (Inactive)",
|
|
976
|
+
description: "Former team that has been dissolved. Kept for audit purposes.",
|
|
977
|
+
external_id: "CN=LegacyTeam,OU=Archived,DC=corp,DC=com",
|
|
978
|
+
source: "ldap", active: false
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
# Memberships — link employee user IDs to groups
|
|
982
|
+
[
|
|
983
|
+
{ group: engineering, user_id: 1, source: "manual" },
|
|
984
|
+
{ group: engineering, user_id: 2, source: "manual" },
|
|
985
|
+
{ group: engineering, user_id: 3, source: "ldap" },
|
|
986
|
+
{ group: design, user_id: 4, source: "manual" },
|
|
987
|
+
{ group: design, user_id: 5, source: "manual" },
|
|
988
|
+
{ group: design, user_id: 2, source: "manual" },
|
|
989
|
+
{ group: management, user_id: 6, source: "manual" },
|
|
990
|
+
{ group: management, user_id: 7, source: "manual" },
|
|
991
|
+
{ group: contractors, user_id: 8, source: "api" },
|
|
992
|
+
{ group: contractors, user_id: 9, source: "api" },
|
|
993
|
+
{ group: contractors, user_id: 10, source: "api" },
|
|
994
|
+
{ group: legacy_team, user_id: 11, source: "ldap" },
|
|
995
|
+
{ group: legacy_team, user_id: 12, source: "ldap" },
|
|
996
|
+
{ group: engineering, user_id: 6, source: "manual" }
|
|
997
|
+
].each do |attrs|
|
|
998
|
+
MembershipModel.create!(attrs)
|
|
999
|
+
rescue ActiveRecord::RecordInvalid
|
|
1000
|
+
nil
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
# Role mappings — map groups to authorization roles
|
|
1004
|
+
[
|
|
1005
|
+
{ group: engineering, role_name: "editor" },
|
|
1006
|
+
{ group: engineering, role_name: "viewer" },
|
|
1007
|
+
{ group: design, role_name: "editor" },
|
|
1008
|
+
{ group: management, role_name: "admin" },
|
|
1009
|
+
{ group: contractors, role_name: "viewer" }
|
|
1010
|
+
].each do |attrs|
|
|
1011
|
+
RoleMappingModel.create!(attrs)
|
|
1012
|
+
rescue ActiveRecord::RecordInvalid
|
|
1013
|
+
nil
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
puts " Created #{GroupModel.count} groups, #{MembershipModel.count} memberships, #{RoleMappingModel.count} role mappings"
|
|
1017
|
+
|
|
1018
|
+
# Phase 9: Extensibility (was Phase 8)
|
|
1019
|
+
ExtModel = LcpRuby.registry.model_for("showcase_extensibility")
|
|
1020
|
+
|
|
1021
|
+
[
|
|
1022
|
+
{ name: "US Dollar Account", currency: "USD", amount: 10000.00 },
|
|
1023
|
+
{ name: "Euro Account", currency: "EUR", amount: 8500.50 },
|
|
1024
|
+
{ name: "British Pound Reserve", currency: "GBP", amount: 25000.00 },
|
|
1025
|
+
{ name: "Japanese Yen Fund", currency: "JPY", amount: 1500000.00 },
|
|
1026
|
+
{ name: "No Currency Set", currency: nil, amount: 500.00 }
|
|
1027
|
+
].each { |attrs| ExtModel.create!(attrs) }
|
|
1028
|
+
puts " Created #{ExtModel.count} showcase_extensibility records"
|
|
1029
|
+
|
|
1030
|
+
# Phase 10: Virtual Fields
|
|
1031
|
+
VirtualModel = LcpRuby.registry.model_for("showcase_virtual_field")
|
|
1032
|
+
|
|
1033
|
+
[
|
|
1034
|
+
{
|
|
1035
|
+
name: "Premium Widget",
|
|
1036
|
+
properties: { color: "blue", priority: 4, unit_price: 29.99, featured: true,
|
|
1037
|
+
category: "electronics", warehouse: "WEST-07", release_date: "2025-06-15",
|
|
1038
|
+
sku_code: "ELEC-PW-001", city: "San Francisco", country: "USA" }
|
|
1039
|
+
},
|
|
1040
|
+
{
|
|
1041
|
+
name: "Basic Gadget",
|
|
1042
|
+
properties: { color: "gray", priority: 2, unit_price: 9.99, featured: false,
|
|
1043
|
+
category: "electronics", warehouse: "MAIN-01", release_date: "2025-01-10",
|
|
1044
|
+
sku_code: "ELEC-BG-042", city: "Berlin", country: "Germany" }
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
name: "Limited Edition Box",
|
|
1048
|
+
properties: { color: "gold", priority: 5, unit_price: 149.00, featured: true,
|
|
1049
|
+
category: "furniture", warehouse: "EAST-03", release_date: "2025-12-01",
|
|
1050
|
+
sku_code: "FURN-LE-100", city: "Tokyo", country: "Japan" }
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
name: "Clearance Item",
|
|
1054
|
+
properties: { color: "red", priority: 1, unit_price: 2.50, featured: false,
|
|
1055
|
+
category: "clothing", sku_code: "CLTH-CI-999" }
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
name: "New Arrival",
|
|
1059
|
+
properties: { color: "green", priority: 3, unit_price: 45.00, featured: true,
|
|
1060
|
+
category: "food", warehouse: "MAIN-01", release_date: "2026-02-01",
|
|
1061
|
+
sku_code: "FOOD-NA-007" }
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
name: "Empty Properties Test",
|
|
1065
|
+
properties: {}
|
|
1066
|
+
}
|
|
1067
|
+
].each { |attrs| VirtualModel.create!(attrs) }
|
|
1068
|
+
puts " Created #{VirtualModel.count} showcase_virtual_field records"
|
|
1069
|
+
|
|
1070
|
+
# Phase 11: Positioning
|
|
1071
|
+
PriorityModel = LcpRuby.registry.model_for("showcase_positioning")
|
|
1072
|
+
|
|
1073
|
+
[
|
|
1074
|
+
{ name: "Design database schema", description: "Create ERD and define table structures", status: "done", priority: "high" },
|
|
1075
|
+
{ name: "Implement authentication", description: "Add JWT-based auth flow", status: "done", priority: "critical" },
|
|
1076
|
+
{ name: "Build REST API endpoints", description: "Create CRUD endpoints for all resources", status: "in_progress", priority: "high" },
|
|
1077
|
+
{ name: "Write integration tests", description: "End-to-end API testing", status: "in_progress", priority: "medium" },
|
|
1078
|
+
{ name: "Set up CI/CD pipeline", description: "GitHub Actions for automated deploy", status: "todo", priority: "medium" },
|
|
1079
|
+
{ name: "Add monitoring & alerts", description: "Datadog integration for error tracking", status: "todo", priority: "low" },
|
|
1080
|
+
{ name: "Performance optimization", description: "Query optimization and caching", status: "todo", priority: "medium" },
|
|
1081
|
+
{ name: "Write user documentation", description: "API docs and user guide", status: "todo", priority: "low" }
|
|
1082
|
+
].each { |attrs| PriorityModel.create!(attrs) }
|
|
1083
|
+
puts " Created #{PriorityModel.count} showcase_positioning records"
|
|
1084
|
+
|
|
1085
|
+
PipelineModel = LcpRuby.registry.model_for("pipeline")
|
|
1086
|
+
StageModel = LcpRuby.registry.model_for("pipeline_stage")
|
|
1087
|
+
|
|
1088
|
+
sales = PipelineModel.create!(name: "Sales Pipeline", description: "Standard B2B sales process")
|
|
1089
|
+
hiring = PipelineModel.create!(name: "Hiring Pipeline", description: "Recruitment workflow")
|
|
1090
|
+
support = PipelineModel.create!(name: "Support Pipeline", description: "Customer support ticket flow")
|
|
1091
|
+
|
|
1092
|
+
[
|
|
1093
|
+
{ name: "Lead", color: "#3498db" },
|
|
1094
|
+
{ name: "Qualified", color: "#2ecc71" },
|
|
1095
|
+
{ name: "Proposal", color: "#f39c12" },
|
|
1096
|
+
{ name: "Negotiation", color: "#e67e22" },
|
|
1097
|
+
{ name: "Closed Won", color: "#27ae60" },
|
|
1098
|
+
{ name: "Closed Lost", color: "#e74c3c" }
|
|
1099
|
+
].each { |attrs| StageModel.create!(attrs.merge(pipeline: sales)) }
|
|
1100
|
+
|
|
1101
|
+
[
|
|
1102
|
+
{ name: "Application", color: "#9b59b6" },
|
|
1103
|
+
{ name: "Phone Screen", color: "#3498db" },
|
|
1104
|
+
{ name: "Technical Interview", color: "#2ecc71" },
|
|
1105
|
+
{ name: "Final Interview", color: "#f39c12" },
|
|
1106
|
+
{ name: "Offer", color: "#27ae60" }
|
|
1107
|
+
].each { |attrs| StageModel.create!(attrs.merge(pipeline: hiring)) }
|
|
1108
|
+
|
|
1109
|
+
[
|
|
1110
|
+
{ name: "New", color: "#e74c3c" },
|
|
1111
|
+
{ name: "In Progress", color: "#f39c12" },
|
|
1112
|
+
{ name: "Waiting on Customer", color: "#95a5a6" },
|
|
1113
|
+
{ name: "Resolved", color: "#27ae60" }
|
|
1114
|
+
].each { |attrs| StageModel.create!(attrs.merge(pipeline: support)) }
|
|
1115
|
+
|
|
1116
|
+
puts " Created #{PipelineModel.count} pipelines with #{StageModel.count} stages"
|
|
1117
|
+
|
|
1118
|
+
# Phase 12: Recipes (JSON Field Nested Editing)
|
|
1119
|
+
RecipeModel = LcpRuby.registry.model_for("showcase_recipe")
|
|
1120
|
+
|
|
1121
|
+
[
|
|
1122
|
+
{
|
|
1123
|
+
title: "Spaghetti Carbonara",
|
|
1124
|
+
cuisine: "italian",
|
|
1125
|
+
servings: 4,
|
|
1126
|
+
steps: [
|
|
1127
|
+
{ instruction: "Bring a large pot of salted water to boil", duration_minutes: 10 },
|
|
1128
|
+
{ instruction: "Cook spaghetti according to package directions", duration_minutes: 12 },
|
|
1129
|
+
{ instruction: "Fry guanciale in a large skillet until crispy", duration_minutes: 8 },
|
|
1130
|
+
{ instruction: "Whisk eggs, pecorino, and black pepper in a bowl", duration_minutes: 2 },
|
|
1131
|
+
{ instruction: "Toss hot pasta with guanciale, then stir in egg mixture off heat", duration_minutes: 3 }
|
|
1132
|
+
],
|
|
1133
|
+
ingredients: [
|
|
1134
|
+
{ name: "Spaghetti", quantity: "400", unit: "g", notes: nil, optional: false },
|
|
1135
|
+
{ name: "Guanciale", quantity: "200", unit: "g", notes: "Pancetta works as a substitute", optional: false },
|
|
1136
|
+
{ name: "Egg yolks", quantity: "6", unit: "pcs", notes: nil, optional: false },
|
|
1137
|
+
{ name: "Pecorino Romano", quantity: "100", unit: "g", notes: "Freshly grated", optional: false },
|
|
1138
|
+
{ name: "Black pepper", quantity: "2", unit: "tsp", notes: "Freshly cracked", optional: false },
|
|
1139
|
+
{ name: "Parmesan", quantity: "50", unit: "g", notes: "For extra richness", optional: true }
|
|
1140
|
+
]
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
title: "Chicken Tikka Masala",
|
|
1144
|
+
cuisine: "indian",
|
|
1145
|
+
servings: 6,
|
|
1146
|
+
steps: [
|
|
1147
|
+
{ instruction: "Marinate chicken in yogurt and spices for at least 1 hour", duration_minutes: 60 },
|
|
1148
|
+
{ instruction: "Grill or broil chicken until charred", duration_minutes: 15 },
|
|
1149
|
+
{ instruction: "Saute onions, garlic, and ginger until golden", duration_minutes: 10 },
|
|
1150
|
+
{ instruction: "Add tomato puree and spices, simmer for 15 minutes", duration_minutes: 15 },
|
|
1151
|
+
{ instruction: "Add cream and grilled chicken, simmer until heated through", duration_minutes: 10 },
|
|
1152
|
+
{ instruction: "Garnish with cilantro and serve with naan", duration_minutes: 2 }
|
|
1153
|
+
],
|
|
1154
|
+
ingredients: [
|
|
1155
|
+
{ name: "Chicken thighs", quantity: "800", unit: "g", notes: "Boneless, skinless", optional: false },
|
|
1156
|
+
{ name: "Yogurt", quantity: "200", unit: "ml", notes: "Plain, full-fat", optional: false },
|
|
1157
|
+
{ name: "Garam masala", quantity: "2", unit: "tbsp", notes: nil, optional: false },
|
|
1158
|
+
{ name: "Tomato puree", quantity: "400", unit: "ml", notes: "Canned crushed tomatoes", optional: false },
|
|
1159
|
+
{ name: "Heavy cream", quantity: "200", unit: "ml", notes: nil, optional: false },
|
|
1160
|
+
{ name: "Onion", quantity: "2", unit: "pcs", notes: "Finely diced", optional: false },
|
|
1161
|
+
{ name: "Garlic", quantity: "4", unit: "pcs", notes: "Cloves, minced", optional: false },
|
|
1162
|
+
{ name: "Fresh cilantro", quantity: "1", unit: "tbsp", notes: "Chopped, for garnish", optional: true }
|
|
1163
|
+
]
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
title: "Miso Ramen",
|
|
1167
|
+
cuisine: "japanese",
|
|
1168
|
+
servings: 2,
|
|
1169
|
+
steps: [
|
|
1170
|
+
{ instruction: "Prepare dashi broth from kombu and bonito flakes", duration_minutes: 20 },
|
|
1171
|
+
{ instruction: "Dissolve miso paste into the warm broth", duration_minutes: 3 },
|
|
1172
|
+
{ instruction: "Cook ramen noodles according to package", duration_minutes: 4 },
|
|
1173
|
+
{ instruction: "Prepare toppings: soft-boil eggs, slice chashu, chop scallions", duration_minutes: 15 },
|
|
1174
|
+
{ instruction: "Assemble bowls: noodles, broth, then arrange toppings", duration_minutes: 3 }
|
|
1175
|
+
],
|
|
1176
|
+
ingredients: [
|
|
1177
|
+
{ name: "Ramen noodles", quantity: "200", unit: "g", notes: "Fresh preferred", optional: false },
|
|
1178
|
+
{ name: "White miso paste", quantity: "3", unit: "tbsp", notes: nil, optional: false },
|
|
1179
|
+
{ name: "Dashi stock", quantity: "800", unit: "ml", notes: nil, optional: false },
|
|
1180
|
+
{ name: "Chashu pork", quantity: "150", unit: "g", notes: "Sliced", optional: false },
|
|
1181
|
+
{ name: "Soft-boiled eggs", quantity: "2", unit: "pcs", notes: "Marinated in soy sauce overnight", optional: true },
|
|
1182
|
+
{ name: "Scallions", quantity: "2", unit: "pcs", notes: "Thinly sliced", optional: false },
|
|
1183
|
+
{ name: "Nori sheets", quantity: "2", unit: "pcs", notes: nil, optional: true }
|
|
1184
|
+
]
|
|
1185
|
+
}
|
|
1186
|
+
].each { |attrs| RecipeModel.create!(attrs) }
|
|
1187
|
+
|
|
1188
|
+
puts " Created #{RecipeModel.count} showcase_recipe records"
|
|
1189
|
+
|
|
1190
|
+
# Phase 14: Advanced Search Showcase
|
|
1191
|
+
SearchModel = LcpRuby.registry.model_for("showcase_search")
|
|
1192
|
+
|
|
1193
|
+
# Grab existing departments, categories, and authors for associations
|
|
1194
|
+
all_depts = DeptModel.all.to_a
|
|
1195
|
+
all_cats = CategoryModel.all.to_a
|
|
1196
|
+
all_authors = AuthorModel.all.to_a
|
|
1197
|
+
|
|
1198
|
+
[
|
|
1199
|
+
{
|
|
1200
|
+
title: "Widget Pro X100",
|
|
1201
|
+
description: "High-end widget with advanced features and premium build quality.",
|
|
1202
|
+
quantity: 250, rating: 4.7, price: 299.99,
|
|
1203
|
+
published: true,
|
|
1204
|
+
status: "published", priority: "high",
|
|
1205
|
+
release_date: Date.today - 30,
|
|
1206
|
+
last_reviewed_at: Time.current - 2.days,
|
|
1207
|
+
tracking_id: SecureRandom.uuid,
|
|
1208
|
+
contact_email: "sales@widgets.example.com",
|
|
1209
|
+
contact_phone: "+1 555 100 2000",
|
|
1210
|
+
source_url: "https://widgets.example.com/pro-x100",
|
|
1211
|
+
department_id: all_depts.find { |d| d.name == "Engineering" }&.id,
|
|
1212
|
+
category_id: all_cats.find { |c| c.name == "Technology" }&.id,
|
|
1213
|
+
author_id: all_authors.first&.id
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
title: "Budget Gadget Basic",
|
|
1217
|
+
description: "Affordable everyday gadget for casual users.",
|
|
1218
|
+
quantity: 1200, rating: 3.2, price: 19.99,
|
|
1219
|
+
published: true,
|
|
1220
|
+
status: "published", priority: "low",
|
|
1221
|
+
release_date: Date.today - 90,
|
|
1222
|
+
last_reviewed_at: Time.current - 15.days,
|
|
1223
|
+
tracking_id: SecureRandom.uuid,
|
|
1224
|
+
contact_email: "info@gadgets.example.com",
|
|
1225
|
+
source_url: "https://gadgets.example.com/basic",
|
|
1226
|
+
department_id: all_depts.find { |d| d.name == "Frontend" }&.id,
|
|
1227
|
+
category_id: all_cats.find { |c| c.name == "Web Development" }&.id,
|
|
1228
|
+
author_id: all_authors.second&.id
|
|
1229
|
+
},
|
|
1230
|
+
{
|
|
1231
|
+
title: "Enterprise Server Rack",
|
|
1232
|
+
description: "42U server rack with integrated cooling and cable management.",
|
|
1233
|
+
quantity: 15, rating: 4.9, price: 4500.00,
|
|
1234
|
+
published: true,
|
|
1235
|
+
status: "approved", priority: "critical",
|
|
1236
|
+
release_date: Date.today - 7,
|
|
1237
|
+
last_reviewed_at: Time.current - 1.hour,
|
|
1238
|
+
tracking_id: SecureRandom.uuid,
|
|
1239
|
+
contact_email: "enterprise@racks.example.com",
|
|
1240
|
+
contact_phone: "+44 20 7946 0958",
|
|
1241
|
+
department_id: all_depts.find { |d| d.name == "Backend" }&.id,
|
|
1242
|
+
category_id: all_cats.find { |c| c.name == "Enterprise" }&.id,
|
|
1243
|
+
author_id: all_authors.third&.id
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
title: "Cloud Monitor Dashboard",
|
|
1247
|
+
description: "Real-time monitoring dashboard for cloud infrastructure.",
|
|
1248
|
+
quantity: 0, rating: 4.1, price: 0.00,
|
|
1249
|
+
published: false,
|
|
1250
|
+
status: "draft", priority: "medium",
|
|
1251
|
+
release_date: Date.today + 30,
|
|
1252
|
+
tracking_id: SecureRandom.uuid,
|
|
1253
|
+
contact_email: "dev@cloud.example.com",
|
|
1254
|
+
department_id: all_depts.find { |d| d.name == "DevOps" }&.id,
|
|
1255
|
+
category_id: all_cats.find { |c| c.name == "Backend" }&.id,
|
|
1256
|
+
author_id: all_authors.fourth&.id
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
title: "Mobile SDK v3",
|
|
1260
|
+
description: "Cross-platform mobile SDK with native performance.",
|
|
1261
|
+
quantity: nil, rating: nil, price: 149.00,
|
|
1262
|
+
published: false,
|
|
1263
|
+
status: "review", priority: "high",
|
|
1264
|
+
release_date: Date.today + 14,
|
|
1265
|
+
last_reviewed_at: Time.current - 3.days,
|
|
1266
|
+
tracking_id: SecureRandom.uuid,
|
|
1267
|
+
contact_phone: "+49 30 1234 5678",
|
|
1268
|
+
source_url: "https://sdk.example.com/v3",
|
|
1269
|
+
department_id: all_depts.find { |d| d.name == "React Team" }&.id,
|
|
1270
|
+
category_id: all_cats.find { |c| c.name == "Frontend" }&.id,
|
|
1271
|
+
author_id: all_authors.first&.id
|
|
1272
|
+
},
|
|
1273
|
+
{
|
|
1274
|
+
title: "Security Audit Tool",
|
|
1275
|
+
description: "Automated security scanning and vulnerability reporting.",
|
|
1276
|
+
quantity: 50, rating: 4.5, price: 899.00,
|
|
1277
|
+
published: true,
|
|
1278
|
+
status: "published", priority: "critical",
|
|
1279
|
+
release_date: Date.today - 60,
|
|
1280
|
+
last_reviewed_at: Time.current - 5.days,
|
|
1281
|
+
tracking_id: SecureRandom.uuid,
|
|
1282
|
+
contact_email: "security@tools.example.com",
|
|
1283
|
+
department_id: all_depts.find { |d| d.name == "API Team" }&.id,
|
|
1284
|
+
category_id: all_cats.find { |c| c.name == "Backend" }&.id,
|
|
1285
|
+
author_id: all_authors.last&.id
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
title: "Design System Components",
|
|
1289
|
+
description: "Reusable UI component library with accessibility built in.",
|
|
1290
|
+
quantity: 87, rating: 4.3, price: 0.00,
|
|
1291
|
+
published: true,
|
|
1292
|
+
status: "published", priority: "medium",
|
|
1293
|
+
release_date: Date.today - 120,
|
|
1294
|
+
last_reviewed_at: Time.current - 30.days,
|
|
1295
|
+
department_id: all_depts.find { |d| d.name == "UX Design" }&.id,
|
|
1296
|
+
category_id: all_cats.find { |c| c.name == "CSS & Styling" }&.id,
|
|
1297
|
+
author_id: all_authors.second&.id
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
title: "Legacy Data Migrator",
|
|
1301
|
+
description: nil,
|
|
1302
|
+
quantity: 3, rating: 2.1, price: 50.00,
|
|
1303
|
+
published: false,
|
|
1304
|
+
status: "archived", priority: "low",
|
|
1305
|
+
release_date: Date.today - 365,
|
|
1306
|
+
tracking_id: SecureRandom.uuid,
|
|
1307
|
+
department_id: all_depts.find { |d| d.name == "Management" }&.id,
|
|
1308
|
+
category_id: all_cats.find { |c| c.name == "Business" }&.id,
|
|
1309
|
+
author_id: all_authors.third&.id
|
|
1310
|
+
},
|
|
1311
|
+
{
|
|
1312
|
+
title: "AI Code Assistant",
|
|
1313
|
+
description: "Machine learning powered code completion and review.",
|
|
1314
|
+
quantity: 0, rating: nil, price: nil,
|
|
1315
|
+
published: false,
|
|
1316
|
+
status: "draft", priority: "high",
|
|
1317
|
+
release_date: nil,
|
|
1318
|
+
tracking_id: nil,
|
|
1319
|
+
contact_email: "ai@assistant.example.com",
|
|
1320
|
+
department_id: all_depts.find { |d| d.name == "Engineering" }&.id,
|
|
1321
|
+
category_id: all_cats.find { |c| c.name == "React Ecosystem" }&.id,
|
|
1322
|
+
author_id: all_authors.fourth&.id
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
title: "Analytics Pipeline",
|
|
1326
|
+
description: "Real-time event processing and reporting pipeline with SQL interface.",
|
|
1327
|
+
quantity: 500, rating: 3.8, price: 1200.00,
|
|
1328
|
+
published: true,
|
|
1329
|
+
status: "approved", priority: "medium",
|
|
1330
|
+
release_date: Date.today - 14,
|
|
1331
|
+
last_reviewed_at: Time.current,
|
|
1332
|
+
tracking_id: SecureRandom.uuid,
|
|
1333
|
+
contact_email: "data@pipeline.example.com",
|
|
1334
|
+
contact_phone: "+1 555 999 8888",
|
|
1335
|
+
source_url: "https://pipeline.example.com",
|
|
1336
|
+
department_id: all_depts.find { |d| d.name == "Backend" }&.id,
|
|
1337
|
+
category_id: all_cats.find { |c| c.name == "Science" }&.id,
|
|
1338
|
+
author_id: all_authors.last&.id
|
|
1339
|
+
},
|
|
1340
|
+
{
|
|
1341
|
+
title: "Notification Service",
|
|
1342
|
+
description: "Multi-channel notification delivery: email, SMS, push, and webhooks.",
|
|
1343
|
+
quantity: 10000, rating: 4.6, price: 75.00,
|
|
1344
|
+
published: true,
|
|
1345
|
+
status: "published", priority: "high",
|
|
1346
|
+
release_date: Date.today - 45,
|
|
1347
|
+
last_reviewed_at: Time.current - 10.days,
|
|
1348
|
+
tracking_id: SecureRandom.uuid,
|
|
1349
|
+
contact_email: "notify@service.example.com",
|
|
1350
|
+
contact_phone: "+1 555 777 6666",
|
|
1351
|
+
department_id: all_depts.find { |d| d.name == "Engineering" }&.id,
|
|
1352
|
+
category_id: all_cats.find { |c| c.name == "Technology" }&.id,
|
|
1353
|
+
author_id: all_authors.first&.id
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
title: "Form Builder Pro",
|
|
1357
|
+
description: "Drag-and-drop form builder with conditional logic and validation rules.",
|
|
1358
|
+
quantity: 42, rating: 3.9, price: 199.00,
|
|
1359
|
+
published: false,
|
|
1360
|
+
status: "review", priority: "medium",
|
|
1361
|
+
release_date: Date.today + 7,
|
|
1362
|
+
last_reviewed_at: Time.current - 1.day,
|
|
1363
|
+
tracking_id: SecureRandom.uuid,
|
|
1364
|
+
source_url: "https://formbuilder.example.com/pro",
|
|
1365
|
+
department_id: all_depts.find { |d| d.name == "Frontend" }&.id,
|
|
1366
|
+
category_id: all_cats.find { |c| c.name == "Startups" }&.id,
|
|
1367
|
+
author_id: all_authors.second&.id
|
|
1368
|
+
}
|
|
1369
|
+
].each do |attrs|
|
|
1370
|
+
SearchModel.create!(attrs)
|
|
1371
|
+
end
|
|
1372
|
+
puts " Created #{SearchModel.count} showcase_search records"
|
|
1373
|
+
|
|
1374
|
+
# Phase 15: Saved Filters
|
|
1375
|
+
if LcpRuby.registry.registered?("saved_filter")
|
|
1376
|
+
SavedFilterModel = LcpRuby.registry.model_for("saved_filter")
|
|
1377
|
+
|
|
1378
|
+
# --- Saved filters for showcase-search (inline display) ---
|
|
1379
|
+
[
|
|
1380
|
+
{
|
|
1381
|
+
name: "Published & Expensive",
|
|
1382
|
+
description: "Items that are published with price >= 100",
|
|
1383
|
+
target_presenter: "showcase_searches",
|
|
1384
|
+
condition_tree: {
|
|
1385
|
+
logic: "and",
|
|
1386
|
+
conditions: [
|
|
1387
|
+
{ field: "published", operator: "true" },
|
|
1388
|
+
{ field: "price", operator: "gteq", value: "100" }
|
|
1389
|
+
]
|
|
1390
|
+
},
|
|
1391
|
+
ql_text: "published is true and price >= 100",
|
|
1392
|
+
visibility: "personal",
|
|
1393
|
+
pinned: true,
|
|
1394
|
+
default_filter: true,
|
|
1395
|
+
owner_id: 1,
|
|
1396
|
+
position: 1,
|
|
1397
|
+
icon: "dollar-sign",
|
|
1398
|
+
color: "green"
|
|
1399
|
+
},
|
|
1400
|
+
{
|
|
1401
|
+
name: "Critical Priority",
|
|
1402
|
+
description: "Items with high or critical priority",
|
|
1403
|
+
target_presenter: "showcase_searches",
|
|
1404
|
+
condition_tree: {
|
|
1405
|
+
logic: "and",
|
|
1406
|
+
conditions: [
|
|
1407
|
+
{ field: "priority", operator: "in", value: %w[high critical] }
|
|
1408
|
+
]
|
|
1409
|
+
},
|
|
1410
|
+
ql_text: "priority in ['high', 'critical']",
|
|
1411
|
+
visibility: "personal",
|
|
1412
|
+
pinned: true,
|
|
1413
|
+
owner_id: 1,
|
|
1414
|
+
position: 2,
|
|
1415
|
+
icon: "alert-triangle",
|
|
1416
|
+
color: "red"
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
name: "Recent Drafts",
|
|
1420
|
+
description: "Draft items created in the last 30 days",
|
|
1421
|
+
target_presenter: "showcase_searches",
|
|
1422
|
+
condition_tree: {
|
|
1423
|
+
logic: "and",
|
|
1424
|
+
conditions: [
|
|
1425
|
+
{ field: "status", operator: "eq", value: "draft" },
|
|
1426
|
+
{ field: "created_at", operator: "last_n_days", value: "30" }
|
|
1427
|
+
]
|
|
1428
|
+
},
|
|
1429
|
+
ql_text: "status = 'draft' and created_at last_n_days 30",
|
|
1430
|
+
visibility: "personal",
|
|
1431
|
+
owner_id: 1,
|
|
1432
|
+
position: 3,
|
|
1433
|
+
icon: "edit",
|
|
1434
|
+
color: "gray"
|
|
1435
|
+
},
|
|
1436
|
+
{
|
|
1437
|
+
name: "All Published",
|
|
1438
|
+
description: "All published items (visible to everyone)",
|
|
1439
|
+
target_presenter: "showcase_searches",
|
|
1440
|
+
condition_tree: {
|
|
1441
|
+
logic: "and",
|
|
1442
|
+
conditions: [
|
|
1443
|
+
{ field: "status", operator: "eq", value: "published" }
|
|
1444
|
+
]
|
|
1445
|
+
},
|
|
1446
|
+
ql_text: "status = 'published'",
|
|
1447
|
+
visibility: "global",
|
|
1448
|
+
pinned: true,
|
|
1449
|
+
owner_id: 1,
|
|
1450
|
+
position: 4,
|
|
1451
|
+
icon: "globe",
|
|
1452
|
+
color: "green"
|
|
1453
|
+
},
|
|
1454
|
+
{
|
|
1455
|
+
name: "Admin: In Review",
|
|
1456
|
+
description: "Items currently in review (admin role filter)",
|
|
1457
|
+
target_presenter: "showcase_searches",
|
|
1458
|
+
condition_tree: {
|
|
1459
|
+
logic: "and",
|
|
1460
|
+
conditions: [
|
|
1461
|
+
{ field: "status", operator: "eq", value: "review" }
|
|
1462
|
+
]
|
|
1463
|
+
},
|
|
1464
|
+
ql_text: "status = 'review'",
|
|
1465
|
+
visibility: "role",
|
|
1466
|
+
target_role: "admin",
|
|
1467
|
+
owner_id: 1,
|
|
1468
|
+
position: 5,
|
|
1469
|
+
icon: "shield",
|
|
1470
|
+
color: "orange"
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
name: "Engineering: High-Value Items",
|
|
1474
|
+
description: "Expensive items for engineering team review",
|
|
1475
|
+
target_presenter: "showcase_searches",
|
|
1476
|
+
condition_tree: {
|
|
1477
|
+
logic: "and",
|
|
1478
|
+
conditions: [
|
|
1479
|
+
{ field: "price", operator: "gteq", value: "200" },
|
|
1480
|
+
{ field: "status", operator: "not_eq", value: "archived" }
|
|
1481
|
+
]
|
|
1482
|
+
},
|
|
1483
|
+
ql_text: "price >= 200 and status != 'archived'",
|
|
1484
|
+
visibility: "group",
|
|
1485
|
+
target_group: "engineering",
|
|
1486
|
+
pinned: true,
|
|
1487
|
+
owner_id: 1,
|
|
1488
|
+
position: 6,
|
|
1489
|
+
icon: "cpu",
|
|
1490
|
+
color: "purple"
|
|
1491
|
+
},
|
|
1492
|
+
# --- Saved filters for articles (dropdown display) ---
|
|
1493
|
+
{
|
|
1494
|
+
name: "Published Articles",
|
|
1495
|
+
description: "All articles with published status",
|
|
1496
|
+
target_presenter: "articles",
|
|
1497
|
+
condition_tree: {
|
|
1498
|
+
logic: "and",
|
|
1499
|
+
conditions: [
|
|
1500
|
+
{ field: "status", operator: "eq", value: "published" }
|
|
1501
|
+
]
|
|
1502
|
+
},
|
|
1503
|
+
ql_text: "status = 'published'",
|
|
1504
|
+
visibility: "global",
|
|
1505
|
+
pinned: true,
|
|
1506
|
+
owner_id: 1,
|
|
1507
|
+
position: 1,
|
|
1508
|
+
icon: "check-circle",
|
|
1509
|
+
color: "green"
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
name: "My Draft Articles",
|
|
1513
|
+
description: "Draft articles (personal filter)",
|
|
1514
|
+
target_presenter: "articles",
|
|
1515
|
+
condition_tree: {
|
|
1516
|
+
logic: "and",
|
|
1517
|
+
conditions: [
|
|
1518
|
+
{ field: "status", operator: "eq", value: "draft" }
|
|
1519
|
+
]
|
|
1520
|
+
},
|
|
1521
|
+
ql_text: "status = 'draft'",
|
|
1522
|
+
visibility: "personal",
|
|
1523
|
+
owner_id: 1,
|
|
1524
|
+
position: 2,
|
|
1525
|
+
icon: "file"
|
|
1526
|
+
},
|
|
1527
|
+
{
|
|
1528
|
+
name: "Long Articles",
|
|
1529
|
+
description: "Articles with more than 500 words",
|
|
1530
|
+
target_presenter: "articles",
|
|
1531
|
+
condition_tree: {
|
|
1532
|
+
logic: "and",
|
|
1533
|
+
conditions: [
|
|
1534
|
+
{ field: "word_count", operator: "gteq", value: "500" }
|
|
1535
|
+
]
|
|
1536
|
+
},
|
|
1537
|
+
ql_text: "word_count >= 500",
|
|
1538
|
+
visibility: "personal",
|
|
1539
|
+
pinned: true,
|
|
1540
|
+
owner_id: 1,
|
|
1541
|
+
position: 3,
|
|
1542
|
+
icon: "book"
|
|
1543
|
+
}
|
|
1544
|
+
].each { |attrs| SavedFilterModel.create!(attrs) }
|
|
1545
|
+
|
|
1546
|
+
puts " Created #{SavedFilterModel.count} saved filters"
|
|
1547
|
+
end
|
|
1548
|
+
|
|
1549
|
+
# Phase: Array Fields Showcase
|
|
1550
|
+
ArrayModel = LcpRuby.registry.model_for("showcase_array")
|
|
1551
|
+
|
|
1552
|
+
[
|
|
1553
|
+
{
|
|
1554
|
+
title: "Web Framework Comparison",
|
|
1555
|
+
description: "Comparing popular web frameworks across multiple dimensions.",
|
|
1556
|
+
tags: %w[ruby rails javascript react comparison],
|
|
1557
|
+
categories: %w[frontend backend],
|
|
1558
|
+
scores: [ 5, 4, 3, 5, 4 ],
|
|
1559
|
+
measurements: [ 98.5, 72.3, 88.1 ],
|
|
1560
|
+
default_labels: %w[important review],
|
|
1561
|
+
record_type: "advanced",
|
|
1562
|
+
featured: true
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
title: "DevOps Pipeline Setup",
|
|
1566
|
+
description: "Setting up CI/CD pipeline with automated testing and deployment.",
|
|
1567
|
+
tags: %w[devops ci cd docker kubernetes urgent],
|
|
1568
|
+
categories: %w[devops backend],
|
|
1569
|
+
scores: [ 5, 5, 4 ],
|
|
1570
|
+
measurements: [ 150.0, 200.5 ],
|
|
1571
|
+
default_labels: %w[important],
|
|
1572
|
+
record_type: "special",
|
|
1573
|
+
featured: true
|
|
1574
|
+
},
|
|
1575
|
+
{
|
|
1576
|
+
title: "Quick Start Guide",
|
|
1577
|
+
description: "A simple getting started tutorial for beginners.",
|
|
1578
|
+
tags: %w[tutorial beginner],
|
|
1579
|
+
categories: %w[frontend],
|
|
1580
|
+
scores: [ 3, 4 ],
|
|
1581
|
+
measurements: [],
|
|
1582
|
+
default_labels: %w[important review],
|
|
1583
|
+
record_type: "basic",
|
|
1584
|
+
featured: false
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
title: "Performance Optimization",
|
|
1588
|
+
description: "Database query optimization and caching strategies.",
|
|
1589
|
+
tags: %w[performance database caching optimization],
|
|
1590
|
+
categories: %w[backend devops],
|
|
1591
|
+
scores: [ 5, 4, 5, 3, 2 ],
|
|
1592
|
+
measurements: [ 12.5, 8.3, 45.7, 3.2 ],
|
|
1593
|
+
default_labels: [],
|
|
1594
|
+
record_type: "advanced",
|
|
1595
|
+
featured: false
|
|
1596
|
+
},
|
|
1597
|
+
{
|
|
1598
|
+
title: "Empty Arrays Demo",
|
|
1599
|
+
description: "Record with no array values to demonstrate empty state rendering.",
|
|
1600
|
+
tags: [],
|
|
1601
|
+
categories: [],
|
|
1602
|
+
scores: [],
|
|
1603
|
+
measurements: [],
|
|
1604
|
+
default_labels: %w[important review],
|
|
1605
|
+
record_type: "basic",
|
|
1606
|
+
featured: false
|
|
1607
|
+
},
|
|
1608
|
+
{
|
|
1609
|
+
title: "Design System Components",
|
|
1610
|
+
description: "Building a component library with design tokens and variants.",
|
|
1611
|
+
tags: %w[design components ui ux],
|
|
1612
|
+
categories: %w[design frontend],
|
|
1613
|
+
scores: [ 4, 4, 5 ],
|
|
1614
|
+
measurements: [ 16.0, 24.0, 32.0, 48.0 ],
|
|
1615
|
+
default_labels: %w[review],
|
|
1616
|
+
record_type: "special",
|
|
1617
|
+
featured: true
|
|
1618
|
+
}
|
|
1619
|
+
].each { |attrs| ArrayModel.create!(attrs) }
|
|
1620
|
+
|
|
1621
|
+
puts " Created #{ArrayModel.count} array demo records"
|
|
1622
|
+
|
|
1623
|
+
# Phase 13: Feature Catalog
|
|
1624
|
+
# Resolve the catalog from the gem's bundled docs (LcpRuby ships
|
|
1625
|
+
# feature-catalog.yml). Works whether showcase runs in this checkout or is
|
|
1626
|
+
# materialized elsewhere via `lcp run showcase` — `GemPaths.docs` follows the
|
|
1627
|
+
# loaded gem, not a repo-relative path.
|
|
1628
|
+
FeatureModel = LcpRuby.registry.model_for("feature")
|
|
1629
|
+
feature_catalog_path = File.join(LcpRuby::GemPaths.docs, "feature-catalog.yml")
|
|
1630
|
+
feature_catalog = YAML.load_file(feature_catalog_path)
|
|
1631
|
+
features = Array(feature_catalog["features"])
|
|
1632
|
+
|
|
1633
|
+
# Drop catalog keys not mapped on the Feature model so new meta-keys
|
|
1634
|
+
# (e.g. `generator:` on generator-bundled features) don't crash the seed.
|
|
1635
|
+
known_attrs = FeatureModel.column_names
|
|
1636
|
+
features.each do |attrs|
|
|
1637
|
+
FeatureModel.create!(attrs.slice(*known_attrs))
|
|
1638
|
+
end
|
|
1639
|
+
puts " Created #{FeatureModel.count} feature catalog entries from docs/feature-catalog.yml"
|
|
1640
|
+
|
|
1641
|
+
# Phase: Userstamps Showcase
|
|
1642
|
+
UserstampsModel = LcpRuby.registry.model_for("showcase_userstamps")
|
|
1643
|
+
|
|
1644
|
+
# Simulate different users creating/updating documents
|
|
1645
|
+
admin_user = if LcpRuby.configuration.authentication == :built_in
|
|
1646
|
+
LcpRuby::User.find_by(email: "admin@example.com")
|
|
1647
|
+
end
|
|
1648
|
+
|
|
1649
|
+
LcpRuby::Current.user = admin_user
|
|
1650
|
+
|
|
1651
|
+
[
|
|
1652
|
+
{ title: "Architecture Decision Record: Microservices", content: "After evaluating monolith vs microservices, we decided to adopt a modular monolith.", status: "published", priority: "high" },
|
|
1653
|
+
{ title: "API Versioning Strategy", content: "Use URL path versioning (v1, v2) for public APIs and header versioning for internal.", status: "published", priority: "normal" },
|
|
1654
|
+
{ title: "Database Migration Guidelines", content: "All migrations must be reversible. No data migrations in schema migrations.", status: "review", priority: "high" },
|
|
1655
|
+
{ title: "Frontend Component Library", content: "Evaluate Radix UI, shadcn/ui, and Headless UI for the design system.", status: "draft", priority: "normal" },
|
|
1656
|
+
{ title: "Deployment Runbook: Production", content: "Step-by-step guide for production deployments including rollback procedures.", status: "published", priority: "high" },
|
|
1657
|
+
{ title: "Code Review Checklist", content: "Security, performance, testing, documentation, and naming conventions.", status: "review", priority: "normal" },
|
|
1658
|
+
{ title: "Incident Response Plan", content: "Escalation paths, communication templates, and post-mortem process.", status: "draft", priority: "low" },
|
|
1659
|
+
{ title: "Technical Debt Register", content: "Tracked items: legacy auth module, N+1 in reports, missing indexes.", status: "archived", priority: "low" }
|
|
1660
|
+
].each { |attrs| UserstampsModel.create!(attrs) }
|
|
1661
|
+
|
|
1662
|
+
LcpRuby::Current.user = nil
|
|
1663
|
+
|
|
1664
|
+
puts " Created #{UserstampsModel.count} tracked documents (userstamps showcase)"
|
|
1665
|
+
|
|
1666
|
+
# Phase: Sequences Showcase
|
|
1667
|
+
SequenceModel = LcpRuby.registry.model_for("showcase_sequence")
|
|
1668
|
+
|
|
1669
|
+
# Create records across categories to demonstrate all sequence types
|
|
1670
|
+
[
|
|
1671
|
+
{ title: "Server outage report", category: "support", description: "Production server went down at 3am" },
|
|
1672
|
+
{ title: "New feature request", category: "engineering", description: "Add dark mode support" },
|
|
1673
|
+
{ title: "Q1 invoice", category: "billing", description: "Quarterly billing for Enterprise plan" },
|
|
1674
|
+
{ title: "API rate limit issue", category: "support", description: "Customer hitting rate limits" },
|
|
1675
|
+
{ title: "Database migration plan", category: "engineering", description: "Migrate from MySQL to PostgreSQL" },
|
|
1676
|
+
{ title: "Annual subscription renewal", category: "billing", description: "Renewal for 2026" },
|
|
1677
|
+
{ title: "General inquiry", category: "general", description: "Prospective customer questions" },
|
|
1678
|
+
{ title: "CI/CD pipeline redesign", category: "engineering", description: "Move to GitHub Actions" },
|
|
1679
|
+
{ title: "Password reset not working", category: "support", description: "Users reporting 500 errors" },
|
|
1680
|
+
{ title: "Credit note CN-2025-001", category: "billing", description: "Refund for overpayment" },
|
|
1681
|
+
{ title: "Security audit findings", category: "general", description: "Penetration test results review" },
|
|
1682
|
+
{ title: "Mobile app crash on login", category: "support", description: "iOS 18 compatibility issue" }
|
|
1683
|
+
].each { |attrs| SequenceModel.create!(attrs) }
|
|
1684
|
+
|
|
1685
|
+
puts " Created #{SequenceModel.count} sequenced records (sequences showcase)"
|
|
1686
|
+
|
|
1687
|
+
# Phase: Soft Delete Showcase
|
|
1688
|
+
# Phase: Aggregates
|
|
1689
|
+
AggCompanyModel = LcpRuby.registry.model_for("showcase_aggregate_company")
|
|
1690
|
+
AggProjectModel = LcpRuby.registry.model_for("showcase_aggregate")
|
|
1691
|
+
AggTaskModel = LcpRuby.registry.model_for("showcase_aggregate_item")
|
|
1692
|
+
|
|
1693
|
+
agg_companies = [
|
|
1694
|
+
{ name: "Acme Corp", country: "USA" },
|
|
1695
|
+
{ name: "Globex Inc", country: "Germany" },
|
|
1696
|
+
{ name: "Initech", country: "Japan" }
|
|
1697
|
+
].map { |attrs| AggCompanyModel.create!(attrs) }
|
|
1698
|
+
|
|
1699
|
+
agg_projects = [
|
|
1700
|
+
{ name: "Platform Redesign", description: "Complete UI/UX overhaul with new design system", status: "active", budget: 150_000, company_id: agg_companies[0].id },
|
|
1701
|
+
{ name: "Mobile App v2", description: "Native mobile app rewrite in Swift/Kotlin", status: "active", budget: 200_000, company_id: agg_companies[0].id },
|
|
1702
|
+
{ name: "Data Pipeline", description: "Real-time data ingestion and processing pipeline", status: "planning", budget: 80_000, company_id: agg_companies[1].id },
|
|
1703
|
+
{ name: "API Gateway", description: "Centralized API gateway with rate limiting and auth", status: "completed", budget: 60_000, company_id: agg_companies[1].id },
|
|
1704
|
+
{ name: "Legacy Migration", description: "Migrate legacy PHP codebase to Rails", status: "archived", budget: 120_000, company_id: agg_companies[2].id }
|
|
1705
|
+
].map { |attrs| AggProjectModel.create!(attrs) }
|
|
1706
|
+
|
|
1707
|
+
# Tasks for Platform Redesign (project 0) — 8 tasks, 3 done, 3 assignees
|
|
1708
|
+
[
|
|
1709
|
+
{ title: "Design system tokens", status: "done", hours: 40, cost: 4000, priority_score: 9, assignee: "Alice", due_date: "2025-01-15" },
|
|
1710
|
+
{ title: "Component library", status: "done", hours: 80, cost: 8000, priority_score: 9, assignee: "Alice", due_date: "2025-02-01" },
|
|
1711
|
+
{ title: "Navigation redesign", status: "done", hours: 24, cost: 2400, priority_score: 7, assignee: "Bob", due_date: "2025-02-15" },
|
|
1712
|
+
{ title: "Dashboard layout", status: "in_progress", hours: 32, cost: 3200, priority_score: 8, assignee: "Bob", due_date: "2025-03-01" },
|
|
1713
|
+
{ title: "Form components", status: "in_progress", hours: 48, cost: 4800, priority_score: 6, assignee: "Carol", due_date: "2025-03-15" },
|
|
1714
|
+
{ title: "Accessibility audit", status: "todo", hours: 16, cost: 1600, priority_score: 5, assignee: "Alice", due_date: "2025-04-01" },
|
|
1715
|
+
{ title: "Performance testing", status: "todo", hours: 20, cost: 2000, priority_score: 4, assignee: "Bob", due_date: "2025-04-15" },
|
|
1716
|
+
{ title: "Documentation", status: "todo", hours: 12, cost: 1200, priority_score: 3, assignee: "Carol", due_date: "2025-05-01" }
|
|
1717
|
+
].each { |attrs| AggTaskModel.create!(attrs.merge(showcase_aggregate_id: agg_projects[0].id)) }
|
|
1718
|
+
|
|
1719
|
+
# Tasks for Mobile App v2 (project 1) — 6 tasks, 1 done, 2 assignees
|
|
1720
|
+
[
|
|
1721
|
+
{ title: "App architecture", status: "done", hours: 24, cost: 3600, priority_score: 10, assignee: "Dan", due_date: "2025-01-20" },
|
|
1722
|
+
{ title: "Auth module", status: "in_progress", hours: 40, cost: 6000, priority_score: 8, assignee: "Eve", due_date: "2025-02-10" },
|
|
1723
|
+
{ title: "Offline sync", status: "in_progress", hours: 60, cost: 9000, priority_score: 7, assignee: "Dan", due_date: "2025-03-01" },
|
|
1724
|
+
{ title: "Push notifications", status: "todo", hours: 20, cost: 3000, priority_score: 5, assignee: "Eve", due_date: "2025-03-20" },
|
|
1725
|
+
{ title: "App store submission", status: "todo", hours: 8, cost: 1200, priority_score: 3, assignee: "Dan", due_date: "2025-04-15" },
|
|
1726
|
+
{ title: "Beta testing", status: "todo", hours: 16, cost: 2400, priority_score: 4, assignee: "Eve", due_date: "2025-04-01" }
|
|
1727
|
+
].each { |attrs| AggTaskModel.create!(attrs.merge(showcase_aggregate_id: agg_projects[1].id)) }
|
|
1728
|
+
|
|
1729
|
+
# Tasks for Data Pipeline (project 2) — 4 tasks, 0 done, 2 assignees
|
|
1730
|
+
[
|
|
1731
|
+
{ title: "Schema design", status: "todo", hours: 16, cost: 2400, priority_score: 8, assignee: "Frank", due_date: "2025-03-01" },
|
|
1732
|
+
{ title: "Kafka setup", status: "todo", hours: 24, cost: 3600, priority_score: 7, assignee: "Grace", due_date: "2025-03-15" },
|
|
1733
|
+
{ title: "Stream processors", status: "todo", hours: 40, cost: 6000, priority_score: 6, assignee: "Frank", due_date: "2025-04-01" },
|
|
1734
|
+
{ title: "Monitoring dashboard", status: "todo", hours: 12, cost: 1800, priority_score: 4, assignee: "Grace", due_date: "2025-04-15" }
|
|
1735
|
+
].each { |attrs| AggTaskModel.create!(attrs.merge(showcase_aggregate_id: agg_projects[2].id)) }
|
|
1736
|
+
|
|
1737
|
+
# Tasks for API Gateway (project 3) — 5 tasks, all done, 1 assignee
|
|
1738
|
+
[
|
|
1739
|
+
{ title: "Gateway framework", status: "done", hours: 32, cost: 3200, priority_score: 9, assignee: "Hank", due_date: "2024-10-01" },
|
|
1740
|
+
{ title: "Rate limiter", status: "done", hours: 16, cost: 1600, priority_score: 8, assignee: "Hank", due_date: "2024-10-15" },
|
|
1741
|
+
{ title: "Auth middleware", status: "done", hours: 24, cost: 2400, priority_score: 8, assignee: "Hank", due_date: "2024-11-01" },
|
|
1742
|
+
{ title: "Load testing", status: "done", hours: 12, cost: 1200, priority_score: 5, assignee: "Hank", due_date: "2024-11-15" },
|
|
1743
|
+
{ title: "Production deploy", status: "done", hours: 8, cost: 800, priority_score: 7, assignee: "Hank", due_date: "2024-12-01" }
|
|
1744
|
+
].each { |attrs| AggTaskModel.create!(attrs.merge(showcase_aggregate_id: agg_projects[3].id)) }
|
|
1745
|
+
|
|
1746
|
+
# Tasks for Legacy Migration (project 4) — 3 tasks, 2 done, 2 assignees
|
|
1747
|
+
[
|
|
1748
|
+
{ title: "Code audit", status: "done", hours: 40, cost: 4000, priority_score: 7, assignee: "Ivy", due_date: "2024-06-01" },
|
|
1749
|
+
{ title: "Data migration scripts", status: "done", hours: 60, cost: 6000, priority_score: 8, assignee: "Jack", due_date: "2024-07-15" },
|
|
1750
|
+
{ title: "Final cutover", status: "cancelled", hours: 16, cost: 1600, priority_score: 9, assignee: "Ivy", due_date: "2024-08-01" }
|
|
1751
|
+
].each { |attrs| AggTaskModel.create!(attrs.merge(showcase_aggregate_id: agg_projects[4].id)) }
|
|
1752
|
+
|
|
1753
|
+
puts " Created #{AggCompanyModel.count} companies, #{AggProjectModel.count} aggregate projects with #{AggTaskModel.count} tasks"
|
|
1754
|
+
|
|
1755
|
+
SoftDeleteModel = LcpRuby.registry.model_for("showcase_soft_delete")
|
|
1756
|
+
SoftDeleteItemModel = LcpRuby.registry.model_for("showcase_soft_delete_item")
|
|
1757
|
+
|
|
1758
|
+
LcpRuby::Current.user = admin_user
|
|
1759
|
+
|
|
1760
|
+
docs = [
|
|
1761
|
+
{ title: "Q1 Planning Document", content: "Strategic objectives and OKRs for Q1. Focus areas: platform stability, developer experience, onboarding.", status: "active", priority: "high" },
|
|
1762
|
+
{ title: "Release Notes v2.4", content: "New features: soft delete, userstamps, cascade discard. Bug fixes: permission cache, scope builder.", status: "active", priority: "normal" },
|
|
1763
|
+
{ title: "Security Audit Findings", content: "Penetration test results and remediation plan. All critical findings addressed.", status: "active", priority: "high" },
|
|
1764
|
+
{ title: "Legacy API Migration Plan", content: "Timeline for deprecating v1 endpoints and migrating consumers to v2.", status: "draft", priority: "normal" },
|
|
1765
|
+
{ title: "Team Retrospective Notes", content: "What went well: deployment automation. What to improve: test coverage for edge cases.", status: "draft", priority: "low" },
|
|
1766
|
+
{ title: "Outdated Design Spec", content: "Initial wireframes for the dashboard. Superseded by the revised spec.", status: "archived", priority: "low" }
|
|
1767
|
+
].map { |attrs| SoftDeleteModel.create!(attrs) }
|
|
1768
|
+
|
|
1769
|
+
# Add child items to some documents
|
|
1770
|
+
items_data = {
|
|
1771
|
+
0 => [
|
|
1772
|
+
{ name: "Define OKRs", notes: "Align with company goals" },
|
|
1773
|
+
{ name: "Assign team leads", notes: "One lead per initiative" },
|
|
1774
|
+
{ name: "Set milestone dates", notes: "Monthly checkpoints" }
|
|
1775
|
+
],
|
|
1776
|
+
1 => [
|
|
1777
|
+
{ name: "Write changelog", notes: "User-facing summary" },
|
|
1778
|
+
{ name: "Update docs", notes: "Reference guides and examples" }
|
|
1779
|
+
],
|
|
1780
|
+
2 => [
|
|
1781
|
+
{ name: "Fix XSS in search", notes: "Input sanitization added" },
|
|
1782
|
+
{ name: "Patch SQL injection", notes: "Parameterized queries" },
|
|
1783
|
+
{ name: "Enable CSP headers", notes: "Report-only mode first" },
|
|
1784
|
+
{ name: "Rotate API keys", notes: "All environments" }
|
|
1785
|
+
],
|
|
1786
|
+
3 => [
|
|
1787
|
+
{ name: "Inventory v1 consumers", notes: "Check analytics for active users" },
|
|
1788
|
+
{ name: "Build compatibility layer", notes: "Translate v1 requests to v2" }
|
|
1789
|
+
]
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
items_data.each do |doc_index, items|
|
|
1793
|
+
items.each do |item_attrs|
|
|
1794
|
+
SoftDeleteItemModel.create!(item_attrs.merge(showcase_soft_delete_id: docs[doc_index].id))
|
|
1795
|
+
end
|
|
1796
|
+
end
|
|
1797
|
+
|
|
1798
|
+
# Discard 2 documents to pre-populate the archive
|
|
1799
|
+
docs[4].discard! # Team Retrospective Notes
|
|
1800
|
+
docs[5].discard! # Outdated Design Spec
|
|
1801
|
+
|
|
1802
|
+
LcpRuby::Current.user = nil
|
|
1803
|
+
|
|
1804
|
+
puts " Created #{SoftDeleteModel.kept.count} active + #{SoftDeleteModel.discarded.count} archived soft delete documents with #{SoftDeleteItemModel.count} items"
|
|
1805
|
+
|
|
1806
|
+
# Phase: Batch Actions Showcase
|
|
1807
|
+
BatchTaskModel = LcpRuby.registry.model_for("showcase_batch_task")
|
|
1808
|
+
|
|
1809
|
+
LcpRuby::Current.user = admin_user
|
|
1810
|
+
|
|
1811
|
+
batch_tasks = [
|
|
1812
|
+
# Open tasks
|
|
1813
|
+
{ title: "Review Q1 budget proposal", status: "open", priority: "high", assignee: "Alice", due_date: "2026-04-15", estimated_hours: 4.0 },
|
|
1814
|
+
{ title: "Update onboarding documentation", status: "open", priority: "normal", assignee: "Bob", due_date: "2026-04-20", estimated_hours: 8.0 },
|
|
1815
|
+
{ title: "Fix broken CI pipeline", status: "open", priority: "critical", assignee: "Carol", due_date: "2026-03-25", estimated_hours: 2.0 },
|
|
1816
|
+
{ title: "Prepare sprint demo", status: "open", priority: "normal", assignee: "Alice", due_date: "2026-04-01", estimated_hours: 3.0 },
|
|
1817
|
+
{ title: "Audit third-party dependencies", status: "open", priority: "high", assignee: "Dan", due_date: "2026-04-10", estimated_hours: 6.0 },
|
|
1818
|
+
{ title: "Design new landing page", status: "open", priority: "normal", assignee: "Eve", due_date: "2026-05-01", estimated_hours: 16.0 },
|
|
1819
|
+
{ title: "Set up staging environment", status: "open", priority: "high", assignee: "Frank", due_date: "2026-03-28", estimated_hours: 5.0 },
|
|
1820
|
+
{ title: "Write API rate limiting spec", status: "open", priority: "normal", assignee: "Grace", due_date: "2026-04-25", estimated_hours: 4.0 },
|
|
1821
|
+
# In progress
|
|
1822
|
+
{ title: "Implement user dashboard", status: "in_progress", priority: "high", assignee: "Alice", due_date: "2026-04-05", estimated_hours: 24.0 },
|
|
1823
|
+
{ title: "Migrate legacy database", status: "in_progress", priority: "critical", assignee: "Bob", due_date: "2026-04-01", estimated_hours: 40.0 },
|
|
1824
|
+
{ title: "Refactor authentication module", status: "in_progress", priority: "normal", assignee: "Carol", due_date: "2026-04-15", estimated_hours: 16.0 },
|
|
1825
|
+
{ title: "Add search autocomplete", status: "in_progress", priority: "normal", assignee: "Dan", due_date: "2026-04-20", estimated_hours: 12.0 },
|
|
1826
|
+
# In review
|
|
1827
|
+
{ title: "Performance optimization report", status: "review", priority: "high", assignee: "Eve", due_date: "2026-03-30", estimated_hours: 8.0 },
|
|
1828
|
+
{ title: "Security patch deployment", status: "review", priority: "critical", assignee: "Frank", due_date: "2026-03-22", estimated_hours: 3.0 },
|
|
1829
|
+
{ title: "Update privacy policy page", status: "review", priority: "low", assignee: "Grace", due_date: "2026-04-30", estimated_hours: 2.0 },
|
|
1830
|
+
# Closed (cannot be batch-deleted by editor role — demonstrates per-record skip)
|
|
1831
|
+
{ title: "Initial project setup", status: "closed", priority: "normal", assignee: "Alice", due_date: "2026-01-15", estimated_hours: 4.0 },
|
|
1832
|
+
{ title: "Database schema design", status: "closed", priority: "high", assignee: "Bob", due_date: "2026-01-20", estimated_hours: 12.0 },
|
|
1833
|
+
{ title: "CI/CD pipeline setup", status: "closed", priority: "high", assignee: "Carol", due_date: "2026-02-01", estimated_hours: 8.0 },
|
|
1834
|
+
{ title: "Code review guidelines", status: "closed", priority: "normal", assignee: "Dan", due_date: "2026-02-10", estimated_hours: 3.0 },
|
|
1835
|
+
{ title: "Release v1.0", status: "closed", priority: "critical", assignee: "Eve", due_date: "2026-03-01", estimated_hours: 6.0 },
|
|
1836
|
+
# Extra records for cross-page selection (per_page: 15, so these push to page 2)
|
|
1837
|
+
{ title: "Plan team offsite", status: "open", priority: "low", assignee: "Frank", due_date: "2026-06-01", estimated_hours: 2.0 },
|
|
1838
|
+
{ title: "Benchmark new ORM version", status: "open", priority: "normal", assignee: "Grace", due_date: "2026-05-15", estimated_hours: 4.0 },
|
|
1839
|
+
{ title: "Write integration test suite", status: "open", priority: "high", assignee: "Alice", due_date: "2026-04-30", estimated_hours: 20.0 },
|
|
1840
|
+
{ title: "Configure CDN caching rules", status: "open", priority: "normal", assignee: "Bob", due_date: "2026-05-10", estimated_hours: 3.0 },
|
|
1841
|
+
{ title: "Evaluate monitoring tools", status: "open", priority: "normal", assignee: "Carol", due_date: "2026-05-20", estimated_hours: 6.0 }
|
|
1842
|
+
].map { |attrs| BatchTaskModel.create!(attrs) }
|
|
1843
|
+
|
|
1844
|
+
# Discard a few to pre-populate the archive view
|
|
1845
|
+
batch_tasks[20].discard! # Plan team offsite
|
|
1846
|
+
batch_tasks[21].discard! # Benchmark new ORM version
|
|
1847
|
+
|
|
1848
|
+
LcpRuby::Current.user = nil
|
|
1849
|
+
|
|
1850
|
+
puts " Created #{BatchTaskModel.kept.count} active + #{BatchTaskModel.discarded.count} archived batch tasks"
|
|
1851
|
+
|
|
1852
|
+
# Phase 14: Row Styling (item_classes) Demo
|
|
1853
|
+
ItemClassModel = LcpRuby.registry.model_for("showcase_item_class")
|
|
1854
|
+
|
|
1855
|
+
item_class_records = [
|
|
1856
|
+
# USE CASE 1: cancelled + eq → muted + strikethrough
|
|
1857
|
+
{ name: "Cancelled project", status: "cancelled", priority: "low", score: 30, amount: 500.00, code: "PROJ-001", email: "alice@example.com", notes: "Was deprioritized.", due_date: 2.months.ago.to_date },
|
|
1858
|
+
|
|
1859
|
+
# USE CASE 2: completed + eq → success (green)
|
|
1860
|
+
{ name: "Completed delivery", status: "completed", priority: "medium", score: 85, amount: 2500.00, code: "DEL-042", email: "bob@example.com", notes: "Shipped on time.", due_date: 1.week.ago.to_date },
|
|
1861
|
+
|
|
1862
|
+
# USE CASE 3: critical priority + eq → danger (red)
|
|
1863
|
+
{ name: "Server outage fix", status: "active", priority: "critical", score: 95, amount: 0.00, code: "INC-911", email: "ops@example.com", notes: "P1 incident.", due_date: Date.current },
|
|
1864
|
+
|
|
1865
|
+
# USE CASE 4: on_hold + eq → warning (yellow)
|
|
1866
|
+
{ name: "Pending approval", status: "on_hold", priority: "medium", score: 60, amount: 1200.00, code: "REQ-007", email: nil, notes: "Waiting for budget sign-off.", due_date: 1.month.from_now.to_date },
|
|
1867
|
+
|
|
1868
|
+
# USE CASE 5: high priority + eq → bold
|
|
1869
|
+
{ name: "Urgent feature request", status: "active", priority: "high", score: 75, amount: 8000.00, code: "FEAT-100", email: "pm@example.com", notes: "Board-level priority.", due_date: 2.weeks.from_now.to_date },
|
|
1870
|
+
|
|
1871
|
+
# USE CASE 6: score > 90 → info (blue) — also critical → danger (accumulation demo)
|
|
1872
|
+
{ name: "Top performer record", status: "active", priority: "medium", score: 98, amount: 15000.00, code: "PERF-001", email: "star@example.com", notes: "Exceeded all KPIs.", due_date: 3.months.from_now.to_date },
|
|
1873
|
+
|
|
1874
|
+
# USE CASE 7: score < 20 → custom class (lcp-item-low-score)
|
|
1875
|
+
{ name: "Underperforming task", status: "draft", priority: "low", score: 12, amount: 100.00, code: "TASK-999", email: nil, notes: nil, due_date: nil },
|
|
1876
|
+
|
|
1877
|
+
# USE CASE 8: blank notes → custom class (lcp-item-missing-notes)
|
|
1878
|
+
{ name: "No documentation yet", status: "active", priority: "medium", score: 50, amount: 300.00, code: "DOC-000", email: "writer@example.com", notes: nil, due_date: 1.month.from_now.to_date },
|
|
1879
|
+
|
|
1880
|
+
# USE CASE 9: code matches ^TEMP → custom class (lcp-item-temp-code)
|
|
1881
|
+
{ name: "Temporary prototype", status: "draft", priority: "low", score: 40, amount: 0.00, code: "TEMP-alpha", email: nil, notes: "Will be replaced.", due_date: nil },
|
|
1882
|
+
|
|
1883
|
+
# USE CASE 10: overdue (service condition) → custom class (lcp-item-overdue)
|
|
1884
|
+
{ name: "Overdue report", status: "active", priority: "high", score: 55, amount: 750.00, code: "RPT-003", email: "analyst@example.com", notes: "Deadline was last week.", due_date: 10.days.ago.to_date },
|
|
1885
|
+
|
|
1886
|
+
# ACCUMULATION: cancelled + low score + blank notes → 3 rules match
|
|
1887
|
+
{ name: "Abandoned experiment", status: "cancelled", priority: "low", score: 5, amount: 0.00, code: "EXP-404", email: nil, notes: nil, due_date: 6.months.ago.to_date },
|
|
1888
|
+
|
|
1889
|
+
# ACCUMULATION: completed + high score → success + info
|
|
1890
|
+
{ name: "Award-winning campaign", status: "completed", priority: "high", score: 99, amount: 50000.00, code: "MKT-001", email: "cmo@example.com", notes: "Won industry award.", due_date: 1.month.ago.to_date },
|
|
1891
|
+
|
|
1892
|
+
# NO MATCH: plain draft, medium priority, no special conditions
|
|
1893
|
+
{ name: "Regular draft item", status: "draft", priority: "medium", score: 50, amount: 200.00, code: "DRAFT-001", email: "user@example.com", notes: "Nothing special here.", due_date: 2.months.from_now.to_date },
|
|
1894
|
+
|
|
1895
|
+
# ACCUMULATION: on_hold + TEMP code + blank notes + overdue
|
|
1896
|
+
{ name: "Stalled temp project", status: "on_hold", priority: "medium", score: 45, amount: 0.00, code: "TEMP-stalled", email: nil, notes: nil, due_date: 3.weeks.ago.to_date }
|
|
1897
|
+
]
|
|
1898
|
+
|
|
1899
|
+
item_class_records.each { |attrs| ItemClassModel.create!(attrs) }
|
|
1900
|
+
puts " Created #{ItemClassModel.count} item_classes demo records"
|
|
1901
|
+
|
|
1902
|
+
# Phase 15: Advanced Conditions Demo
|
|
1903
|
+
ConditionCategoryModel = LcpRuby.registry.model_for("showcase_condition_category")
|
|
1904
|
+
ConditionModel = LcpRuby.registry.model_for("showcase_condition")
|
|
1905
|
+
ConditionTaskModel = LcpRuby.registry.model_for("showcase_condition_task")
|
|
1906
|
+
ConditionThresholdModel = LcpRuby.registry.model_for("showcase_condition_threshold")
|
|
1907
|
+
|
|
1908
|
+
# Categories (for dot-path condition demos)
|
|
1909
|
+
cat_finance = ConditionCategoryModel.create!(name: "Finance", industry: "finance", country_code: "CZ", verified: true)
|
|
1910
|
+
cat_tech = ConditionCategoryModel.create!(name: "Technology", industry: "technology", country_code: "US", verified: true)
|
|
1911
|
+
cat_unverified = ConditionCategoryModel.create!(name: "New Vendor", industry: "retail", country_code: "DE", verified: false)
|
|
1912
|
+
cat_healthcare = ConditionCategoryModel.create!(name: "Healthcare", industry: "healthcare", country_code: "UK", verified: true)
|
|
1913
|
+
|
|
1914
|
+
# Thresholds (for lookup value reference demos)
|
|
1915
|
+
ConditionThresholdModel.create!(key: "high_amount", threshold: 10000, label: "High Amount Threshold")
|
|
1916
|
+
ConditionThresholdModel.create!(key: "critical_amount", threshold: 40000, label: "Critical Amount Threshold")
|
|
1917
|
+
ConditionThresholdModel.create!(key: "min_budget", threshold: 3000, label: "Minimum Budget")
|
|
1918
|
+
|
|
1919
|
+
conditions = [
|
|
1920
|
+
# 1. COMPOUND (all): active + overdue → danger row
|
|
1921
|
+
{ title: "Overdue budget review", status: "active", priority: "high", amount: 5000, budget_limit: 10000,
|
|
1922
|
+
author_id: 1, due_date: 2.weeks.ago.to_date, code: "FIN-001", description: "Quarterly review is past due.",
|
|
1923
|
+
showcase_condition_category_id: cat_finance.id },
|
|
1924
|
+
|
|
1925
|
+
# 2. COMPOUND (any): draft → info row
|
|
1926
|
+
{ title: "Draft proposal", status: "draft", priority: "medium", amount: 2000, budget_limit: 10000,
|
|
1927
|
+
author_id: 1, due_date: 1.month.from_now.to_date, code: "PROP-001", description: "Initial proposal.",
|
|
1928
|
+
showcase_condition_category_id: cat_tech.id },
|
|
1929
|
+
|
|
1930
|
+
# 3. COMPOUND (any): review → info row
|
|
1931
|
+
{ title: "Architecture review", status: "review", priority: "high", amount: 8000, budget_limit: 15000,
|
|
1932
|
+
author_id: 2, due_date: 1.week.from_now.to_date, code: "ARCH-010", description: "Waiting for peer review.",
|
|
1933
|
+
showcase_condition_category_id: cat_tech.id },
|
|
1934
|
+
|
|
1935
|
+
# 4. NOT: closed → muted + strikethrough
|
|
1936
|
+
{ title: "Closed procurement", status: "closed", priority: "low", amount: 3000, budget_limit: 5000,
|
|
1937
|
+
author_id: 1, due_date: 3.months.ago.to_date, code: "PROC-099", description: "Completed and archived.",
|
|
1938
|
+
showcase_condition_category_id: cat_finance.id },
|
|
1939
|
+
|
|
1940
|
+
# 5. DOT-PATH: unverified category → warning row
|
|
1941
|
+
{ title: "New vendor onboarding", status: "active", priority: "medium", amount: 1500, budget_limit: 5000,
|
|
1942
|
+
author_id: 1, due_date: 2.weeks.from_now.to_date, code: "VEND-003", description: "Vendor not yet verified.",
|
|
1943
|
+
showcase_condition_category_id: cat_unverified.id },
|
|
1944
|
+
|
|
1945
|
+
# 6. FIELD_REF: amount > budget_limit → bold row
|
|
1946
|
+
{ title: "Over-budget project", status: "active", priority: "critical", amount: 25000, budget_limit: 15000,
|
|
1947
|
+
author_id: 2, due_date: 1.month.from_now.to_date, code: "PROJ-777", description: "Spending exceeds budget limit.",
|
|
1948
|
+
showcase_condition_category_id: cat_tech.id },
|
|
1949
|
+
|
|
1950
|
+
# 7. STARTS_WITH: code starts with URGENT
|
|
1951
|
+
{ title: "Urgent compliance fix", status: "active", priority: "critical", amount: 12000, budget_limit: 20000,
|
|
1952
|
+
author_id: 1, due_date: 3.days.from_now.to_date, code: "URGENT-SEC-01", description: "Security compliance issue.",
|
|
1953
|
+
showcase_condition_category_id: cat_healthcare.id },
|
|
1954
|
+
|
|
1955
|
+
# 8. CONTAINS: code contains "temp" (case-insensitive)
|
|
1956
|
+
{ title: "Temporary workaround", status: "draft", priority: "low", amount: 500, budget_limit: 5000,
|
|
1957
|
+
author_id: 1, due_date: nil, code: "fix-temp-patch", description: "Short-term fix, needs permanent solution.",
|
|
1958
|
+
showcase_condition_category_id: cat_tech.id },
|
|
1959
|
+
|
|
1960
|
+
# 9. High-value closed: triggers compound record_rule (deny update/destroy)
|
|
1961
|
+
{ title: "Big closed deal", status: "closed", priority: "high", amount: 50000, budget_limit: 30000,
|
|
1962
|
+
author_id: 2, due_date: 2.months.ago.to_date, code: "DEAL-100", description: "High-value closed contract.",
|
|
1963
|
+
showcase_condition_category_id: cat_finance.id },
|
|
1964
|
+
|
|
1965
|
+
# 10. Approved record (collection condition demo target)
|
|
1966
|
+
{ title: "Approved initiative", status: "approved", priority: "medium", amount: 7500, budget_limit: 10000,
|
|
1967
|
+
author_id: 1, due_date: 2.months.from_now.to_date, code: "INIT-042", description: "Approved after task reviews.",
|
|
1968
|
+
showcase_condition_category_id: cat_healthcare.id },
|
|
1969
|
+
|
|
1970
|
+
# 11. Review with tasks (collection condition: has approved task → success)
|
|
1971
|
+
{ title: "Pending approval with tasks", status: "review", priority: "high", amount: 9000, budget_limit: 12000,
|
|
1972
|
+
author_id: 1, due_date: 3.weeks.from_now.to_date, code: "REV-005", description: "Has tasks for review.",
|
|
1973
|
+
showcase_condition_category_id: cat_finance.id },
|
|
1974
|
+
|
|
1975
|
+
# 12. ACCUMULATION: draft + contains "temp" + overdue
|
|
1976
|
+
{ title: "Stale temp draft", status: "draft", priority: "low", amount: 100, budget_limit: 5000,
|
|
1977
|
+
author_id: 1, due_date: 1.month.ago.to_date, code: "TEMP-old-draft", description: "Draft with temp code, past due.",
|
|
1978
|
+
showcase_condition_category_id: cat_unverified.id },
|
|
1979
|
+
|
|
1980
|
+
# 13. Plain record — no rules match
|
|
1981
|
+
{ title: "Normal active project", status: "active", priority: "medium", amount: 4000, budget_limit: 10000,
|
|
1982
|
+
author_id: 1, due_date: 3.months.from_now.to_date, code: "PROJ-STD-01", description: "Regular project, no special conditions.",
|
|
1983
|
+
showcase_condition_category_id: cat_tech.id },
|
|
1984
|
+
|
|
1985
|
+
# 14. DATE reference demo: future due date, active
|
|
1986
|
+
{ title: "Upcoming deadline", status: "active", priority: "high", amount: 6000, budget_limit: 10000,
|
|
1987
|
+
author_id: 2, due_date: 2.days.from_now.to_date, code: "DEAD-001", description: "Due date is in the future — no overdue highlight.",
|
|
1988
|
+
showcase_condition_category_id: cat_finance.id },
|
|
1989
|
+
|
|
1990
|
+
# 15. LOOKUP: amount (15000) exceeds high_amount threshold (10000) → highlighted row
|
|
1991
|
+
{ title: "Above lookup threshold", status: "active", priority: "medium", amount: 15000, budget_limit: 20000,
|
|
1992
|
+
author_id: 1, due_date: 1.month.from_now.to_date, code: "LOOK-001", description: "Amount exceeds the 'high_amount' threshold from showcase_condition_threshold table (lookup value reference).",
|
|
1993
|
+
showcase_condition_category_id: cat_finance.id },
|
|
1994
|
+
|
|
1995
|
+
# 16. LOOKUP: amount (8000) below high_amount threshold (10000) → no highlight
|
|
1996
|
+
{ title: "Below lookup threshold", status: "active", priority: "low", amount: 8000, budget_limit: 20000,
|
|
1997
|
+
author_id: 1, due_date: 2.months.from_now.to_date, code: "LOOK-002", description: "Amount is below the lookup threshold — no highlight.",
|
|
1998
|
+
showcase_condition_category_id: cat_tech.id }
|
|
1999
|
+
]
|
|
2000
|
+
|
|
2001
|
+
condition_records = conditions.map { |attrs| ConditionModel.create!(attrs) }
|
|
2002
|
+
|
|
2003
|
+
# Tasks for collection condition demos
|
|
2004
|
+
# Record 11 ("Pending approval with tasks") — has approved task → success row
|
|
2005
|
+
ConditionTaskModel.create!(title: "Technical review", status: "approved", reviewer_name: "Alice", showcase_condition_id: condition_records[10].id)
|
|
2006
|
+
ConditionTaskModel.create!(title: "Budget review", status: "pending", reviewer_name: "Bob", showcase_condition_id: condition_records[10].id)
|
|
2007
|
+
|
|
2008
|
+
# Record 10 ("Approved initiative") — all tasks approved
|
|
2009
|
+
ConditionTaskModel.create!(title: "Compliance check", status: "approved", reviewer_name: "Carol", showcase_condition_id: condition_records[9].id)
|
|
2010
|
+
ConditionTaskModel.create!(title: "Legal review", status: "approved", reviewer_name: "Dave", showcase_condition_id: condition_records[9].id)
|
|
2011
|
+
|
|
2012
|
+
# Record 3 ("Architecture review") — has tasks but none approved yet
|
|
2013
|
+
ConditionTaskModel.create!(title: "Code review", status: "pending", reviewer_name: "Eve", showcase_condition_id: condition_records[2].id)
|
|
2014
|
+
ConditionTaskModel.create!(title: "Security audit", status: "rejected", reviewer_name: "Frank", showcase_condition_id: condition_records[2].id)
|
|
2015
|
+
|
|
2016
|
+
# Record 6 ("Over-budget project") — mixed tasks
|
|
2017
|
+
ConditionTaskModel.create!(title: "Vendor sign-off", status: "approved", reviewer_name: "Grace", showcase_condition_id: condition_records[5].id)
|
|
2018
|
+
ConditionTaskModel.create!(title: "Finance approval", status: "rejected", reviewer_name: "Hank", showcase_condition_id: condition_records[5].id)
|
|
2019
|
+
|
|
2020
|
+
puts " Created #{ConditionCategoryModel.count} condition categories, #{ConditionThresholdModel.count} condition thresholds, #{ConditionModel.count} condition records, #{ConditionTaskModel.count} condition tasks"
|
|
2021
|
+
|
|
2022
|
+
# Phase: Workflow Showcase
|
|
2023
|
+
if LcpRuby.registry.registered?("showcase_workflow")
|
|
2024
|
+
WorkflowModel = LcpRuby.registry.model_for("showcase_workflow")
|
|
2025
|
+
|
|
2026
|
+
# Set current user for userstamps
|
|
2027
|
+
LcpRuby::Current.user = admin_user if defined?(admin_user)
|
|
2028
|
+
|
|
2029
|
+
# Create records in various workflow states to demonstrate the full lifecycle.
|
|
2030
|
+
# For seeding, bypass the workflow guard by setting the transition-active flag.
|
|
2031
|
+
bypass_guard = ->(record) { record.instance_variable_set(LcpRuby::Workflow::TransitionExecutor::TRANSITION_ACTIVE_IVAR, true) }
|
|
2032
|
+
|
|
2033
|
+
wf_records = [
|
|
2034
|
+
# Draft records (initial state, editable)
|
|
2035
|
+
{ title: "New landing page design", description: "Redesign the main landing page with modern UI components.", priority: "high", category: "feature", amount: 5000.00, reviewer_id: 1, status: "draft", workflow_version: 1 },
|
|
2036
|
+
{ title: "Fix login timeout bug", description: "Users report session timeout after 5 minutes instead of 30.", priority: "urgent", category: "bugfix", amount: 200.00, reviewer_id: 1, status: "draft", workflow_version: 1 },
|
|
2037
|
+
{ title: "Update API documentation", description: "Add missing endpoints and update examples for v3 API.", priority: "normal", category: "documentation", amount: 0, reviewer_id: 1, status: "draft", workflow_version: 1 },
|
|
2038
|
+
|
|
2039
|
+
# Pending review (submitted, key fields locked) — has active approval requests
|
|
2040
|
+
{ title: "Database migration to PostgreSQL", description: "Migrate from MySQL to PostgreSQL for better JSON support.", priority: "high", category: "improvement", amount: 15000.00, reviewer_id: 1, status: "pending_review", submitted_at: 2.days.ago, submitted_by: 1, submitted_by_name: "Admin User", workflow_version: 1 },
|
|
2041
|
+
{ title: "Add dark mode support", description: "Implement dark mode toggle with CSS variables.", priority: "normal", category: "feature", amount: 3000.00, reviewer_id: 1, status: "pending_review", submitted_at: 1.day.ago, submitted_by: 1, submitted_by_name: "Admin User", workflow_version: 1 },
|
|
2042
|
+
|
|
2043
|
+
# Approved (readonly: all) — approved_amount captured at approval time via field_ref
|
|
2044
|
+
{ title: "Performance optimization sprint", description: "Optimize database queries and add Redis caching layer.", priority: "high", category: "improvement", amount: 8000.00, approved_amount: 8000.00, reviewer_id: 1, status: "approved", submitted_at: 5.days.ago, submitted_by: 1, submitted_by_name: "Admin User", approved_at: 3.days.ago, reviewed_at: 3.days.ago, workflow_version: 1 },
|
|
2045
|
+
|
|
2046
|
+
# In progress (some fields locked)
|
|
2047
|
+
{ title: "Mobile responsive redesign", description: "Make all pages responsive for mobile devices.", priority: "high", category: "feature", amount: 12000.00, approved_amount: 12000.00, reviewer_id: 1, status: "in_progress", submitted_at: 7.days.ago, submitted_by: 1, submitted_by_name: "Admin User", approved_at: 5.days.ago, reviewed_at: 5.days.ago, workflow_version: 1 },
|
|
2048
|
+
|
|
2049
|
+
# Rejected (can be reworked) — has completed (rejected) approval request
|
|
2050
|
+
{ title: "Replace React with Vue", description: "Migrate frontend framework from React to Vue.js.", priority: "normal", category: "improvement", amount: 50000.00, reviewer_id: 1, status: "rejected", submitted_at: 10.days.ago, submitted_by: 1, submitted_by_name: "Admin User", reviewed_at: 8.days.ago, rejection_reason: "Cost too high. Please break into smaller phases.", workflow_version: 1 },
|
|
2051
|
+
|
|
2052
|
+
# On hold (waiting for external input)
|
|
2053
|
+
{ title: "Third-party API integration", description: "Integrate with Stripe API for payment processing.", priority: "high", category: "feature", amount: 7500.00, approved_amount: 7500.00, reviewer_id: 1, status: "on_hold", submitted_at: 14.days.ago, submitted_by: 1, submitted_by_name: "Admin User", approved_at: 12.days.ago, reviewed_at: 12.days.ago, notes: "Waiting for Stripe sandbox credentials.", workflow_version: 1 },
|
|
2054
|
+
|
|
2055
|
+
# Completed (terminal, fully locked) — has completed (approved) approval request
|
|
2056
|
+
{ title: "Set up CI/CD pipeline", description: "GitHub Actions workflow for automated testing and deployment.", priority: "high", category: "improvement", amount: 2000.00, approved_amount: 2000.00, reviewer_id: 1, status: "completed", submitted_at: 30.days.ago, submitted_by: 1, submitted_by_name: "Admin User", approved_at: 28.days.ago, reviewed_at: 28.days.ago, completed_at: 20.days.ago, workflow_version: 1 },
|
|
2057
|
+
{ title: "Add unit test coverage", description: "Increase test coverage from 40% to 80%.", priority: "normal", category: "improvement", amount: 4000.00, approved_amount: 4000.00, reviewer_id: 1, status: "completed", submitted_at: 25.days.ago, submitted_by: 1, submitted_by_name: "Admin User", approved_at: 23.days.ago, reviewed_at: 23.days.ago, completed_at: 15.days.ago, workflow_version: 1 },
|
|
2058
|
+
|
|
2059
|
+
# Cancelled (terminal)
|
|
2060
|
+
{ title: "Legacy system migration", description: "Migrate data from legacy COBOL system.", priority: "low", category: "improvement", amount: 100000.00, reviewer_id: 1, status: "cancelled", submitted_at: 20.days.ago, submitted_by: 1, submitted_by_name: "Admin User", notes: "Project cancelled due to budget cuts.", workflow_version: 1 }
|
|
2061
|
+
].map do |attrs|
|
|
2062
|
+
record = WorkflowModel.new(attrs)
|
|
2063
|
+
bypass_guard.call(record)
|
|
2064
|
+
record.save!
|
|
2065
|
+
record
|
|
2066
|
+
end
|
|
2067
|
+
|
|
2068
|
+
puts " Created #{WorkflowModel.count} showcase_workflow records"
|
|
2069
|
+
|
|
2070
|
+
# Create audit log entries to demonstrate the audit trail
|
|
2071
|
+
if LcpRuby.registry.registered?("workflow_audit_log")
|
|
2072
|
+
AuditLogModel = LcpRuby.registry.model_for("workflow_audit_log")
|
|
2073
|
+
|
|
2074
|
+
user_snapshot = { id: 1, email: "admin@example.com", name: "Admin User", role: "admin" }
|
|
2075
|
+
|
|
2076
|
+
audit_entries = [
|
|
2077
|
+
# Completed request lifecycle: draft → pending_review → approved → in_progress → completed
|
|
2078
|
+
{ record_type: "showcase_workflow", record_id: wf_records[9].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2079
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2080
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 30.days.ago },
|
|
2081
|
+
{ record_type: "showcase_workflow", record_id: wf_records[9].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2082
|
+
transition_name: "approve", from_state: "pending_review", to_state: "approved",
|
|
2083
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 28.days.ago },
|
|
2084
|
+
{ record_type: "showcase_workflow", record_id: wf_records[9].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2085
|
+
transition_name: "start_work", from_state: "approved", to_state: "in_progress",
|
|
2086
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 27.days.ago },
|
|
2087
|
+
{ record_type: "showcase_workflow", record_id: wf_records[9].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2088
|
+
transition_name: "complete", from_state: "in_progress", to_state: "completed",
|
|
2089
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 20.days.ago },
|
|
2090
|
+
|
|
2091
|
+
# Rejected request: draft → pending_review → rejected
|
|
2092
|
+
{ record_type: "showcase_workflow", record_id: wf_records[7].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2093
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2094
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 10.days.ago },
|
|
2095
|
+
{ record_type: "showcase_workflow", record_id: wf_records[7].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2096
|
+
transition_name: "reject", from_state: "pending_review", to_state: "rejected",
|
|
2097
|
+
user_id: 1, user_snapshot: user_snapshot, comment: "Cost too high. Please break into smaller phases.",
|
|
2098
|
+
created_at: 8.days.ago },
|
|
2099
|
+
|
|
2100
|
+
# On-hold request: draft → pending_review → approved → in_progress → on_hold
|
|
2101
|
+
{ record_type: "showcase_workflow", record_id: wf_records[8].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2102
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2103
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 14.days.ago },
|
|
2104
|
+
{ record_type: "showcase_workflow", record_id: wf_records[8].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2105
|
+
transition_name: "approve", from_state: "pending_review", to_state: "approved",
|
|
2106
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 12.days.ago },
|
|
2107
|
+
{ record_type: "showcase_workflow", record_id: wf_records[8].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2108
|
+
transition_name: "start_work", from_state: "approved", to_state: "in_progress",
|
|
2109
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 11.days.ago },
|
|
2110
|
+
{ record_type: "showcase_workflow", record_id: wf_records[8].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2111
|
+
transition_name: "hold", from_state: "in_progress", to_state: "on_hold",
|
|
2112
|
+
user_id: 1, user_snapshot: user_snapshot, comment: "Waiting for Stripe sandbox credentials.",
|
|
2113
|
+
created_at: 10.days.ago },
|
|
2114
|
+
|
|
2115
|
+
# Cancelled request: draft → pending_review → cancelled
|
|
2116
|
+
{ record_type: "showcase_workflow", record_id: wf_records[11].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2117
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2118
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 20.days.ago },
|
|
2119
|
+
{ record_type: "showcase_workflow", record_id: wf_records[11].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2120
|
+
transition_name: "cancel", from_state: "pending_review", to_state: "cancelled",
|
|
2121
|
+
user_id: 1, user_snapshot: user_snapshot, comment: "Project cancelled due to budget cuts.",
|
|
2122
|
+
created_at: 19.days.ago },
|
|
2123
|
+
|
|
2124
|
+
# Pending review submissions
|
|
2125
|
+
{ record_type: "showcase_workflow", record_id: wf_records[3].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2126
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2127
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 2.days.ago },
|
|
2128
|
+
{ record_type: "showcase_workflow", record_id: wf_records[4].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2129
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2130
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 1.day.ago },
|
|
2131
|
+
|
|
2132
|
+
# System trigger example (auto_approve)
|
|
2133
|
+
{ record_type: "showcase_workflow", record_id: wf_records[5].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2134
|
+
transition_name: "submit", from_state: "draft", to_state: "pending_review",
|
|
2135
|
+
user_id: 1, user_snapshot: user_snapshot, created_at: 5.days.ago },
|
|
2136
|
+
{ record_type: "showcase_workflow", record_id: wf_records[5].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2137
|
+
transition_name: "auto_approve", from_state: "pending_review", to_state: "approved",
|
|
2138
|
+
user_id: 1, user_snapshot: user_snapshot,
|
|
2139
|
+
metadata: { triggered_by: "approval_engine" },
|
|
2140
|
+
created_at: 3.days.ago },
|
|
2141
|
+
|
|
2142
|
+
# Wildcard reset_to_draft example (admin emergency reset from on_hold)
|
|
2143
|
+
{ record_type: "showcase_workflow", record_id: wf_records[2].id, workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2144
|
+
transition_name: "reset_to_draft", from_state: "pending_review", to_state: "draft",
|
|
2145
|
+
user_id: 1, user_snapshot: user_snapshot,
|
|
2146
|
+
comment: "Emergency reset — requirements changed, needs full rework.",
|
|
2147
|
+
created_at: 1.day.ago }
|
|
2148
|
+
]
|
|
2149
|
+
|
|
2150
|
+
audit_entries.each { |attrs| AuditLogModel.create!(attrs) }
|
|
2151
|
+
puts " Created #{AuditLogModel.count} workflow audit log entries"
|
|
2152
|
+
end
|
|
2153
|
+
|
|
2154
|
+
# Create approval request/step/task seed data to demonstrate the multi-step approval engine
|
|
2155
|
+
if LcpRuby.registry.registered?("workflow_approval_request") &&
|
|
2156
|
+
LcpRuby.registry.registered?("workflow_approval_step") &&
|
|
2157
|
+
LcpRuby.registry.registered?("workflow_approval_task")
|
|
2158
|
+
ApprovalRequestModel = LcpRuby.registry.model_for("workflow_approval_request")
|
|
2159
|
+
ApprovalStepModel = LcpRuby.registry.model_for("workflow_approval_step")
|
|
2160
|
+
ApprovalTaskModel = LcpRuby.registry.model_for("workflow_approval_task")
|
|
2161
|
+
|
|
2162
|
+
user_snapshot = { id: 1, email: "admin@example.com", name: "Admin User", role: "admin" }
|
|
2163
|
+
|
|
2164
|
+
# --- Active approval request for "Database migration to PostgreSQL" (wf_records[3]) ---
|
|
2165
|
+
# Step 1: reviewer_check is active with pending task
|
|
2166
|
+
# Step 2: admin_sign_off is pending (sequential, waits for step 1)
|
|
2167
|
+
req1 = ApprovalRequestModel.create!(
|
|
2168
|
+
record_type: "showcase_workflow", record_id: wf_records[3].id,
|
|
2169
|
+
workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2170
|
+
state_name: "pending_review", strategy: "sequential",
|
|
2171
|
+
status: "pending", initiated_by_id: 1, initiated_by_snapshot: user_snapshot,
|
|
2172
|
+
restart_count: 0
|
|
2173
|
+
)
|
|
2174
|
+
step1_req1 = ApprovalStepModel.create!(
|
|
2175
|
+
approval_request_id: req1.id, step_name: "reviewer_check", label: "Reviewer Check",
|
|
2176
|
+
position: 1, status: "active", required_approvals: 1, assignment: "all",
|
|
2177
|
+
activated_at: 2.days.ago
|
|
2178
|
+
)
|
|
2179
|
+
step2_req1 = ApprovalStepModel.create!(
|
|
2180
|
+
approval_request_id: req1.id, step_name: "admin_sign_off", label: "Admin Sign-off",
|
|
2181
|
+
position: 2, status: "pending", required_approvals: 1, assignment: "all"
|
|
2182
|
+
)
|
|
2183
|
+
# Pending task for current user (approver_id: 1 matches reviewer_id field)
|
|
2184
|
+
ApprovalTaskModel.create!(
|
|
2185
|
+
approval_step_id: step1_req1.id, approver_id: 1,
|
|
2186
|
+
approver_snapshot: user_snapshot, status: "pending"
|
|
2187
|
+
)
|
|
2188
|
+
|
|
2189
|
+
# --- Active approval request for "Add dark mode support" (wf_records[4]) ---
|
|
2190
|
+
# Step 1: reviewer_check completed (approved), step 2: admin_sign_off active
|
|
2191
|
+
req2 = ApprovalRequestModel.create!(
|
|
2192
|
+
record_type: "showcase_workflow", record_id: wf_records[4].id,
|
|
2193
|
+
workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2194
|
+
state_name: "pending_review", strategy: "sequential",
|
|
2195
|
+
status: "pending", initiated_by_id: 1, initiated_by_snapshot: user_snapshot,
|
|
2196
|
+
restart_count: 0
|
|
2197
|
+
)
|
|
2198
|
+
step1_req2 = ApprovalStepModel.create!(
|
|
2199
|
+
approval_request_id: req2.id, step_name: "reviewer_check", label: "Reviewer Check",
|
|
2200
|
+
position: 1, status: "completed", required_approvals: 1, assignment: "all",
|
|
2201
|
+
activated_at: 1.day.ago, completed_at: 12.hours.ago
|
|
2202
|
+
)
|
|
2203
|
+
step2_req2 = ApprovalStepModel.create!(
|
|
2204
|
+
approval_request_id: req2.id, step_name: "admin_sign_off", label: "Admin Sign-off",
|
|
2205
|
+
position: 2, status: "active", required_approvals: 1, assignment: "all",
|
|
2206
|
+
activated_at: 12.hours.ago
|
|
2207
|
+
)
|
|
2208
|
+
# Completed task (approved) from step 1
|
|
2209
|
+
ApprovalTaskModel.create!(
|
|
2210
|
+
approval_step_id: step1_req2.id, approver_id: 1,
|
|
2211
|
+
approver_snapshot: user_snapshot, status: "approved",
|
|
2212
|
+
comment: "Looks good, dark mode is a frequently requested feature.",
|
|
2213
|
+
decided_at: 12.hours.ago
|
|
2214
|
+
)
|
|
2215
|
+
# Pending task for admin sign-off (step 2)
|
|
2216
|
+
ApprovalTaskModel.create!(
|
|
2217
|
+
approval_step_id: step2_req2.id, approver_id: 1,
|
|
2218
|
+
approver_snapshot: user_snapshot, status: "pending"
|
|
2219
|
+
)
|
|
2220
|
+
|
|
2221
|
+
# --- Completed (approved) approval request for "Set up CI/CD pipeline" (wf_records[9]) ---
|
|
2222
|
+
# Both steps completed with approved tasks
|
|
2223
|
+
req3 = ApprovalRequestModel.create!(
|
|
2224
|
+
record_type: "showcase_workflow", record_id: wf_records[9].id,
|
|
2225
|
+
workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2226
|
+
state_name: "pending_review", strategy: "sequential",
|
|
2227
|
+
status: "approved", initiated_by_id: 1, initiated_by_snapshot: user_snapshot,
|
|
2228
|
+
restart_count: 0, resolved_at: 28.days.ago
|
|
2229
|
+
)
|
|
2230
|
+
step1_req3 = ApprovalStepModel.create!(
|
|
2231
|
+
approval_request_id: req3.id, step_name: "reviewer_check", label: "Reviewer Check",
|
|
2232
|
+
position: 1, status: "completed", required_approvals: 1, assignment: "all",
|
|
2233
|
+
activated_at: 30.days.ago, completed_at: 29.days.ago
|
|
2234
|
+
)
|
|
2235
|
+
step2_req3 = ApprovalStepModel.create!(
|
|
2236
|
+
approval_request_id: req3.id, step_name: "admin_sign_off", label: "Admin Sign-off",
|
|
2237
|
+
position: 2, status: "completed", required_approvals: 1, assignment: "all",
|
|
2238
|
+
activated_at: 29.days.ago, completed_at: 28.days.ago
|
|
2239
|
+
)
|
|
2240
|
+
ApprovalTaskModel.create!(
|
|
2241
|
+
approval_step_id: step1_req3.id, approver_id: 1,
|
|
2242
|
+
approver_snapshot: user_snapshot, status: "approved",
|
|
2243
|
+
comment: "CI/CD is critical infrastructure, approved.", decided_at: 29.days.ago
|
|
2244
|
+
)
|
|
2245
|
+
ApprovalTaskModel.create!(
|
|
2246
|
+
approval_step_id: step2_req3.id, approver_id: 1,
|
|
2247
|
+
approver_snapshot: user_snapshot, status: "approved",
|
|
2248
|
+
comment: "Final sign-off. Budget approved.", decided_at: 28.days.ago
|
|
2249
|
+
)
|
|
2250
|
+
|
|
2251
|
+
# --- Completed (rejected) approval request for "Replace React with Vue" (wf_records[7]) ---
|
|
2252
|
+
# Step 1 completed (rejected) — short_circuit stopped further steps
|
|
2253
|
+
req4 = ApprovalRequestModel.create!(
|
|
2254
|
+
record_type: "showcase_workflow", record_id: wf_records[7].id,
|
|
2255
|
+
workflow_name: "showcase_workflow", workflow_version: 1,
|
|
2256
|
+
state_name: "pending_review", strategy: "sequential",
|
|
2257
|
+
status: "rejected", initiated_by_id: 1, initiated_by_snapshot: user_snapshot,
|
|
2258
|
+
restart_count: 0, resolved_at: 8.days.ago
|
|
2259
|
+
)
|
|
2260
|
+
step1_req4 = ApprovalStepModel.create!(
|
|
2261
|
+
approval_request_id: req4.id, step_name: "reviewer_check", label: "Reviewer Check",
|
|
2262
|
+
position: 1, status: "completed", required_approvals: 1, assignment: "all",
|
|
2263
|
+
activated_at: 10.days.ago, completed_at: 8.days.ago
|
|
2264
|
+
)
|
|
2265
|
+
step2_req4 = ApprovalStepModel.create!(
|
|
2266
|
+
approval_request_id: req4.id, step_name: "admin_sign_off", label: "Admin Sign-off",
|
|
2267
|
+
position: 2, status: "skipped", required_approvals: 1, assignment: "all"
|
|
2268
|
+
)
|
|
2269
|
+
ApprovalTaskModel.create!(
|
|
2270
|
+
approval_step_id: step1_req4.id, approver_id: 1,
|
|
2271
|
+
approver_snapshot: user_snapshot, status: "rejected",
|
|
2272
|
+
comment: "Cost too high. Please break into smaller phases.",
|
|
2273
|
+
decided_at: 8.days.ago
|
|
2274
|
+
)
|
|
2275
|
+
|
|
2276
|
+
# --- Delegated task example for "Database migration" (add a delegated task to req1) ---
|
|
2277
|
+
# Shows delegation: original task delegated, new task created for delegate
|
|
2278
|
+
delegated_task = ApprovalTaskModel.create!(
|
|
2279
|
+
approval_step_id: step1_req1.id, approver_id: 1,
|
|
2280
|
+
approver_snapshot: user_snapshot, status: "delegated",
|
|
2281
|
+
comment: "Delegating to editor for technical review.",
|
|
2282
|
+
decided_at: 1.day.ago
|
|
2283
|
+
)
|
|
2284
|
+
ApprovalTaskModel.create!(
|
|
2285
|
+
approval_step_id: step1_req1.id, approver_id: 1,
|
|
2286
|
+
approver_snapshot: { id: 1, email: "admin@example.com", name: "Admin User (delegate)", role: "admin" },
|
|
2287
|
+
status: "pending", delegated_from_id: delegated_task.id
|
|
2288
|
+
)
|
|
2289
|
+
|
|
2290
|
+
puts " Created #{ApprovalRequestModel.count} approval requests, #{ApprovalStepModel.count} approval steps, #{ApprovalTaskModel.count} approval tasks"
|
|
2291
|
+
end
|
|
2292
|
+
|
|
2293
|
+
LcpRuby::Current.user = nil
|
|
2294
|
+
end
|
|
2295
|
+
|
|
2296
|
+
# Phase: Background Jobs Showcase
|
|
2297
|
+
if LcpRuby.registry.registered?("showcase_job_execution")
|
|
2298
|
+
JobExecModel = LcpRuby.registry.model_for("showcase_job_execution")
|
|
2299
|
+
|
|
2300
|
+
user_snapshot = { id: 1, email: "admin@example.com", name: "Admin User", role: "admin" }
|
|
2301
|
+
now = Time.current
|
|
2302
|
+
|
|
2303
|
+
[
|
|
2304
|
+
# Completed successfully — cleanup job
|
|
2305
|
+
{
|
|
2306
|
+
job_type: "showcase_cleanup",
|
|
2307
|
+
status: "completed",
|
|
2308
|
+
progress: 100,
|
|
2309
|
+
attempt: 1,
|
|
2310
|
+
params: { model: "showcase_job_execution", scope: "completed_jobs", operation: "count" }.to_json,
|
|
2311
|
+
log: [
|
|
2312
|
+
{ at: (now - 2.hours).iso8601, level: "info", message: "Starting run_scope: showcase_job_execution.completed_jobs" },
|
|
2313
|
+
{ at: (now - 2.hours + 3.seconds).iso8601, level: "info", message: "Operation count completed: 12 records matched" }
|
|
2314
|
+
].to_json,
|
|
2315
|
+
started_at: now - 2.hours,
|
|
2316
|
+
last_attempt_at: now - 2.hours,
|
|
2317
|
+
completed_at: now - 2.hours + 3.seconds,
|
|
2318
|
+
triggered_by_id: 1,
|
|
2319
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2320
|
+
unique_key: Digest::SHA256.hexdigest("job_type=showcase_cleanup")[0..63],
|
|
2321
|
+
active_job_id: SecureRandom.uuid,
|
|
2322
|
+
created_at: now - 2.hours - 5.seconds
|
|
2323
|
+
},
|
|
2324
|
+
# Completed — webhook job
|
|
2325
|
+
{
|
|
2326
|
+
job_type: "showcase_webhook",
|
|
2327
|
+
status: "completed",
|
|
2328
|
+
progress: 100,
|
|
2329
|
+
attempt: 1,
|
|
2330
|
+
params: { source: "lcp_showcase", job_type: "webhook_demo" }.to_json,
|
|
2331
|
+
log: [
|
|
2332
|
+
{ at: (now - 1.hour).iso8601, level: "info", message: "POST https://httpbin.org/post" },
|
|
2333
|
+
{ at: (now - 1.hour + 2.seconds).iso8601, level: "info", message: "Response: 200 OK (1.8s)" }
|
|
2334
|
+
].to_json,
|
|
2335
|
+
started_at: now - 1.hour,
|
|
2336
|
+
last_attempt_at: now - 1.hour,
|
|
2337
|
+
completed_at: now - 1.hour + 2.seconds,
|
|
2338
|
+
triggered_by_id: 1,
|
|
2339
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2340
|
+
active_job_id: SecureRandom.uuid,
|
|
2341
|
+
created_at: now - 1.hour - 3.seconds
|
|
2342
|
+
},
|
|
2343
|
+
# Completed — multi-step job
|
|
2344
|
+
{
|
|
2345
|
+
job_type: "showcase_multi_step",
|
|
2346
|
+
status: "completed",
|
|
2347
|
+
progress: 100,
|
|
2348
|
+
attempt: 1,
|
|
2349
|
+
log: [
|
|
2350
|
+
{ at: (now - 45.minutes).iso8601, level: "info", message: "Step 1/3: fire_event(step_one_started)" },
|
|
2351
|
+
{ at: (now - 45.minutes + 1.second).iso8601, level: "info", message: "Step 2/3: fire_event(step_two_started)" },
|
|
2352
|
+
{ at: (now - 45.minutes + 2.seconds).iso8601, level: "info", message: "Step 3/3: fire_event(step_three_started)" },
|
|
2353
|
+
{ at: (now - 45.minutes + 3.seconds).iso8601, level: "info", message: "All 3 steps completed" }
|
|
2354
|
+
].to_json,
|
|
2355
|
+
started_at: now - 45.minutes,
|
|
2356
|
+
last_attempt_at: now - 45.minutes,
|
|
2357
|
+
completed_at: now - 45.minutes + 3.seconds,
|
|
2358
|
+
triggered_by_id: 1,
|
|
2359
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2360
|
+
active_job_id: SecureRandom.uuid,
|
|
2361
|
+
created_at: now - 45.minutes - 2.seconds
|
|
2362
|
+
},
|
|
2363
|
+
# Failed with retries exhausted — webhook to bad endpoint
|
|
2364
|
+
{
|
|
2365
|
+
job_type: "showcase_webhook",
|
|
2366
|
+
status: "failed",
|
|
2367
|
+
progress: 0,
|
|
2368
|
+
attempt: 2,
|
|
2369
|
+
error_message: "Net::ReadTimeout: execution expired after 10s",
|
|
2370
|
+
params: { source: "lcp_showcase", job_type: "webhook_demo" }.to_json,
|
|
2371
|
+
log: [
|
|
2372
|
+
{ at: (now - 3.hours).iso8601, level: "info", message: "POST https://httpbin.org/post", attempt: 1 },
|
|
2373
|
+
{ at: (now - 3.hours + 10.seconds).iso8601, level: "error", message: "Net::ReadTimeout: execution expired after 10s", attempt: 1 },
|
|
2374
|
+
{ at: (now - 3.hours + 40.seconds).iso8601, level: "info", message: "Retry 2/1 — re-enqueuing with 30s delay", attempt: 1 },
|
|
2375
|
+
{ at: (now - 2.hours - 30.minutes).iso8601, level: "info", message: "POST https://httpbin.org/post", attempt: 2 },
|
|
2376
|
+
{ at: (now - 2.hours - 30.minutes + 10.seconds).iso8601, level: "error", message: "Net::ReadTimeout: execution expired after 10s", attempt: 2 },
|
|
2377
|
+
{ at: (now - 2.hours - 30.minutes + 10.seconds).iso8601, level: "error", message: "Max retries (1) exhausted — marking as failed" }
|
|
2378
|
+
].to_json,
|
|
2379
|
+
started_at: now - 3.hours,
|
|
2380
|
+
last_attempt_at: now - 2.hours - 30.minutes,
|
|
2381
|
+
triggered_by_id: 1,
|
|
2382
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2383
|
+
active_job_id: SecureRandom.uuid,
|
|
2384
|
+
created_at: now - 3.hours - 2.seconds
|
|
2385
|
+
},
|
|
2386
|
+
# Currently running — long cleanup
|
|
2387
|
+
{
|
|
2388
|
+
job_type: "showcase_cleanup",
|
|
2389
|
+
status: "running",
|
|
2390
|
+
progress: 65,
|
|
2391
|
+
attempt: 1,
|
|
2392
|
+
params: { model: "showcase_job_execution", scope: "completed_jobs", operation: "destroy_all", batch_size: 100 }.to_json,
|
|
2393
|
+
log: [
|
|
2394
|
+
{ at: (now - 5.minutes).iso8601, level: "info", message: "Starting run_scope: showcase_job_execution.completed_jobs" },
|
|
2395
|
+
{ at: (now - 4.minutes).iso8601, level: "info", message: "Batch 1: destroyed 100 records" },
|
|
2396
|
+
{ at: (now - 3.minutes).iso8601, level: "info", message: "Batch 2: destroyed 100 records" }
|
|
2397
|
+
].to_json,
|
|
2398
|
+
started_at: now - 5.minutes,
|
|
2399
|
+
last_attempt_at: now - 5.minutes,
|
|
2400
|
+
triggered_by_id: 1,
|
|
2401
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2402
|
+
active_job_id: SecureRandom.uuid,
|
|
2403
|
+
created_at: now - 5.minutes - 1.second
|
|
2404
|
+
},
|
|
2405
|
+
# Pending — queued but not started
|
|
2406
|
+
{
|
|
2407
|
+
job_type: "showcase_event_triggered",
|
|
2408
|
+
status: "pending",
|
|
2409
|
+
progress: 0,
|
|
2410
|
+
attempt: 0,
|
|
2411
|
+
params: { event: "showcase_job_triggered" }.to_json,
|
|
2412
|
+
log: [].to_json,
|
|
2413
|
+
scheduled_at: now + 10.minutes,
|
|
2414
|
+
triggered_by_id: 1,
|
|
2415
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2416
|
+
active_job_id: SecureRandom.uuid,
|
|
2417
|
+
created_at: now - 30.seconds
|
|
2418
|
+
},
|
|
2419
|
+
# Cancelled — user requested cancellation
|
|
2420
|
+
{
|
|
2421
|
+
job_type: "showcase_multi_step",
|
|
2422
|
+
status: "cancelled",
|
|
2423
|
+
progress: 33,
|
|
2424
|
+
attempt: 1,
|
|
2425
|
+
cancellation_requested: true,
|
|
2426
|
+
log: [
|
|
2427
|
+
{ at: (now - 6.hours).iso8601, level: "info", message: "Step 1/3: fire_event(step_one_started)" },
|
|
2428
|
+
{ at: (now - 6.hours + 1.second).iso8601, level: "info", message: "Cancellation requested — stopping after step 1" },
|
|
2429
|
+
{ at: (now - 6.hours + 1.second).iso8601, level: "warn", message: "Job cancelled by user" }
|
|
2430
|
+
].to_json,
|
|
2431
|
+
started_at: now - 6.hours,
|
|
2432
|
+
last_attempt_at: now - 6.hours,
|
|
2433
|
+
completed_at: now - 6.hours + 1.second,
|
|
2434
|
+
triggered_by_id: 1,
|
|
2435
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2436
|
+
active_job_id: SecureRandom.uuid,
|
|
2437
|
+
created_at: now - 6.hours - 3.seconds
|
|
2438
|
+
},
|
|
2439
|
+
# Completed with unique key — deduplicated
|
|
2440
|
+
{
|
|
2441
|
+
job_type: "showcase_cleanup",
|
|
2442
|
+
status: "completed",
|
|
2443
|
+
progress: 100,
|
|
2444
|
+
attempt: 1,
|
|
2445
|
+
params: { model: "showcase_job_execution", scope: "failed_jobs", operation: "count" }.to_json,
|
|
2446
|
+
log: [
|
|
2447
|
+
{ at: (now - 30.minutes).iso8601, level: "info", message: "Starting run_scope: showcase_job_execution.failed_jobs" },
|
|
2448
|
+
{ at: (now - 30.minutes + 1.second).iso8601, level: "info", message: "Operation count completed: 3 records matched" }
|
|
2449
|
+
].to_json,
|
|
2450
|
+
started_at: now - 30.minutes,
|
|
2451
|
+
last_attempt_at: now - 30.minutes,
|
|
2452
|
+
completed_at: now - 30.minutes + 1.second,
|
|
2453
|
+
triggered_by_id: 1,
|
|
2454
|
+
triggered_by_snapshot: user_snapshot.to_json,
|
|
2455
|
+
unique_key: Digest::SHA256.hexdigest("job_type=showcase_cleanup|scope=failed_jobs")[0..63],
|
|
2456
|
+
active_job_id: SecureRandom.uuid,
|
|
2457
|
+
created_at: now - 30.minutes - 2.seconds
|
|
2458
|
+
}
|
|
2459
|
+
].each { |attrs| JobExecModel.create!(attrs) }
|
|
2460
|
+
|
|
2461
|
+
puts " Created #{JobExecModel.count} job execution records (background jobs showcase)"
|
|
2462
|
+
end
|
|
2463
|
+
|
|
2464
|
+
# Phase: Audited Records Showcase
|
|
2465
|
+
if LcpRuby.registry.registered?("showcase_audited_record")
|
|
2466
|
+
AuditedRecordModel = LcpRuby.registry.model_for("showcase_audited_record")
|
|
2467
|
+
|
|
2468
|
+
LcpRuby::Current.user = admin_user if defined?(admin_user)
|
|
2469
|
+
|
|
2470
|
+
[
|
|
2471
|
+
{ title: "Server Configuration Update", description: "Updated production server settings for improved throughput.", status: "active", priority: "high", amount: 2500.00, completion: 85.0 },
|
|
2472
|
+
{ title: "Security Audit Report", description: "Quarterly security audit findings and remediation plan.", status: "active", priority: "high", amount: 15000.00, completion: 100.0 },
|
|
2473
|
+
{ title: "API Rate Limiting Policy", description: "New rate limiting rules for public API endpoints.", status: "draft", priority: "normal", amount: 500.00, completion: 30.0 },
|
|
2474
|
+
{ title: "Database Backup Procedure", description: "Daily automated backup with 30-day retention.", status: "active", priority: "normal", amount: 1200.00, completion: 95.0 },
|
|
2475
|
+
{ title: "Legacy Code Cleanup", description: "Remove deprecated modules from the codebase.", status: "archived", priority: "low", amount: 3000.00, completion: 100.0 },
|
|
2476
|
+
{ title: "Performance Benchmarks", description: "Baseline performance metrics for all API endpoints.", status: "draft", priority: "high", amount: 800.00, completion: 10.0 }
|
|
2477
|
+
].each { |attrs| AuditedRecordModel.create!(attrs) }
|
|
2478
|
+
|
|
2479
|
+
# Simulate an update to generate audit trail
|
|
2480
|
+
record = AuditedRecordModel.first
|
|
2481
|
+
record.update!(status: "archived", completion: 100.0) if record
|
|
2482
|
+
|
|
2483
|
+
LcpRuby::Current.user = nil
|
|
2484
|
+
puts " Created #{AuditedRecordModel.count} audited records (with audit trail)"
|
|
2485
|
+
end
|
|
2486
|
+
|
|
2487
|
+
# Phase: DB-Stored Page Definitions
|
|
2488
|
+
if LcpRuby.registry.registered?("page_config")
|
|
2489
|
+
PageConfigModel = LcpRuby.registry.model_for("page_config")
|
|
2490
|
+
|
|
2491
|
+
[
|
|
2492
|
+
{
|
|
2493
|
+
name: "custom_employee_dashboard",
|
|
2494
|
+
definition: {
|
|
2495
|
+
"name" => "custom_employee_dashboard",
|
|
2496
|
+
"slug" => "employee-dashboard",
|
|
2497
|
+
"model" => "employee",
|
|
2498
|
+
"layout" => "semantic",
|
|
2499
|
+
"zones" => [
|
|
2500
|
+
{ "name" => "main", "presenter" => "employees", "area" => "main" },
|
|
2501
|
+
{ "name" => "department_kpi", "type" => "widget", "area" => "sidebar", "width" => 4,
|
|
2502
|
+
"widget" => { "type" => "kpi_card", "model" => "department", "aggregate" => "count", "label_key" => "Departments" } }
|
|
2503
|
+
]
|
|
2504
|
+
},
|
|
2505
|
+
active: false,
|
|
2506
|
+
notes: "Example DB-stored composite page — activate to override auto-generated employee page."
|
|
2507
|
+
},
|
|
2508
|
+
{
|
|
2509
|
+
name: "project_overview_db",
|
|
2510
|
+
definition: {
|
|
2511
|
+
"name" => "project_overview_db",
|
|
2512
|
+
"slug" => "project-overview",
|
|
2513
|
+
"model" => "project",
|
|
2514
|
+
"layout" => "semantic",
|
|
2515
|
+
"zones" => [
|
|
2516
|
+
{ "name" => "main", "presenter" => "projects", "area" => "main" },
|
|
2517
|
+
{ "name" => "project_count", "type" => "widget", "area" => "sidebar", "width" => 4,
|
|
2518
|
+
"widget" => { "type" => "kpi_card", "model" => "project", "aggregate" => "count", "label_key" => "Total Projects" } }
|
|
2519
|
+
]
|
|
2520
|
+
},
|
|
2521
|
+
active: false,
|
|
2522
|
+
notes: "Example DB-stored page for projects with a KPI sidebar."
|
|
2523
|
+
}
|
|
2524
|
+
].each { |attrs| PageConfigModel.create!(attrs) }
|
|
2525
|
+
|
|
2526
|
+
puts " Created #{PageConfigModel.count} page config records"
|
|
2527
|
+
end
|
|
2528
|
+
|
|
2529
|
+
# Phase: Feature Catalog — Auditing entries
|
|
2530
|
+
|
|
2531
|
+
# Phase: Feature Catalog — DB-Stored Page Definitions entries
|
|
2532
|
+
|
|
2533
|
+
# Phase: Feature Catalog — Custom Types entries
|
|
2534
|
+
|
|
2535
|
+
# Phase: Model Inheritance Showcase
|
|
2536
|
+
if LcpRuby.registry.registered?("showcase_report")
|
|
2537
|
+
ReportModel = LcpRuby.registry.model_for("showcase_report")
|
|
2538
|
+
|
|
2539
|
+
[
|
|
2540
|
+
{ title: "Q1 Revenue Analysis", description: "Quarterly revenue breakdown by region and product line.", status: "published", priority: "high", category: "financial", tags: "Q1, revenue, quarterly", report_date: Date.current - 30, author_name: "Alice Johnson", confidential: false, page_count: 24 },
|
|
2541
|
+
{ title: "Cloud Migration Technical Review", description: "Architecture assessment for migrating on-premise services to AWS.", status: "approved", priority: "high", category: "technical", tags: "cloud, aws, migration", report_date: Date.current - 14, author_name: "Bob Smith", confidential: false, page_count: 42 },
|
|
2542
|
+
{ title: "Employee Satisfaction Survey 2024", description: "Annual employee engagement and satisfaction survey results.", status: "review", priority: "normal", category: "hr", tags: "survey, engagement, annual", report_date: Date.current - 7, author_name: "Carol Davis", confidential: true, page_count: 18 },
|
|
2543
|
+
{ title: "GDPR Compliance Audit", description: "Data privacy compliance assessment for European operations.", status: "draft", priority: "high", category: "legal", tags: "gdpr, privacy, compliance", report_date: Date.current, author_name: "David Lee", confidential: true, page_count: 0 },
|
|
2544
|
+
{ title: "Product Roadmap H2 2024", description: "Feature planning and prioritization for the second half of the year.", status: "published", priority: "normal", category: "general", tags: "roadmap, planning, H2", report_date: Date.current - 60, author_name: "Eve Martinez", confidential: false, page_count: 15 },
|
|
2545
|
+
{ title: "Security Penetration Test Results", description: "External pen-test findings and remediation recommendations.", status: "approved", priority: "high", category: "technical", tags: "security, pentest, vulnerabilities", report_date: Date.current - 3, author_name: "Frank Wilson", confidential: true, page_count: 31 },
|
|
2546
|
+
{ title: "Budget Forecast FY2025", description: "Projected expenses and revenue for the next fiscal year.", status: "draft", priority: "normal", category: "financial", tags: "budget, forecast, FY2025", report_date: Date.current + 30, author_name: "Grace Chen", confidential: false, page_count: 0 },
|
|
2547
|
+
{ title: "Onboarding Process Improvement", description: "Recommendations for streamlining new employee onboarding.", status: "review", priority: "low", category: "hr", tags: "onboarding, process, improvement", report_date: Date.current - 21, author_name: "Henry Brown", confidential: false, page_count: 12 }
|
|
2548
|
+
].each { |attrs| ReportModel.create!(attrs) }
|
|
2549
|
+
|
|
2550
|
+
puts " Created #{ReportModel.count} showcase report records"
|
|
2551
|
+
end
|
|
2552
|
+
|
|
2553
|
+
if LcpRuby.registry.registered?("showcase_memo")
|
|
2554
|
+
MemoModel = LcpRuby.registry.model_for("showcase_memo")
|
|
2555
|
+
|
|
2556
|
+
[
|
|
2557
|
+
{ title: "Office Closure — Holiday Schedule", description: "Reminder: the office will be closed December 24-26. Remote work is available for urgent matters.", status: "active", priority: "normal", recipient: "All Employees", urgency: "routine", due_date: Date.current + 10 },
|
|
2558
|
+
{ title: "Production Incident: Database Failover", description: "Production DB failed over to replica at 03:22 UTC. Investigating root cause. All services restored.", status: "active", priority: "high", recipient: "Engineering Team", urgency: "immediate", due_date: Date.current, read_at: Time.current - 2.hours },
|
|
2559
|
+
{ title: "Quarterly Planning Kick-off", description: "Please prepare your team's OKRs for Q2 by end of week. Template attached.", status: "active", priority: "normal", recipient: "Department Heads", urgency: "normal", due_date: Date.current + 5 },
|
|
2560
|
+
{ title: "New VPN Configuration Required", description: "IT is rolling out new VPN certificates. Install by Friday to maintain remote access.", status: "active", priority: "normal", recipient: "Remote Workers", urgency: "urgent", due_date: Date.current + 3 },
|
|
2561
|
+
{ title: "Parking Lot Maintenance Notice", description: "Section B of the parking lot will be resurfaced next week. Use Section C temporarily.", status: "archived", priority: "low", recipient: "Building Occupants", urgency: "routine", due_date: Date.current - 7, read_at: Time.current - 5.days },
|
|
2562
|
+
{ title: "Budget Approval Needed — Marketing Campaign", description: "Requesting sign-off on the $50K Q2 digital marketing campaign budget.", status: "draft", priority: "high", recipient: "CFO", urgency: "urgent", due_date: Date.current + 2 },
|
|
2563
|
+
{ title: "Team Lunch — Friday Celebration", description: "Join us for a team lunch to celebrate the successful product launch!", status: "active", priority: "low", recipient: "Product Team", urgency: "routine", due_date: Date.current + 1, read_at: Time.current - 1.hour },
|
|
2564
|
+
{ title: "Security Training Reminder", description: "Annual security awareness training is due by end of month. Complete the online module.", status: "active", priority: "normal", recipient: "All Employees", urgency: "normal", due_date: Date.current + 15 }
|
|
2565
|
+
].each { |attrs| MemoModel.create!(attrs) }
|
|
2566
|
+
|
|
2567
|
+
puts " Created #{MemoModel.count} showcase memo records"
|
|
2568
|
+
end
|
|
2569
|
+
|
|
2570
|
+
# Phase: STI (Single Table Inheritance) Showcase
|
|
2571
|
+
if LcpRuby.registry.registered?("showcase_person")
|
|
2572
|
+
PersonModel = LcpRuby.registry.model_for("showcase_person")
|
|
2573
|
+
|
|
2574
|
+
[
|
|
2575
|
+
{ first_name: "Alice", last_name: "Johnson", display_name: "Alice Johnson", email: "alice@example.com", phone: "+1-555-0101", job_title: "CTO", status: "active", source: "manual", notes: "Key decision maker for technology purchases." },
|
|
2576
|
+
{ first_name: "Bob", last_name: "Smith", display_name: "Bob Smith", email: "bob.smith@acme.com", phone: "+1-555-0102", job_title: "Sales Director", status: "active", source: "referral", notes: "Referred by Alice Johnson." },
|
|
2577
|
+
{ first_name: "Carol", last_name: "Davis", display_name: "Carol Davis", email: "carol.d@startup.io", phone: "+44-20-7946-0958", job_title: "Product Manager", status: "active", source: "import" },
|
|
2578
|
+
{ first_name: "David", last_name: "Lee", display_name: "David Lee", email: "david.lee@corp.com", phone: "+1-555-0104", job_title: "VP Engineering", status: "inactive", source: "api", notes: "Left the company. Contact successor." },
|
|
2579
|
+
{ first_name: "Eve", last_name: "Martinez", display_name: "Eve Martinez", email: "eve@consulting.com", phone: "+1-555-0105", job_title: "Senior Consultant", status: "active", source: "manual", birth_date: Date.new(1985, 3, 15) },
|
|
2580
|
+
{ first_name: "Frank", last_name: "Wilson", display_name: "Frank Wilson", email: "frank.w@techco.com", phone: "+49-30-12345678", job_title: "CEO", status: "active", source: "referral", birth_date: Date.new(1978, 11, 22) }
|
|
2581
|
+
].each { |attrs| PersonModel.create!(attrs) }
|
|
2582
|
+
|
|
2583
|
+
puts " Created #{PersonModel.count} showcase person records"
|
|
2584
|
+
end
|
|
2585
|
+
|
|
2586
|
+
if LcpRuby.registry.registered?("showcase_organization")
|
|
2587
|
+
OrgModel = LcpRuby.registry.model_for("showcase_organization")
|
|
2588
|
+
|
|
2589
|
+
[
|
|
2590
|
+
{ company_name: "Acme Corporation", display_name: "Acme Corporation", email: "info@acme.com", phone: "+1-555-1000", website: "https://acme.com", registration_number: "US-12345678", industry: "technology", status: "active", source: "manual", notes: "Enterprise customer since 2020." },
|
|
2591
|
+
{ company_name: "Global Finance Ltd", display_name: "Global Finance Ltd", email: "contact@globalfinance.com", phone: "+44-20-7946-0100", website: "https://globalfinance.com", registration_number: "UK-87654321", industry: "finance", status: "active", source: "import" },
|
|
2592
|
+
{ company_name: "HealthFirst Medical", display_name: "HealthFirst Medical", email: "partnerships@healthfirst.org", phone: "+1-555-2000", website: "https://healthfirst.org", registration_number: "US-99887766", industry: "healthcare", status: "active", source: "referral", notes: "Interested in our enterprise plan." },
|
|
2593
|
+
{ company_name: "TechStart Inc", display_name: "TechStart Inc", email: "hello@techstart.io", phone: "+1-555-3000", website: "https://techstart.io", registration_number: "US-11223344", industry: "technology", status: "active", source: "api" },
|
|
2594
|
+
{ company_name: "Old Manufacturing Co", display_name: "Old Manufacturing Co", email: "sales@oldmfg.com", phone: "+1-555-4000", industry: "manufacturing", status: "archived", source: "manual", notes: "Acquired by Acme Corporation. No longer active." },
|
|
2595
|
+
{ company_name: "City University", display_name: "City University", email: "it@cityuni.edu", phone: "+1-555-5000", website: "https://cityuni.edu", registration_number: "EDU-55667788", industry: "education", status: "active", source: "manual" }
|
|
2596
|
+
].each { |attrs| OrgModel.create!(attrs) }
|
|
2597
|
+
|
|
2598
|
+
puts " Created #{OrgModel.count} showcase organization records"
|
|
2599
|
+
end
|
|
2600
|
+
|
|
2601
|
+
# STI Feature Catalog entries
|
|
2602
|
+
|
|
2603
|
+
# === Custom Render Blocks showcase data ===
|
|
2604
|
+
if LcpRuby.registry.registered?("showcase_custom_render")
|
|
2605
|
+
CustomRenderModel = LcpRuby.registry.model_for("showcase_custom_render")
|
|
2606
|
+
|
|
2607
|
+
[
|
|
2608
|
+
{ name: "Bratislava Office", description: "Main headquarters in the capital city. Handles all central operations and management.", latitude: 48.1486, longitude: 17.1077, status: "active", priority: "high", tags_json: '["headquarters","slovakia","main-office"]', score: 92, category: "infrastructure", due_date: Date.current + 30, notes: "Renovations planned for Q3." },
|
|
2609
|
+
{ name: "Prague Branch", description: "Czech Republic satellite office focusing on client relationships and regional sales.", latitude: 50.0755, longitude: 14.4378, status: "active", priority: "medium", tags_json: '["branch","czech-republic","sales"]', score: 78, category: "infrastructure", due_date: Date.current + 60 },
|
|
2610
|
+
{ name: "Vienna Research Lab", description: "R&D facility for experimental projects and prototyping.", latitude: 48.2082, longitude: 16.3738, status: "review", priority: "critical", tags_json: '["research","austria","lab","r-and-d"]', score: 65, category: "research", due_date: Date.current - 5, notes: "Budget review overdue. Needs immediate attention." },
|
|
2611
|
+
{ name: "Budapest Hub", description: "Engineering hub for backend infrastructure and platform development.", latitude: 47.4979, longitude: 19.0402, status: "active", priority: "high", tags_json: '["engineering","hungary","platform"]', score: 88, category: "engineering" },
|
|
2612
|
+
{ name: "Warsaw Office", description: "New office opening to support growing Polish market.", latitude: 52.2297, longitude: 21.0122, status: "draft", priority: "medium", tags_json: '["new","poland","expansion"]', score: 30, category: "infrastructure", due_date: Date.current + 120 },
|
|
2613
|
+
{ name: "Berlin Design Studio", description: "Creative team workspace for UX research and product design.", latitude: 52.5200, longitude: 13.4050, status: "active", priority: "low", tags_json: '["design","germany","ux"]', score: 71, category: "design" },
|
|
2614
|
+
{ name: "Remote Team Alpha", description: "Fully remote distributed team working on mobile applications.", status: "active", priority: "medium", tags_json: '["remote","mobile","distributed"]', score: 85, category: "engineering", notes: "No physical location — fully distributed." },
|
|
2615
|
+
{ name: "Legacy System Migration", description: "Project to migrate legacy monolith to microservices architecture.", status: "completed", priority: "critical", tags_json: '["migration","legacy","microservices"]', score: 100, category: "engineering", due_date: Date.current - 30 },
|
|
2616
|
+
{ name: "Annual Report 2025", description: "Preparation of the company-wide annual report with financial summaries.", status: "archived", priority: "low", tags_json: '["report","annual","finance"]', score: 50, category: "management" },
|
|
2617
|
+
{ name: "Security Audit Q1", description: "Quarterly security assessment covering infrastructure and application layers.", status: "review", priority: "high", tags_json: '["security","audit","compliance"]', score: 45, category: "security", due_date: Date.current + 14 }
|
|
2618
|
+
].each { |attrs| CustomRenderModel.create!(attrs) }
|
|
2619
|
+
|
|
2620
|
+
puts " Created #{CustomRenderModel.count} custom render showcase records"
|
|
2621
|
+
end
|
|
2622
|
+
|
|
2623
|
+
# Custom Render Blocks Feature Catalog entries
|
|
2624
|
+
|
|
2625
|
+
# Phase: Feature Catalog — Batch Actions entries
|
|
2626
|
+
|
|
2627
|
+
# === Monitoring & Error Log Showcase Data ===
|
|
2628
|
+
if LcpRuby.registry.registered?("lcp_error_log")
|
|
2629
|
+
ErrorLogModel = LcpRuby.registry.model_for("lcp_error_log")
|
|
2630
|
+
|
|
2631
|
+
now = Time.current
|
|
2632
|
+
[
|
|
2633
|
+
{
|
|
2634
|
+
fingerprint: Digest::SHA256.hexdigest("ActiveRecord::RecordNotFound|app/controllers/lcp_ruby/resources_controller.rb:42")[0..63],
|
|
2635
|
+
error_class: "ActiveRecord::RecordNotFound",
|
|
2636
|
+
message: "Couldn't find LcpRuby::Dynamic::Employee with 'id'=999",
|
|
2637
|
+
subsystem: "controller",
|
|
2638
|
+
stacktrace: [
|
|
2639
|
+
"app/controllers/lcp_ruby/resources_controller.rb:42:in `show'",
|
|
2640
|
+
"actionpack/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'"
|
|
2641
|
+
],
|
|
2642
|
+
context: { url: "/showcase/employees/999", user_id: 1, method: "GET" },
|
|
2643
|
+
occurrence_count: 12,
|
|
2644
|
+
first_occurred_at: now - 7.days,
|
|
2645
|
+
last_occurred_at: now - 2.hours
|
|
2646
|
+
},
|
|
2647
|
+
{
|
|
2648
|
+
fingerprint: Digest::SHA256.hexdigest("NoMethodError|app/renderers/custom_chart.rb:15")[0..63],
|
|
2649
|
+
error_class: "NoMethodError",
|
|
2650
|
+
message: "undefined method `data_points' for nil:NilClass",
|
|
2651
|
+
subsystem: "renderer",
|
|
2652
|
+
stacktrace: [
|
|
2653
|
+
"app/renderers/custom_chart.rb:15:in `render'",
|
|
2654
|
+
"lib/lcp_ruby/display/renderer_registry.rb:38:in `call'"
|
|
2655
|
+
],
|
|
2656
|
+
context: { model: "article", field: "analytics_chart", presenter: "articles" },
|
|
2657
|
+
occurrence_count: 5,
|
|
2658
|
+
first_occurred_at: now - 3.days,
|
|
2659
|
+
last_occurred_at: now - 1.day
|
|
2660
|
+
},
|
|
2661
|
+
{
|
|
2662
|
+
fingerprint: Digest::SHA256.hexdigest("Timeout::Error|lib/lcp_ruby/data_source/rest_json.rb:78")[0..63],
|
|
2663
|
+
error_class: "Timeout::Error",
|
|
2664
|
+
message: "execution expired — GET https://api.example.com/weather/stations",
|
|
2665
|
+
subsystem: "data_source",
|
|
2666
|
+
stacktrace: [
|
|
2667
|
+
"lib/lcp_ruby/data_source/rest_json.rb:78:in `fetch_records'",
|
|
2668
|
+
"lib/lcp_ruby/data_source/resilient_wrapper.rb:22:in `call'"
|
|
2669
|
+
],
|
|
2670
|
+
context: { adapter: "rest_json", endpoint: "https://api.example.com/weather/stations", timeout: 5 },
|
|
2671
|
+
occurrence_count: 47,
|
|
2672
|
+
first_occurred_at: now - 14.days,
|
|
2673
|
+
last_occurred_at: now - 30.minutes
|
|
2674
|
+
},
|
|
2675
|
+
{
|
|
2676
|
+
fingerprint: Digest::SHA256.hexdigest("Pundit::NotAuthorizedError|app/controllers/lcp_ruby/application_controller.rb:55")[0..63],
|
|
2677
|
+
error_class: "Pundit::NotAuthorizedError",
|
|
2678
|
+
message: "not allowed to update? this LcpRuby::Dynamic::ShowcasePermission",
|
|
2679
|
+
subsystem: "authorization",
|
|
2680
|
+
stacktrace: [
|
|
2681
|
+
"app/controllers/lcp_ruby/application_controller.rb:55:in `authorize_action!'",
|
|
2682
|
+
"app/controllers/lcp_ruby/resources_controller.rb:88:in `update'"
|
|
2683
|
+
],
|
|
2684
|
+
context: { user_id: 3, role: "viewer", action: "update", model: "showcase_permission" },
|
|
2685
|
+
occurrence_count: 3,
|
|
2686
|
+
first_occurred_at: now - 1.day,
|
|
2687
|
+
last_occurred_at: now - 6.hours
|
|
2688
|
+
},
|
|
2689
|
+
{
|
|
2690
|
+
fingerprint: Digest::SHA256.hexdigest("ActiveRecord::StatementInvalid|lib/lcp_ruby/virtual_columns/builder.rb:102")[0..63],
|
|
2691
|
+
error_class: "ActiveRecord::StatementInvalid",
|
|
2692
|
+
message: "SQLite3::SQLException: no such column: departments.budget_total",
|
|
2693
|
+
subsystem: "virtual_columns",
|
|
2694
|
+
stacktrace: [
|
|
2695
|
+
"lib/lcp_ruby/virtual_columns/builder.rb:102:in `inject_subquery'",
|
|
2696
|
+
"lib/lcp_ruby/virtual_columns/collector.rb:45:in `apply!'"
|
|
2697
|
+
],
|
|
2698
|
+
context: { model: "department", virtual_column: "budget_total", sql_fragment: "SUM(projects.budget)" },
|
|
2699
|
+
occurrence_count: 1,
|
|
2700
|
+
first_occurred_at: now - 5.hours,
|
|
2701
|
+
last_occurred_at: now - 5.hours
|
|
2702
|
+
},
|
|
2703
|
+
{
|
|
2704
|
+
fingerprint: Digest::SHA256.hexdigest("JSON::ParserError|lib/lcp_ruby/custom_fields/utils.rb:28")[0..63],
|
|
2705
|
+
error_class: "JSON::ParserError",
|
|
2706
|
+
message: "unexpected token at '{invalid json'",
|
|
2707
|
+
subsystem: "custom_fields",
|
|
2708
|
+
stacktrace: [
|
|
2709
|
+
"lib/lcp_ruby/custom_fields/utils.rb:28:in `parse_json_value'",
|
|
2710
|
+
"lib/lcp_ruby/custom_fields/applicator.rb:65:in `apply_accessors!'"
|
|
2711
|
+
],
|
|
2712
|
+
context: { model: "employee", field: "metadata", raw_value: "{invalid json" },
|
|
2713
|
+
occurrence_count: 8,
|
|
2714
|
+
first_occurred_at: now - 10.days,
|
|
2715
|
+
last_occurred_at: now - 4.hours
|
|
2716
|
+
},
|
|
2717
|
+
{
|
|
2718
|
+
fingerprint: Digest::SHA256.hexdigest("ActionController::RoutingError|middleware/routing.rb:12")[0..63],
|
|
2719
|
+
error_class: "ActionController::RoutingError",
|
|
2720
|
+
message: "No route matches [GET] '/showcase/nonexistent-page'",
|
|
2721
|
+
subsystem: "routing",
|
|
2722
|
+
stacktrace: [
|
|
2723
|
+
"actionpack/lib/action_dispatch/middleware/debug_exceptions.rb:65",
|
|
2724
|
+
"actionpack/lib/action_dispatch/middleware/show_exceptions.rb:33"
|
|
2725
|
+
],
|
|
2726
|
+
context: { url: "/showcase/nonexistent-page", method: "GET" },
|
|
2727
|
+
occurrence_count: 22,
|
|
2728
|
+
first_occurred_at: now - 30.days,
|
|
2729
|
+
last_occurred_at: now - 1.hour
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
fingerprint: Digest::SHA256.hexdigest("Net::ReadTimeout|lib/lcp_ruby/background_jobs/executor_job.rb:34")[0..63],
|
|
2733
|
+
error_class: "Net::ReadTimeout",
|
|
2734
|
+
message: "Net::ReadTimeout with #<TCPSocket:(closed)>",
|
|
2735
|
+
subsystem: "background_jobs",
|
|
2736
|
+
stacktrace: [
|
|
2737
|
+
"lib/lcp_ruby/background_jobs/executor_job.rb:34:in `perform'",
|
|
2738
|
+
"activejob/lib/active_job/execution.rb:53:in `perform_now'"
|
|
2739
|
+
],
|
|
2740
|
+
context: { job_name: "sync_weather_data", attempt: 3, max_retries: 5 },
|
|
2741
|
+
occurrence_count: 15,
|
|
2742
|
+
first_occurred_at: now - 5.days,
|
|
2743
|
+
last_occurred_at: now - 3.hours
|
|
2744
|
+
}
|
|
2745
|
+
].each { |attrs| ErrorLogModel.create!(attrs) }
|
|
2746
|
+
|
|
2747
|
+
puts " Created #{ErrorLogModel.count} error log entries (monitoring showcase)"
|
|
2748
|
+
end
|
|
2749
|
+
|
|
2750
|
+
# Monitoring Feature Catalog entries
|
|
2751
|
+
|
|
2752
|
+
# Phase: Export Features
|
|
2753
|
+
|
|
2754
|
+
# Phase: Import Features
|
|
2755
|
+
|
|
2756
|
+
# === Form Submit Actions showcase data ===
|
|
2757
|
+
|
|
2758
|
+
if LcpRuby.registry.registered?("showcase_form_action")
|
|
2759
|
+
FormActionModel = LcpRuby.registry.model_for("showcase_form_action")
|
|
2760
|
+
form_action_records = [
|
|
2761
|
+
{ title: "Fix login page CSS", description: "The login button is misaligned on mobile Safari.", status: "draft", priority: "high", category: "bug" },
|
|
2762
|
+
{ title: "Add dark mode support", description: "Implement system-preference-aware dark mode toggle.", status: "draft", priority: "normal", category: "feature" },
|
|
2763
|
+
{ title: "Update API docs", description: "Document the new /v2/users endpoint.", status: "submitted", priority: "low", category: "docs" },
|
|
2764
|
+
{ title: "Refactor auth middleware", description: "Extract token validation into a shared concern.", status: "approved", priority: "normal", category: "chore" },
|
|
2765
|
+
{ title: "Write onboarding guide", description: "Step-by-step guide for new developers.", status: "published", priority: "normal", category: "docs", published_at: 2.days.ago },
|
|
2766
|
+
{ title: "Fix N+1 in dashboard", description: "Add eager loading for the stats widget queries.", status: "draft", priority: "urgent", category: "bug" },
|
|
2767
|
+
{ title: "Add CSV export", description: "Allow users to export filtered results as CSV.", status: "submitted", priority: "high", category: "feature" },
|
|
2768
|
+
{ title: "Clean up dead code", description: "Remove unused helper methods from ApplicationHelper.", status: "rejected", priority: "low", category: "chore" }
|
|
2769
|
+
]
|
|
2770
|
+
form_action_records.each do |attrs|
|
|
2771
|
+
target_status = attrs.delete(:status)
|
|
2772
|
+
record = FormActionModel.create!(attrs.merge(status: "draft"))
|
|
2773
|
+
# Bypass workflow guard to set non-draft status for seed data
|
|
2774
|
+
FormActionModel.where(id: record.id).update_all(status: target_status) if target_status != "draft"
|
|
2775
|
+
end
|
|
2776
|
+
puts " Added #{form_action_records.size} Form Action Task demo records"
|
|
2777
|
+
end
|
|
2778
|
+
|
|
2779
|
+
# Phase: Form Submit Actions Feature Catalog
|
|
2780
|
+
|
|
2781
|
+
|
|
2782
|
+
|
|
2783
|
+
# Sample inventory data for the bind_to / host_controller demo presenters
|
|
2784
|
+
if defined?(HostInventoryItem)
|
|
2785
|
+
HostInventoryItem.delete_all
|
|
2786
|
+
inventory_seeds = [
|
|
2787
|
+
{ name: "Wireless Headphones", sku: "WH-001", description: "Noise cancelling bluetooth headphones.", status: "active", color: "black", price_cents: 12_999, quantity: 42 },
|
|
2788
|
+
{ name: "USB-C Hub", sku: "HUB-7", description: "7-in-1 hub with HDMI, SD, and USB-A.", status: "active", color: "gray", price_cents: 4_500, quantity: 17 },
|
|
2789
|
+
{ name: "Mechanical Keyboard", sku: "KB-MX", description: "Hot-swappable switches, RGB.", status: "active", color: "white", price_cents: 15_900, quantity: 8 },
|
|
2790
|
+
{ name: "Office Chair (Mesh)", sku: "CH-MS", description: "Ergonomic office chair, lumbar support.", status: "active", color: "black", price_cents: 32_900, quantity: 3 },
|
|
2791
|
+
{ name: "Laptop Stand", sku: "LS-V1", description: "Aluminum laptop stand, foldable.", status: "active", color: "silver", price_cents: 5_900, quantity: 0 },
|
|
2792
|
+
{ name: "Pre-launch Widget", sku: "PRE-1", description: "Coming next quarter.", status: "draft", color: "blue", price_cents: 0, quantity: 0 },
|
|
2793
|
+
{ name: "Old Smartphone Case", sku: "OLD-PH", description: "End of life — do not reorder.", status: "discontinued", color: "red", price_cents: 1_500, quantity: 12 }
|
|
2794
|
+
]
|
|
2795
|
+
inventory_seeds.each_with_index do |attrs, i|
|
|
2796
|
+
HostInventoryItem.create!(attrs.merge(position: i + 1))
|
|
2797
|
+
end
|
|
2798
|
+
puts " Created #{HostInventoryItem.count} HostInventoryItem records"
|
|
2799
|
+
end
|
|
2800
|
+
|
|
2801
|
+
# Sample platform_profile data for the lcp_managed: demo
|
|
2802
|
+
if defined?(PlatformProfile)
|
|
2803
|
+
PlatformProfile.delete_all
|
|
2804
|
+
|
|
2805
|
+
# Pick a category target (LCP dynamic model) for the managed FK rows
|
|
2806
|
+
category_class = LcpRuby.registry.model_for("category") if LcpRuby.registry.registered?("category")
|
|
2807
|
+
cat = category_class&.first
|
|
2808
|
+
|
|
2809
|
+
profile_seeds = [
|
|
2810
|
+
{ name: "Alice Anderson", bio: "Backend engineer who likes coffee.", favorite_color: "blue" },
|
|
2811
|
+
{ name: "Bob Bauer", bio: "Frontend dev, Storybook enthusiast.", favorite_color: "green" },
|
|
2812
|
+
{ name: "Cynthia Chen", bio: "Designer with a degree in topology.", favorite_color: "magenta" },
|
|
2813
|
+
{ name: "Dimitri Dvorak", bio: "Data scientist who lives in spreadsheets.", favorite_color: "yellow" }
|
|
2814
|
+
]
|
|
2815
|
+
profile_seeds.each do |attrs|
|
|
2816
|
+
PlatformProfile.create!(attrs.merge(category_id: cat&.id))
|
|
2817
|
+
end
|
|
2818
|
+
puts " Created #{PlatformProfile.count} PlatformProfile records (lcp_managed: demo)"
|
|
2819
|
+
end
|
|
2820
|
+
|
|
2821
|
+
# Phase: Association primary_key Showcase
|
|
2822
|
+
#
|
|
2823
|
+
# Demonstrates joining on a business key (vema_code) instead of the
|
|
2824
|
+
# default surrogate id. The chain is:
|
|
2825
|
+
#
|
|
2826
|
+
# showcase_hr_employee.division_code → showcase_division.vema_code
|
|
2827
|
+
# showcase_division.bu_code → showcase_business_unit.vema_code
|
|
2828
|
+
#
|
|
2829
|
+
# Plus a has_one :through over the custom-key belongs_to so
|
|
2830
|
+
# `employee.showcase_business_unit` resolves transitively without any
|
|
2831
|
+
# extra plumbing on the through-side declaration.
|
|
2832
|
+
if LcpRuby.registry.registered?("showcase_business_unit")
|
|
2833
|
+
BUModel = LcpRuby.registry.model_for("showcase_business_unit")
|
|
2834
|
+
DivisionModel = LcpRuby.registry.model_for("showcase_division")
|
|
2835
|
+
HrEmpModel = LcpRuby.registry.model_for("showcase_hr_employee")
|
|
2836
|
+
|
|
2837
|
+
business_units = [
|
|
2838
|
+
{ vema_code: "HQ01", name: "Headquarters", description: "Central administration and corporate functions." },
|
|
2839
|
+
{ vema_code: "TEC01", name: "Technology", description: "Engineering, product, IT operations." },
|
|
2840
|
+
{ vema_code: "OPS01", name: "Operations", description: "Manufacturing, logistics, supply chain." },
|
|
2841
|
+
{ vema_code: "SAL01", name: "Sales & Marketing", description: "Customer-facing revenue functions." }
|
|
2842
|
+
]
|
|
2843
|
+
business_units.each { |attrs| BUModel.create!(attrs) }
|
|
2844
|
+
|
|
2845
|
+
divisions = [
|
|
2846
|
+
# bu_code stores the business unit's vema_code (not its id) — the
|
|
2847
|
+
# FK column was auto-created as string because the association
|
|
2848
|
+
# declares primary_key: :vema_code.
|
|
2849
|
+
{ vema_code: "D-FIN", name: "Finance", bu_code: "HQ01" },
|
|
2850
|
+
{ vema_code: "D-LEG", name: "Legal & Compliance", bu_code: "HQ01" },
|
|
2851
|
+
{ vema_code: "D-ENG", name: "Engineering", bu_code: "TEC01" },
|
|
2852
|
+
{ vema_code: "D-PRD", name: "Product", bu_code: "TEC01" },
|
|
2853
|
+
{ vema_code: "D-IT", name: "IT Operations", bu_code: "TEC01" },
|
|
2854
|
+
{ vema_code: "D-MFG", name: "Manufacturing", bu_code: "OPS01" },
|
|
2855
|
+
{ vema_code: "D-LOG", name: "Logistics", bu_code: "OPS01" },
|
|
2856
|
+
{ vema_code: "D-SAL", name: "Sales", bu_code: "SAL01" },
|
|
2857
|
+
{ vema_code: "D-MKT", name: "Marketing", bu_code: "SAL01" }
|
|
2858
|
+
]
|
|
2859
|
+
divisions.each { |attrs| DivisionModel.create!(attrs) }
|
|
2860
|
+
|
|
2861
|
+
# Lookup BU code per division — used to populate the denormalized
|
|
2862
|
+
# bu_code on each employee so the dashboard's BU filter is a single
|
|
2863
|
+
# WHERE on showcase_hr_employees instead of a JOIN.
|
|
2864
|
+
division_bu = divisions.to_h { |d| [ d[:vema_code], d[:bu_code] ] }
|
|
2865
|
+
|
|
2866
|
+
# ── Hand-curated leadership roster ──────────────────────────────
|
|
2867
|
+
# Kept so the existing association-primary-key feature card and the
|
|
2868
|
+
# presenter index still show familiar named records. All twelve are
|
|
2869
|
+
# currently active (no terminated_at); they form the dashboard's
|
|
2870
|
+
# "always present" management cohort.
|
|
2871
|
+
curated = [
|
|
2872
|
+
{ name: "Alice Johnson", email: "alice.j@example.com", job_title: "CFO", division_code: "D-FIN", is_management: true, hired_at: Date.new(2019, 3, 14) },
|
|
2873
|
+
{ name: "Bob Smith", email: "bob.s@example.com", job_title: "Controller", division_code: "D-FIN", is_management: false, hired_at: Date.new(2020, 7, 1) },
|
|
2874
|
+
{ name: "Carol Davis", email: "carol.d@example.com", job_title: "General Counsel", division_code: "D-LEG", is_management: true, hired_at: Date.new(2018, 9, 2) },
|
|
2875
|
+
{ name: "David Lee", email: "david.l@example.com", job_title: "VP Engineering", division_code: "D-ENG", is_management: true, hired_at: Date.new(2017, 1, 15) },
|
|
2876
|
+
{ name: "Eve Martinez", email: "eve.m@example.com", job_title: "Senior Engineer", division_code: "D-ENG", is_management: false, hired_at: Date.new(2021, 4, 12) },
|
|
2877
|
+
{ name: "Frank Wilson", email: "frank.w@example.com", job_title: "Engineering Lead", division_code: "D-ENG", status: "on_leave", is_management: true, hired_at: Date.new(2020, 11, 30) },
|
|
2878
|
+
{ name: "Grace Chen", email: "grace.c@example.com", job_title: "Product Manager", division_code: "D-PRD", is_management: true, hired_at: Date.new(2022, 2, 7) },
|
|
2879
|
+
{ name: "Henry Brown", email: "henry.b@example.com", job_title: "Head of IT", division_code: "D-IT", is_management: true, hired_at: Date.new(2019, 6, 19) },
|
|
2880
|
+
{ name: "Iris Patel", email: "iris.p@example.com", job_title: "Plant Manager", division_code: "D-MFG", is_management: true, hired_at: Date.new(2018, 10, 5) },
|
|
2881
|
+
{ name: "Jacob Romero", email: "jacob.r@example.com", job_title: "Logistics Lead", division_code: "D-LOG", is_management: true, hired_at: Date.new(2021, 8, 23) },
|
|
2882
|
+
{ name: "Karen White", email: "karen.w@example.com", job_title: "Account Executive", division_code: "D-SAL", is_management: false, hired_at: Date.new(2022, 5, 11) },
|
|
2883
|
+
{ name: "Liam Garcia", email: "liam.g@example.com", job_title: "Marketing Director", division_code: "D-MKT", is_management: true, hired_at: Date.new(2020, 1, 8) }
|
|
2884
|
+
]
|
|
2885
|
+
curated.each do |attrs|
|
|
2886
|
+
HrEmpModel.create!(attrs.merge(bu_code: division_bu.fetch(attrs[:division_code])))
|
|
2887
|
+
end
|
|
2888
|
+
|
|
2889
|
+
# ── Synthetic cohort that drives the turnover dashboard ─────────
|
|
2890
|
+
#
|
|
2891
|
+
# ~200 employees seeded across BUs/divisions with hires + terminations
|
|
2892
|
+
# spread across the last three years (anchored on Date.today). The
|
|
2893
|
+
# distributions are tilted so each chart on hr_turnover_dashboard
|
|
2894
|
+
# has something to show: visible trend per month, a BU/Division
|
|
2895
|
+
# ranking, and a non-trivial mix of termination reasons.
|
|
2896
|
+
#
|
|
2897
|
+
# Knobs to tweak:
|
|
2898
|
+
# ANNUAL_TURNOVER_RATE — fraction of cohort that turns over per year
|
|
2899
|
+
# DIVISION_WEIGHTS — relative size of each division
|
|
2900
|
+
# REASON_WEIGHTS — mix of voluntary/involuntary/etc.
|
|
2901
|
+
rng = Random.new(20260517) # deterministic seed for reproducible demo
|
|
2902
|
+
|
|
2903
|
+
# Weights chosen so each reason has ~weekly density over the 3-year
|
|
2904
|
+
# window — keeps the stacked-area "Voluntary vs involuntary over
|
|
2905
|
+
# time" chart continuous instead of pointillistic. Real HR turnover
|
|
2906
|
+
# is usually voluntary-heavy, but the showcase prioritises chart
|
|
2907
|
+
# readability over realism.
|
|
2908
|
+
#
|
|
2909
|
+
# Locals (not top-level constants) so re-running `rails db:seed`
|
|
2910
|
+
# doesn't trigger `already initialized constant` warnings.
|
|
2911
|
+
annual_turnover_rate = 0.28
|
|
2912
|
+
reason_weights = {
|
|
2913
|
+
"voluntary" => 45,
|
|
2914
|
+
"involuntary" => 35,
|
|
2915
|
+
"retired" => 10,
|
|
2916
|
+
"transfer" => 10
|
|
2917
|
+
}.freeze
|
|
2918
|
+
|
|
2919
|
+
# Larger divisions get more synthetic employees — keeps the per-BU
|
|
2920
|
+
# chart from being a flat row of equal bars.
|
|
2921
|
+
division_weights = {
|
|
2922
|
+
"D-FIN" => 12, "D-LEG" => 6,
|
|
2923
|
+
"D-ENG" => 30, "D-PRD" => 14, "D-IT" => 12,
|
|
2924
|
+
"D-MFG" => 35, "D-LOG" => 18,
|
|
2925
|
+
"D-SAL" => 25, "D-MKT" => 18
|
|
2926
|
+
}.freeze
|
|
2927
|
+
|
|
2928
|
+
first_names = %w[Adam Beth Carl Dana Eric Fiona Greg Hana Ivan Jana Karel Lara Marek Nina Oleg Petra
|
|
2929
|
+
Quinn Roman Sara Tomas Ula Viktor Wenda Xena Yuri Zora Alex Brigit Cyril Diana
|
|
2930
|
+
Emil Filip Greta Hugo Ines Jonas Klara Linda Olga Pavel Renata Samuel Tereza
|
|
2931
|
+
Vaclav Walter Yvona Zlata Adela Bohumil Daniela Egon Frantisek Gerda Helena
|
|
2932
|
+
Igor Jolana Kamil Ludek Marcel Nora Otakar Pavla].freeze
|
|
2933
|
+
last_names = %w[Novak Svoboda Dvorak Cerny Prochazka Kucera Vesely Horak Krejci Marek Pokorny
|
|
2934
|
+
Pospisil Hajek Jelinek Polak Soukup Konecny Sedlacek Dolezal Stastny Holub Cermak
|
|
2935
|
+
Janda Stepan Vlcek Kratochvil Beran Hejna Hruska Pavlik Zeman Tomanek Houska Bartos
|
|
2936
|
+
Smid Hruby Vacek Stejskal Pesek Maly Kratky].freeze
|
|
2937
|
+
job_pool = {
|
|
2938
|
+
management: [ "Director", "Head of Department", "Senior Manager", "Team Lead", "Principal" ],
|
|
2939
|
+
individual: [ "Specialist", "Senior Specialist", "Analyst", "Associate", "Engineer", "Coordinator", "Consultant" ]
|
|
2940
|
+
}.freeze
|
|
2941
|
+
|
|
2942
|
+
weighted_pick = lambda do |weights|
|
|
2943
|
+
total = weights.values.sum
|
|
2944
|
+
pick = rng.rand(total)
|
|
2945
|
+
cumulative = 0
|
|
2946
|
+
weights.each do |key, weight|
|
|
2947
|
+
cumulative += weight
|
|
2948
|
+
return key if pick < cumulative
|
|
2949
|
+
end
|
|
2950
|
+
weights.keys.last
|
|
2951
|
+
end
|
|
2952
|
+
|
|
2953
|
+
today = Date.today
|
|
2954
|
+
hires_window_start = today - (365 * 3) # three full years of history
|
|
2955
|
+
email_seq = 0
|
|
2956
|
+
|
|
2957
|
+
division_weights.each do |division_code, count|
|
|
2958
|
+
bu_code = division_bu.fetch(division_code)
|
|
2959
|
+
count.times do
|
|
2960
|
+
first = first_names.sample(random: rng)
|
|
2961
|
+
last = last_names.sample(random: rng)
|
|
2962
|
+
email_seq += 1
|
|
2963
|
+
is_mgmt = rng.rand < 0.18 # ~18% management
|
|
2964
|
+
job_title = (is_mgmt ? job_pool[:management] : job_pool[:individual]).sample(random: rng)
|
|
2965
|
+
|
|
2966
|
+
hired_offset = rng.rand((today - hires_window_start).to_i)
|
|
2967
|
+
hired_at = hires_window_start + hired_offset
|
|
2968
|
+
|
|
2969
|
+
# Probability of having left within the (today - hired_at) window
|
|
2970
|
+
# using a simple geometric approximation of annual_turnover_rate.
|
|
2971
|
+
months_employed = ((today - hired_at).to_f / 30.4).clamp(1, 36)
|
|
2972
|
+
term_prob = 1 - (1 - annual_turnover_rate) ** (months_employed / 12.0)
|
|
2973
|
+
terminated_at = nil
|
|
2974
|
+
termination_reason = nil
|
|
2975
|
+
status = "active"
|
|
2976
|
+
if rng.rand < term_prob
|
|
2977
|
+
terminated_at = hired_at + rng.rand((today - hired_at).to_i)
|
|
2978
|
+
termination_reason = weighted_pick.call(reason_weights)
|
|
2979
|
+
status = "terminated"
|
|
2980
|
+
elsif rng.rand < 0.04
|
|
2981
|
+
status = "on_leave"
|
|
2982
|
+
end
|
|
2983
|
+
|
|
2984
|
+
HrEmpModel.create!(
|
|
2985
|
+
name: "#{first} #{last}",
|
|
2986
|
+
email: "#{first.downcase}.#{last.downcase}.#{email_seq}@example.com",
|
|
2987
|
+
job_title: job_title,
|
|
2988
|
+
status: status,
|
|
2989
|
+
division_code: division_code,
|
|
2990
|
+
bu_code: bu_code,
|
|
2991
|
+
is_management: is_mgmt,
|
|
2992
|
+
hired_at: hired_at,
|
|
2993
|
+
terminated_at: terminated_at,
|
|
2994
|
+
termination_reason: termination_reason
|
|
2995
|
+
)
|
|
2996
|
+
end
|
|
2997
|
+
end
|
|
2998
|
+
|
|
2999
|
+
puts " Created #{BUModel.count} business units, #{DivisionModel.count} divisions, " \
|
|
3000
|
+
"#{HrEmpModel.count} HR employees " \
|
|
3001
|
+
"(#{HrEmpModel.where.not(terminated_at: nil).count} with terminations) " \
|
|
3002
|
+
"— powers hr_turnover_dashboard"
|
|
3003
|
+
end
|
|
3004
|
+
|
|
3005
|
+
# Feature catalog entries — Association primary_key
|
|
3006
|
+
|
|
3007
|
+
# Phase: Record Aliases & Slug Fields Showcase
|
|
3008
|
+
if LcpRuby.registry.registered?("showcase_announcement")
|
|
3009
|
+
AnnouncementModel = LcpRuby.registry.model_for("showcase_announcement")
|
|
3010
|
+
|
|
3011
|
+
[
|
|
3012
|
+
{ title: "Platform v2.0 Released", body: "We are excited to announce the release of Platform v2.0 with record aliases, slug fields, and many more improvements.", priority: "high", published_at: 3.days.ago },
|
|
3013
|
+
{ title: "Scheduled Maintenance Window", body: "The platform will undergo scheduled maintenance this Saturday from 2:00 AM to 6:00 AM UTC. Please save your work.", priority: "urgent", published_at: 1.day.ago },
|
|
3014
|
+
{ title: "New Team Members", body: "Please welcome our three new engineers joining the platform team this month.", priority: "normal", published_at: 5.days.ago },
|
|
3015
|
+
{ title: "Q1 All-Hands Meeting", body: "Join us for the quarterly all-hands meeting next Friday at 3 PM. We will cover roadmap updates and team achievements.", priority: "normal", published_at: 2.weeks.ago },
|
|
3016
|
+
{ title: "Security Policy Update", body: "Our security policy has been updated to require MFA for all accounts. Please enable it by end of month.", priority: "high", published_at: 6.hours.ago }
|
|
3017
|
+
].each { |attrs| AnnouncementModel.create!(attrs) }
|
|
3018
|
+
|
|
3019
|
+
puts " Created #{AnnouncementModel.count} announcement records (record_aliases showcase)"
|
|
3020
|
+
end
|
|
3021
|
+
|
|
3022
|
+
# API Tokens — seed one active and one revoked token for the admin user.
|
|
3023
|
+
# The plaintext is generated by the model's before_validation callback and
|
|
3024
|
+
# printed here for the demo curl flow. On reseed the plaintext changes;
|
|
3025
|
+
# fetch it again from `rails console` (the hash overwrites in DB).
|
|
3026
|
+
if LcpRuby.registry.registered?("api_token")
|
|
3027
|
+
TokenModel = LcpRuby.registry.model_for("api_token")
|
|
3028
|
+
TokenModel.delete_all
|
|
3029
|
+
|
|
3030
|
+
admin = if LcpRuby.configuration.authentication == :built_in
|
|
3031
|
+
LcpRuby::User.find_by(email: "admin@example.com")
|
|
3032
|
+
end
|
|
3033
|
+
|
|
3034
|
+
if admin
|
|
3035
|
+
active = TokenModel.create!(name: "Demo curl token", user_id: admin.id)
|
|
3036
|
+
puts " Created active API token — plaintext: #{active.plaintext_token}"
|
|
3037
|
+
|
|
3038
|
+
revoked = TokenModel.create!(name: "Old laptop (revoked)", user_id: admin.id)
|
|
3039
|
+
revoked.update!(revoked_at: Time.current)
|
|
3040
|
+
puts " Created revoked API token (#{revoked.token_prefix})"
|
|
3041
|
+
else
|
|
3042
|
+
puts " Skipped api_token seeds — admin@example.com user not found."
|
|
3043
|
+
end
|
|
3044
|
+
end
|
|
3045
|
+
|
|
3046
|
+
# ── Type-system defaults demo ────────────────────────────────────────────
|
|
3047
|
+
# Seeds for the showcase_type_default model (config/lcp_ruby/models/
|
|
3048
|
+
# showcase_type_default.rb). Demonstrates Phases 1-4b of
|
|
3049
|
+
# docs/design/type_system_defaults.md visibly: bare boolean/enum columns
|
|
3050
|
+
# auto-renderer via runtime_type_renderers, type-name synonym for renderer:,
|
|
3051
|
+
# display_in_index opt-in, and custom :rating type with multi-rule
|
|
3052
|
+
# null_false_validation.
|
|
3053
|
+
if defined?(LcpRuby::Dynamic::ShowcaseTypeDefault)
|
|
3054
|
+
TypeDefaultModel = LcpRuby::Dynamic::ShowcaseTypeDefault
|
|
3055
|
+
TypeDefaultModel.delete_all
|
|
3056
|
+
[
|
|
3057
|
+
{ title: "Phase 4 enrichment baseline", active: true, stage: "draft", score: 4.5,
|
|
3058
|
+
short_summary: "Baseline scenario showing all four enrichment paths.",
|
|
3059
|
+
body: ("This is a long body field demonstrating row-unfriendly :text type behavior. " * 5),
|
|
3060
|
+
notes: "Notes are :text but only shown in form/show, not index." },
|
|
3061
|
+
{ title: "C7 type-name synonym demo", active: true, stage: "review", score: 5.0,
|
|
3062
|
+
short_summary: "Stage column declared with renderer: :enum (a type name) — runtime resolves to badge.",
|
|
3063
|
+
body: "Phase 4 added a runtime fallback in Display::RendererRegistry.renderer_for that consults Types::TypeRegistry for type-name keys.",
|
|
3064
|
+
notes: "Decision 4 revised in docs/design/type_system_defaults.md." },
|
|
3065
|
+
{ title: "Boolean+presence anti-pattern (avoided)", active: false, stage: "published", score: 3.0,
|
|
3066
|
+
short_summary: "active is :boolean null:false WITHOUT explicit validates :presence — Phase 4b auto-applies inclusion.",
|
|
3067
|
+
body: "If a configurator wrote validates :presence on :boolean, ConfigurationValidator rejects per Decision 15.",
|
|
3068
|
+
notes: "" },
|
|
3069
|
+
{ title: "Custom :rating type — multi-rule null_false_validation", active: true, stage: "published", score: 4.0,
|
|
3070
|
+
short_summary: "score is :rating, declared null:false. Type definition carries presence AND numericality 1..5.",
|
|
3071
|
+
body: "Decision 12: null_false_validation is an array, not a single hash — composes multiple rules per type.",
|
|
3072
|
+
notes: "Try editing this record and setting score to 0 or 6 — Rails-shaped error." },
|
|
3073
|
+
{ title: "display_in_index opt-in", active: true, stage: "draft", score: 3.5,
|
|
3074
|
+
short_summary: "Body column uses display_in_index: true (validator-silencer; runtime renders raw text).",
|
|
3075
|
+
body: "Sometimes you want long text in the row anyway. The opt-in says: I know this is row-unfriendly, render it as is.",
|
|
3076
|
+
notes: "" },
|
|
3077
|
+
{ title: "Stale archived scenario", active: false, stage: "archived", score: 2.0,
|
|
3078
|
+
short_summary: "Old example kept for completeness — boolean shows as red ✗ via boolean_icon enrichment.",
|
|
3079
|
+
body: "Phase 4 enrichment is gated behind LcpRuby.configuration.runtime_type_renderers — showcase has it on.",
|
|
3080
|
+
notes: "" }
|
|
3081
|
+
].each { |attrs| TypeDefaultModel.create!(attrs) }
|
|
3082
|
+
puts "Created #{TypeDefaultModel.count} ShowcaseTypeDefault demo records (Phase 1-4b type-system defaults)"
|
|
3083
|
+
end
|
|
3084
|
+
|
|
3085
|
+
puts "Seeding complete!"
|