diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..def4840 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,59 @@ +# Git +.git +.gitignore +README.md + +# Logs +log/* +tmp/* +*.log + +# Runtime data +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# node_modules +node_modules + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Editor directories and files +.vscode +.idea + +# Docker +Dockerfile +docker-compose*.yml +.dockerignore + +# Rails specific +public/assets +public/packs +public/packs-test +storage/* +.byebug_history + +# Bundle config +.bundle + +# Environment variables +.env +.env.local +.env.*.local + +# Rails cache +tmp/cache \ No newline at end of file diff --git a/.env b/.env new file mode 100644 index 0000000..e2fe2af --- /dev/null +++ b/.env @@ -0,0 +1,31 @@ +# Configurações do Banco de Dados PostgreSQL +POSTGRES_DB=northwind +POSTGRES_USER=admin_panel_user +POSTGRES_PASSWORD=admin_panel_password +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 + +# URL completa do banco (alternativa) +DATABASE_URL=postgresql://admin_panel_user:admin_panel_password@postgres:5432/northwind + +# Configurações do Rails +RAILS_ENV=development +RAILS_SERVE_STATIC_FILES=true +RAILS_LOG_TO_STDOUT=true + +# Configurações do pgAdmin +PGADMIN_DEFAULT_EMAIL=admin@admin.com +PGADMIN_DEFAULT_PASSWORD=admin_panel_password + +# Portas do host (para mapeamento Docker) +RAILS_PORT=3030 +POSTGRES_EXTERNAL_PORT=3031 +PGADMIN_EXTERNAL_PORT=3032 + +# Configurações do Forest Admin +FOREST_ENV_SECRET=development_secret_key_change_in_production +FOREST_AUTH_SECRET=development_auth_key_change_in_production +DATABASE_REJECT_UNAUTHORIZED=false + +# Chave secreta do Rails +SECRET_KEY_BASE=development_secret_key_base_at_least_30_characters_long_for_security \ No newline at end of file diff --git a/.forestadmin-schema.json b/.forestadmin-schema.json index 3c6b16f..27d6e15 100644 --- a/.forestadmin-schema.json +++ b/.forestadmin-schema.json @@ -3455,6 +3455,4379 @@ }], "segments": [], "actions": [] + }, { + "name": "SpinaAccount", + "name_old": "__spina_accounts", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "address", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "city", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "email", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "json_attributes", + "type": "Json", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "phone", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "postal_code", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "preferences", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "robots_allowed", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaAttachment", + "name_old": "__spina_attachments", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "file", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaAttachmentCollection", + "name_old": "spina_attachment_collections", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaAttachmentCollectionsAttachment", + "name_old": "spina_attachment_collections_attachments", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "attachment_collection_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "attachment_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaImage", + "name_old": "__spina_images", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "media_folder_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaImageCollection", + "name_old": "spina_image_collections", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaImageCollectionsImage", + "name_old": "spina_image_collections_images", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "image_collection_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "image_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaLayoutPart", + "name_old": "spina_layout_parts", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "account_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "layout_partable_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "layout_partable_type", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaLine", + "name_old": "spina_lines", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaLineTranslation", + "name_old": "spina_line_translations", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "content", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "locale", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "spina_line_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaMediaFolder", + "name_old": "__spina_media_folders", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaNavigation", + "name_old": "__spina_navigations", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "auto_add_pages", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "label", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": "0", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaNavigationItem", + "name_old": "__spina_navigation_items", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "ancestry", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "navigation_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "page_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": "0", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaOption", + "name_old": "spina_options", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "value", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaPage", + "name_old": "__spina_pages", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "active", + "type": "Boolean", + "default_value": "true", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "ancestry", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "ancestry_children_count", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "ancestry_depth", + "type": "Number", + "default_value": "0", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "deletable", + "type": "Boolean", + "default_value": "true", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "draft", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "json_attributes", + "type": "Json", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "layout_template", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "link_url", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "resource_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "show_in_menu", + "type": "Boolean", + "default_value": "true", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "skip_to_first_child", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "slug", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "view_template", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaPagePart", + "name_old": "spina_page_parts", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "page_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "page_partable_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "page_partable_type", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaPageTranslation", + "name_old": "spina_page_translations", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "description", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "locale", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "materialized_path", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "menu_title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "seo_title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "spina_page_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "url_title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaResource", + "name_old": "__spina_resources", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "label", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "order_by", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "slug", + "type": "Json", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "view_template", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaRewriteRule", + "name_old": "__spina_rewrite_rules", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "new_path", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "old_path", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaSetting", + "name_old": "__spina_settings", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "plugin", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "preferences", + "type": "Json", + "default_value": "{}", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaStructure", + "name_old": "spina_structures", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaStructureItem", + "name_old": "spina_structure_items", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "structure_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaStructurePart", + "name_old": "spina_structure_parts", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "structure_item_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "structure_partable_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "structure_partable_type", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "title", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaText", + "name_old": "spina_texts", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaTextTranslation", + "name_old": "spina_text_translations", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "content", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "locale", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "spina_text_id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "SpinaUser", + "name_old": "__spina_users", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "admin", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "email", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "last_logged_in", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "password_digest", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "password_reset_sent_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "password_reset_token", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Account", + "name_old": "spina__spina_accounts", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "address", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "city", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "email", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "json_attributes", + "type": "Json", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }] + }, { + "field": "phone", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "postal_code", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "preferences", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "robots_allowed", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Attachment", + "name_old": "spina__spina_attachments", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "file", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "file_attachment", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "ActiveStorage__Attachment.id", + "inverse_of": "record", + "relationship": "HasOne", + "widget": null, + "validations": [] + }, { + "field": "file_blob", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "ActiveStorage__Blob.id", + "inverse_of": "attachments", + "relationship": "HasOne", + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Image", + "name_old": "spina__spina_images", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "file_attachment", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "ActiveStorage__Attachment.id", + "inverse_of": "record", + "relationship": "HasOne", + "widget": null, + "validations": [] + }, { + "field": "file_blob", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "ActiveStorage__Blob.id", + "inverse_of": null, + "relationship": "HasOne", + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "media_folder", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__MediaFolder.id", + "inverse_of": "images", + "relationship": "BelongsTo", + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__MediaFolder", + "name_old": "spina__spina_media_folders", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "images", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Image.id", + "inverse_of": "media_folder", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Navigation", + "name_old": "spina__spina_navigations", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "auto_add_pages", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "label", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }] + }, { + "field": "navigation_items", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__NavigationItem.id", + "inverse_of": "navigation", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "pages", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Page.id", + "inverse_of": "navigations", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": "0", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__NavigationItem", + "name_old": "spina__spina_navigation_items", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "ancestry", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is like", + "value": "/^[0-9]+(/[0-9]+)*$/" + }] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "navigation", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Navigation.id", + "inverse_of": "navigation_items", + "relationship": "BelongsTo", + "widget": null, + "validations": [] + }, { + "field": "page", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Page.id", + "inverse_of": "navigation_items", + "relationship": "BelongsTo", + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": "0", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Page", + "name_old": "spina__spina_pages", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "active", + "type": "Boolean", + "default_value": "true", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "ancestry", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "ancestry_children_count", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "ancestry_depth", + "type": "Number", + "default_value": "0", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "deletable", + "type": "Boolean", + "default_value": "true", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "draft", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "json_attributes", + "type": "Json", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "layout_template", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "link_url", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "navigation_items", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__NavigationItem.id", + "inverse_of": "page", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "navigations", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Navigation.id", + "inverse_of": "pages", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "position", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "resource", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Resource.id", + "inverse_of": "pages", + "relationship": "BelongsTo", + "widget": null, + "validations": [] + }, { + "field": "show_in_menu", + "type": "Boolean", + "default_value": "true", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "skip_to_first_child", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "slug", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "string_translations", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Mobility__Backends__ActiveRecord__KeyValue__StringTranslation.id", + "inverse_of": "translatable", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "view_template", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Resource", + "name_old": "spina__spina_resources", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "label", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "order_by", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "pages", + "type": ["Number"], + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": false, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": "Spina__Page.id", + "inverse_of": "resource", + "relationship": "HasMany", + "widget": null, + "validations": [] + }, { + "field": "slug", + "type": "Json", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "view_template", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__RewriteRule", + "name_old": "spina__spina_rewrite_rules", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "new_path", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "old_path", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__Setting", + "name_old": "spina__spina_settings", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "plugin", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }] + }, { + "field": "preferences", + "type": "Json", + "default_value": "{}", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] + }, { + "name": "Spina__User", + "name_old": "spina__spina_users", + "icon": null, + "is_read_only": false, + "is_searchable": true, + "is_virtual": false, + "only_for_relationships": false, + "pagination_type": "page", + "fields": [{ + "field": "admin", + "type": "Boolean", + "default_value": "false", + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "created_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "email", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }, { + "message": null, + "type": "is like", + "value": "/^[^@]+@[^@]+$/" + }] + }, { + "field": "id", + "type": "Number", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "last_logged_in", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "name", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": true, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [{ + "message": null, + "type": "is present" + }] + }, { + "field": "password_digest", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "password_reset_sent_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "password_reset_token", + "type": "String", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }, { + "field": "updated_at", + "type": "Date", + "default_value": null, + "enums": null, + "integration": null, + "is_filterable": true, + "is_read_only": false, + "is_required": false, + "is_sortable": true, + "is_virtual": false, + "reference": null, + "inverse_of": null, + "widget": null, + "validations": [] + }], + "segments": [], + "actions": [] }], "meta": { "liana": "forest-rails", diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..16f5fb1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,75 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [2.0.0] - 2025-07-22 - Docker & Dashboard Enhancement + +### ✨ Major Features Added +- **Complete Docker Environment** - Full containerization with PostgreSQL and optional pgAdmin +- **Interactive Homepage** - Beautiful dashboard showing all admin panels with direct links +- **Custom Port Configuration** - Runs on port 3030 to avoid conflicts +- **Production-Ready Setup** - Security-focused with non-root user and isolated networking + +### 🐳 Infrastructure +- **PostgreSQL 15** - Database server on port 3031 +- **pgAdmin 4** - Optional web admin interface on port 3032 +- **Docker Network** - Isolated `admin_panel_network` for secure communication +- **Volume Persistence** - Data persistence for database and pgAdmin +- **Dockerize Integration** - Smart service dependency management + +### 🎨 User Experience +- **Responsive Design** - Modern UI with gradient background and card layout +- **Live Status Detection** - Shows available vs unavailable admin panels +- **System Information** - Displays Ruby, Rails, database, and environment details +- **One-Click Access** - Direct navigation to any admin panel +- **Mobile-Friendly** - Works seamlessly on desktop and mobile devices + +### 🔧 Developer Experience +- **Simplified Commands** - Easy `docker-compose up` start +- **Development Profiles** - Optional services with `--profile dev` +- **Comprehensive Documentation** - Detailed setup and troubleshooting guides +- **Environment Flexibility** - Support for different database configurations +- **Command Shortcuts** - Quick access to Rails console, migrations, and seeds + +### 🛡️ Security & Best Practices +- **Non-Root User** - Runs as `rails` user (UID 1000) for security +- **Network Isolation** - Internal Docker network prevents external access +- **Port Customization** - Avoids conflicts with standard ports (3000, 5432, 80) +- **Environment Variables** - Secure configuration management +- **Volume Permissions** - Proper file ownership and permissions + +### 📚 Documentation +- **README.md** - Updated with Docker setup instructions +- **README_DOCKER.md** - Comprehensive Docker guide with troubleshooting +- **docker-compose.yml** - Production-ready configuration +- **docker-compose.with-seeds.yml** - Alternative with automatic seeding +- **Configuration Examples** - Sample environment variable files + +### 🐛 Bug Fixes & Improvements +- **Seed Execution** - Fixed hanging issue with `duty_free` gem's `binding.pry` +- **Asset Pipeline** - Optimized Docker build process +- **Database Waiting** - Smart PostgreSQL readiness detection +- **Container Health** - Proper graceful shutdown and restart handling + +### 🤝 Contributor +**[@virgiliodantas](https://github.com/virgiliodantas)** - Docker environment, interactive dashboard, and infrastructure improvements + +--- + +## [1.0.0] - 2023-03-07 - Initial Release + +### Features +- Multiple admin panel integrations (ActiveAdmin, Avo, Forest, Motor, Rails Admin, Trestle) +- Basic Rails application structure +- Database models and relationships +- Comparison framework for admin panels + +### Admin Panels Included +- **ActiveAdmin** - Ruby-centric admin interface +- **Avo** - Modern admin panel with attractive UI +- **Forest Admin** - Semi-SaaS admin solution +- **Motor Admin** - Lean admin interface with GUI form builder +- **Rails Admin** - Classic Rails admin panel +- **Trestle** - Ruby-centric DSL admin panel +- **Spina CMS** - Content management system +- **API Documentation** - OpenAPI 3.0 with Swagger UI \ No newline at end of file diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..8d59b7c --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,76 @@ +# Contributors + +We appreciate all contributions to this project! 🙏 + +## 🏗️ Core Contributors + +### [@virgiliodantas](https://github.com/virgiliodantas) +**Docker & Infrastructure Enhancement** - July 2025 + +**Major Contributions:** +- 🐳 **Complete Docker Environment**: Implemented full containerization with PostgreSQL, pgAdmin, and optimized Rails container +- 🎨 **Interactive Dashboard**: Created beautiful responsive homepage with live admin panel detection and direct access links +- 🔧 **Production-Ready Infrastructure**: Configured secure Docker networking, custom ports, and non-root user execution +- 📚 **Comprehensive Documentation**: Authored detailed setup guides, troubleshooting docs, and usage examples +- 🛡️ **Security & Best Practices**: Implemented isolated networking, proper permissions, and environment-based configuration + +**Technical Implementation:** +- Docker Compose orchestration with service profiles +- Custom port mapping (3030 for Rails, 3031 for PostgreSQL, 3032 for pgAdmin) +- Isolated Docker network (`admin_panel_network`) for security +- Smart service dependency management with dockerize +- Responsive UI with modern design patterns +- Rails routing and controller architecture for the dashboard +- PostgreSQL integration with persistent volumes +- Optional service activation via Docker profiles + +**Documentation & User Experience:** +- Created `README_DOCKER.md` with comprehensive setup instructions +- Updated main `README.md` with Docker integration information +- Authored `CHANGELOG.md` with detailed feature breakdown +- Implemented troubleshooting guides for common issues +- Designed user-friendly commands and configuration examples + +--- + +## 🎯 Original Project + +**Admin Panel Comparison Framework** - March 2023 + +The foundation project that provides: +- Multiple admin panel gem integrations +- Database models and sample data structure +- Rails application framework for comparison testing +- Initial configuration for ActiveAdmin, Avo, Forest Admin, Motor Admin, Rails Admin, Trestle, and Spina CMS + +## 🤝 How to Contribute + +We welcome contributions! Here's how you can help: + +1. **🐛 Bug Reports**: Found an issue? [Create an issue](https://github.com/your-username/admin_panel_comparison/issues) +2. **✨ Feature Requests**: Have an idea? Share it with us! +3. **📖 Documentation**: Help improve our guides and examples +4. **🔧 Code Contributions**: Submit pull requests for improvements +5. **🧪 Testing**: Test the setup on different environments and share feedback + +### Contribution Guidelines + +- Follow the existing code style and patterns +- Update documentation for any new features +- Test your changes with Docker environment +- Include clear commit messages and PR descriptions + +### Development Setup + +```bash +# Fork the repository and clone your fork +git clone https://github.com/your-username/admin_panel_comparison.git + +# Start development environment +docker-compose --profile dev up + +# Make your changes and test +# Submit a pull request with clear description +``` + +Thank you for making this project better! 🚀 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e459b4e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,60 @@ +# Use Ruby 3.2.0 como base +FROM ruby:3.2.0 + +# Instalar dependências do sistema +RUN apt-get update -qq && apt-get install -y \ + build-essential \ + libpq-dev \ + nodejs \ + npm \ + curl \ + git \ + wget \ + && rm -rf /var/lib/apt/lists/* + +# Instalar Yarn +RUN npm install -g yarn + +# Instalar dockerize para aguardar serviços +ENV DOCKERIZE_VERSION v0.6.1 +RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \ + && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz + +# Criar usuário rails +RUN groupadd --gid 1000 rails \ + && useradd --uid 1000 --gid rails --shell /bin/bash --create-home rails + +# Definir diretório de trabalho +WORKDIR /app + +# Copiar Gemfile e Gemfile.lock +COPY Gemfile Gemfile.lock ./ + +# Instalar gems como root +RUN bundle config build.nokogiri --use-system-libraries \ + && bundle install + +# Copiar o código da aplicação +COPY --chown=rails:rails . . + +# Pré-compilar assets (se necessário) +#RUN bundle exec rails assets:precompile RAILS_ENV=production + +# Alterar ownership do diretório para o usuário rails +RUN chown -R rails:rails /app + +# Mudar para o usuário rails +USER rails + +# Expor porta 3030 +EXPOSE 3030 + +# Script de inicialização +COPY --chown=rails:rails docker-entrypoint.sh /usr/bin/ +RUN chmod +x /usr/bin/docker-entrypoint.sh + +ENTRYPOINT ["docker-entrypoint.sh"] + +# Comando padrão +CMD ["rails", "server", "-b", "0.0.0.0"] \ No newline at end of file diff --git a/Gemfile b/Gemfile index a2fb1e6..1097b8e 100644 --- a/Gemfile +++ b/Gemfile @@ -26,7 +26,7 @@ gem 'motor-admin' gem 'rails_admin' gem 'spina' gem 'trestle' - +gem 'mobility' # For Avo gem 'ransack' diff --git a/Gemfile.lock b/Gemfile.lock index 3080f1e..03c4100 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -400,8 +400,11 @@ GEM activesupport (>= 3) attr_required (>= 0.0.5) httpclient (>= 2.4) - tailwindcss-rails (2.0.23-arm64-darwin) - railties (>= 6.0.0) + tailwindcss-rails (4.3.0) + railties (>= 7.0.0) + tailwindcss-ruby (~> 4.0) + tailwindcss-ruby (4.1.11-arm64-darwin) + tailwindcss-ruby (4.1.11-x86_64-linux-gnu) thor (1.2.1) timeout (0.3.2) trestle (0.9.7) @@ -462,6 +465,7 @@ DEPENDENCIES forest_liana (~> 7) importmap-rails jbuilder + mobility motor-admin pg (~> 1.1) pry-byebug diff --git a/README.md b/README.md index 688140b..49d46b7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,35 @@ -# Compare Admin Panels! +# Compare Admin Panels! 🚀 -An analysis of six popular admin panel gems for use with Ruby on Rails: +An analysis of six popular admin panel gems for use with Ruby on Rails with **Docker support** and interactive dashboard. + +## ✨ Quick Start with Docker + +Get all admin panels running in minutes: + +```bash +# Clone the repository +git clone https://github.com/your-username/admin_panel_comparison.git +cd admin_panel_comparison + +# Start with Docker (without pgAdmin) +docker-compose up + +# Or with pgAdmin for development +docker-compose --profile dev up +``` + +**Access the interactive dashboard:** [http://localhost:3030](http://localhost:3030) + +### 🆕 What's New + +- **🚀 One-Command Setup**: Complete environment with `docker-compose up` +- **🎨 Interactive Dashboard**: Beautiful homepage showing all available panels +- **🔒 Production-Ready**: Security-focused with non-root user and isolated networking +- **⚙️ Smart Configuration**: Custom ports to avoid conflicts with existing services +- **🔧 Optional pgAdmin**: Database administration only when needed +- **📖 Comprehensive Docs**: Step-by-step guides for development and troubleshooting + +--- ## Active Admin * The "original" admin panel -- still widely used for new projects today @@ -52,6 +81,79 @@ https://trestle.io/ Also considering adding ThoughtBot's [Administrate](https://github.com/thoughtbot/administrate) and Chris Oliver's [madmin](https://github.com/excid3/madmin). +## 🐳 Docker Setup & Infrastructure + +### Architecture + +This project includes a complete Docker environment with: + +- **🎯 Interactive Homepage**: Beautiful dashboard at [http://localhost:3030](http://localhost:3030) +- **🗄️ PostgreSQL 15**: Database server accessible on port 3031 +- **🔧 pgAdmin (Optional)**: Web-based PostgreSQL admin on port 3032 +- **🌐 Isolated Network**: Internal Docker network for secure communication +- **👤 Security**: Runs with non-root `rails` user (UID 1000) + +### Services & Ports + +| Service | Host Port | Description | +|---------|-----------|-------------| +| **Rails App** | 3030 | Main application with admin panels | +| **PostgreSQL** | 3031 | Database server | +| **pgAdmin** | 3032 | Database administration (optional) | + +### Getting Started + +```bash +# 1. Build and start (minimal setup) +docker-compose up + +# 2. With PostgreSQL admin interface +docker-compose --profile dev up + +# 3. Run in background +docker-compose up -d + +# 4. View logs +docker-compose logs -f web +``` + +### Database Management + +```bash +# Access PostgreSQL directly +docker-compose exec postgres psql -U admin_panel_user -d northwind + +# Run Rails commands +docker-compose exec web rails console +docker-compose exec web rails db:migrate +docker-compose exec web rails db:seed +``` + +### 📊 Interactive Dashboard Features + +The homepage ([localhost:3030](http://localhost:3030)) includes: + +- ✅ **Live Panel Status**: Shows which admin panels are available +- 🔗 **One-Click Access**: Direct links to all admin interfaces +- 📈 **System Information**: Ruby, Rails, and database details +- 📱 **Responsive Design**: Works on desktop and mobile +- 🎨 **Modern UI**: Beautiful gradient design with cards + +### Configuration + +All services use custom ports to avoid conflicts: + +- **Database**: `northwind` (PostgreSQL) +- **Credentials**: admin_panel_user / admin_panel_password +- **Network**: Internal `admin_panel_network` for security +- **Volumes**: Persistent data for PostgreSQL and pgAdmin + +📖 **For detailed Docker instructions, see [README_DOCKER.md](README_DOCKER.md)** +📋 **For complete changelog, see [CHANGELOG.md](CHANGELOG.md)** +👥 **For contributor information, see [CONTRIBUTORS.md](CONTRIBUTORS.md)** + +--- + ### Troubleshooting If you get: @@ -63,3 +165,27 @@ then do this: bin/rails g active_admin:assets ``` (But it really should already be there.) + +## 🏗️ Development Setup (Traditional) + +If you prefer to run without Docker: + +1. **Prerequisites**: Ruby 3.2.0, Rails 7.0.4+, PostgreSQL +2. **Install dependencies**: `bundle install` +3. **Setup database**: `rails db:create db:migrate` +4. **Start server**: `rails server` +5. **Access**: [http://localhost:3000](http://localhost:3000) + +## 🤝 Contributors + +- **Original Project**: Comparison framework and admin panel integrations +- **Docker & Dashboard Enhancement**: [@virgiliodantas](https://github.com/virgiliodantas) + - Complete Docker environment with PostgreSQL and pgAdmin + - Interactive homepage with live admin panel status + - Custom ports configuration to avoid conflicts + - Isolated Docker networking and security improvements + - Comprehensive documentation and troubleshooting guides + +## 📝 License & Usage + +This project is designed for educational and comparison purposes. Each admin panel gem has its own license and terms of use. Please refer to their respective documentation for production usage guidelines. diff --git a/README_DOCKER.md b/README_DOCKER.md new file mode 100644 index 0000000..95335b7 --- /dev/null +++ b/README_DOCKER.md @@ -0,0 +1,229 @@ +# Admin Panel Comparison - Docker Setup + +Este documento explica como rodar o projeto Rails usando Docker. + +## Pré-requisitos + +- Docker +- Docker Compose + +## Configuração e Execução + +### 1. Build da imagem Docker + +```bash +docker-compose build +``` + +### 2. Executar a aplicação + +```bash +# Versão padrão (sem pgAdmin, sem seeds automáticos) +docker-compose up + +# Com pgAdmin para desenvolvimento +docker-compose --profile dev up + +# Versão alternativa (COM seeds automáticos - pode travar) +docker-compose -f docker-compose.with-seeds.yml up + +# Com pgAdmin E seeds automáticos +docker-compose -f docker-compose.with-seeds.yml --profile dev up +``` + +A aplicação estará disponível em `http://localhost:3030` + +### 3. Executar em background + +```bash +docker-compose up -d +``` + +### 4. Ver logs + +```bash +docker-compose logs -f web +``` + +### 5. Parar os containers + +```bash +docker-compose down +``` + +## Comandos úteis + +### Executar comandos Rails dentro do container + +```bash +# Executar console Rails +docker-compose exec web rails console + +# Executar testes +docker-compose exec web rails test + +# Executar generator +docker-compose exec web rails generate + +# Instalar nova gem +docker-compose exec web bundle install + +# Executar migrações +docker-compose exec web rails db:migrate + +# Executar seeds (CUIDADO: Pode travar devido ao gem duty_free usar binding.pry) +docker-compose exec web rails db:seed + +# Executar seeds sem interação (mais seguro) +docker-compose exec web bash -c "DISABLE_PRY=true rails db:seed" +``` + +### Acessar containers diretamente + +```bash +# Acessar container da aplicação Rails +docker-compose exec web bash + +# Acessar container do PostgreSQL +docker-compose exec postgres psql -U admin_panel_user -d northwind + +# Ver logs específicos +docker-compose logs postgres +docker-compose logs pgadmin +docker-compose logs web +``` + +### Rebuild completo (quando houver mudanças no Dockerfile ou Gemfile) + +```bash +docker-compose down +docker-compose build --no-cache +docker-compose up +``` + +## Serviços e Portas Disponíveis + +### Aplicação Rails +- **URL**: http://localhost:3030 +- **Container**: admin_panel_web + +### Banco de Dados +- **PostgreSQL**: localhost:3031 +- **Database**: northwind +- **Username**: admin_panel_user +- **Password**: admin_panel_password + +### pgAdmin (Administração do PostgreSQL) - OPCIONAL +- **URL**: http://localhost:3032 +- **Email**: admin@admin.com +- **Password**: admin_panel_password +- **Ativação**: Use `--profile dev` para incluir pgAdmin + +#### Configuração Inicial do pgAdmin: +1. Execute: `docker-compose --profile dev up` +2. Acesse http://localhost:3032 +3. Faça login com as credenciais acima +4. Para conectar ao PostgreSQL: + - Host: `postgres` + - Port: `5432` + - Username: `admin_panel_user` + - Password: `admin_panel_password` + +## 🎯 Página Inicial e Painéis Administrativos + +### Página Inicial +- **URL**: http://localhost:3030 +- **Descrição**: Lista interativa com todos os painéis disponíveis e links clicáveis + +### Painéis Administrativos Diretos + +- **ActiveAdmin**: http://localhost:3030/aa +- **Avo**: http://localhost:3030/avo +- **Forest Admin**: http://localhost:3030/forest (Não Funcional - Problemas de IP) +- **Motor Admin**: http://localhost:3030/motor +- **Rails Admin**: http://localhost:3030/ra +- **Spina CMS**: http://localhost:3030/spina (Não Funcional - Problemas de configuração) +- **Trestle**: http://localhost:3030/trestle +- **API Docs**: http://localhost:3030/api-docs + +## Arquitetura dos Containers + +### Rede Interna +- **Nome**: admin_panel_network +- **Tipo**: bridge +- **Propósito**: Comunicação isolada entre web server e PostgreSQL + +### Containers +1. **admin_panel_postgres**: PostgreSQL 15 +2. **admin_panel_pgadmin**: pgAdmin 4 (OPCIONAL - apenas com --profile dev) +3. **admin_panel_web**: Aplicação Rails + +### Configuração de Rede + +Os containers se comunicam através da rede interna `admin_panel_network`, evitando conflitos com outros containers que possam estar usando as portas padrões (3000, 5432, 80). + +## Notas Importantes + +- O container web roda com o usuário `rails` (UID 1000) por questões de segurança +- Os gems são armazenados em um volume para melhor performance +- PostgreSQL e dados do pgAdmin são persistidos em volumes +- A aplicação aguarda o PostgreSQL estar disponível antes de iniciar +- Banco de dados e migrações são executados automaticamente na inicialização +- **Seeds NÃO são executados automaticamente** devido a problemas com binding.pry no gem duty_free + +## Troubleshooting + +### Problemas de permissão +Se encontrar problemas de permissão, certifique-se de que seu usuário local tem UID 1000 ou ajuste o Dockerfile. + +### Rebuild da imagem após mudanças +Se modificar o Gemfile ou Dockerfile, execute: +```bash +docker-compose build --no-cache +``` + +### Limpar volumes +Para limpar todos os volumes (cuidado - remove dados): +```bash +docker-compose down -v +``` + +### Problemas com PostgreSQL +Se o PostgreSQL não inicializar corretamente: +```bash +# Parar todos os containers +docker-compose down + +# Remover volume do PostgreSQL (apaga dados!) +docker volume rm admin_panel_comparison_postgres_data + +# Reiniciar +docker-compose up --build +``` + +### Resetar banco de dados +```bash +# Dentro do container da aplicação (sem seeds) +docker-compose exec web rails db:drop db:create db:migrate + +# Com seeds (pode travar devido ao binding.pry) +docker-compose exec web rails db:drop db:create db:migrate db:seed +``` + +## ⚠️ Problemas Conhecidos + +### Gem duty_free e binding.pry +O gem `duty_free` usado para importar dados de exemplo contém `binding.pry` que pode travar a execução em containers Docker. Por isso: + +- **Seeds não são executados automaticamente** na inicialização +- Se quiser executar seeds, faça manualmente: `docker-compose exec web rails db:seed` +- Se o seed travar, pare o container com `Ctrl+C` e reinicie: `docker-compose up` +- Os painéis administrativos funcionam perfeitamente sem dados de exemplo + +### Forest Admin e Verificação de IP +O Forest Admin por padrão verifica whitelists de IP para segurança. Em desenvolvimento local: + +- **Configuração automática**: IP whitelist desabilitado automaticamente em `development` +- **Chaves de desenvolvimento**: Configuradas automaticamente via Docker +- **Produção**: Configure `FOREST_ENV_SECRET` e `FOREST_AUTH_SECRET` com valores reais +- **Acesso liberado**: Qualquer IP pode acessar em desenvolvimento (`localhost:3030/forest`) \ No newline at end of file diff --git a/app/assets/config/spina/tailwind.config.js b/app/assets/config/spina/tailwind.config.js new file mode 100644 index 0000000..9aa1291 --- /dev/null +++ b/app/assets/config/spina/tailwind.config.js @@ -0,0 +1,29 @@ +module.exports = { + content: [ + '/usr/local/bundle/gems/spina-2.14.0/app/views/**/*.*', +'/usr/local/bundle/gems/spina-2.14.0/app/components/**/*.*', +'/usr/local/bundle/gems/spina-2.14.0/app/helpers/**/*.*', +'/usr/local/bundle/gems/spina-2.14.0/app/assets/javascripts/**/*.js', +'/usr/local/bundle/gems/spina-2.14.0/app/**/application.tailwind.css' + ], + theme: { + fontFamily: { + body: ['Metropolis'], + mono: ['ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', "Liberation Mono", "Courier New", 'monospace'] + }, + extend: { + colors: { + spina: { + light: '#797ab8', + DEFAULT: '#6865b4', + dark: '#3a3a70' + } + } + } + }, + plugins: [ + require('@tailwindcss/forms'), + require('@tailwindcss/aspect-ratio'), + require('@tailwindcss/typography') + ] +} diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000..493d1b0 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,57 @@ +class HomeController < ApplicationController + def index + @admin_panels = [ + { + name: "ActiveAdmin", + path: "/aa", + description: "The 'original' admin panel - Ruby-centric markup using Arbre", + available: Object.const_defined?('ActiveAdmin') + }, + { + name: "Avo", + path: "/avo", + description: "Modern interface with attractive UI and newest front-end technology", + available: Object.const_defined?('Avo') + }, + { + name: "Forest Admin", + path: "/forest", + description: "Semi-SaaS solution with data visualization and workflow enhancements", + available: false + }, + { + name: "Motor Admin", + path: "/motor", + description: "Very lean with GUI Form Builder and CSV export capabilities", + available: Object.const_defined?('Motor') + }, + { + name: "Rails Admin", + path: "/ra", + description: "The 'original' sexier alternative admin panel with CSV export", + available: Object.const_defined?('RailsAdmin') + }, + { + name: "Spina CMS", + path: "/spina", + description: "Beautiful CMS for Rails applications", + available: false + }, + { + name: "Trestle", + path: "/trestle", + description: "Ruby-centric DSL with well thought out set of helpers", + available: Object.const_defined?('Trestle') + }, + { + name: "API Documentation", + path: "/api-docs", + description: "OpenAPI 3.0 documentation with Swagger UI", + available: Object.const_defined?('Rswag') + } + ] + + @available_panels = @admin_panels.select { |panel| panel[:available] } + @unavailable_panels = @admin_panels.reject { |panel| panel[:available] } + end +end \ No newline at end of file diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 0000000..ffa7144 --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1,202 @@ +<% content_for :title, "Admin Panel Comparison" %> + + + + + + + Admin Panel Comparison - Rails + + + +
+
+

🚀 Admin Panel Comparison

+

Compare diferentes soluções de painéis administrativos para Ruby on Rails

+
+ + <% if @available_panels.any? %> +
+ <% @available_panels.each do |panel| %> + +
<%= panel[:name] %>
+
<%= panel[:description] %>
+ ✅ Disponível +
+ <% end %> +
+ <% end %> + + <% if @unavailable_panels.any? %> +
+
Painéis Indisponíveis
+
+ <% @unavailable_panels.each do |panel| %> +
+
<%= panel[:name] %>
+
<%= panel[:description] %>
+ ❌ Não configurado +
+ <% end %> +
+
+ <% end %> + +
+

📊 Informações do Sistema

+
+
+ Ruby Version: + <%= RUBY_VERSION %> +
+
+ Rails Version: + <%= Rails.version %> +
+
+ Database: + <%= Rails.application.config.database_configuration[Rails.env]['adapter'] rescue 'N/A' %> +
+
+ Environment: + <%= Rails.env.titleize %> +
+
+ +
+

🐳 Rodando em Docker | 🌐 Porta 3030 | 🗄️ PostgreSQL na porta 3031

+
+
+
+ + \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index 74a5bce..e2c2293 100644 --- a/config/application.rb +++ b/config/application.rb @@ -20,3 +20,6 @@ class Application < Rails::Application # config.eager_load_paths << Rails.root.join("extras") end end +Rails.autoloaders.main.ignore( + Rails.root.join("lib/forest_liana"), +) diff --git a/config/database.yml b/config/database.yml index 67145ef..0c2ea8f 100644 --- a/config/database.yml +++ b/config/database.yml @@ -23,25 +23,11 @@ default: &default development: <<: *default - database: northwind - - # The specified database role being used to connect to postgres. - # To create additional roles in postgres see `$ createuser --help`. - # When left blank, postgres will use the default role. This is - # the same name as the operating system user running Rails. - #username: admin_panel_comparison - - # The password associated with the postgres role (username). - #password: - - # Connect on a TCP socket. Omitted by default since the client uses a - # domain socket that doesn't need configuration. Windows does not have - # domain sockets, so uncomment these lines. - #host: localhost - - # The TCP port the server listens on. Defaults to 5432. - # If your server runs on a different port number, change accordingly. - #port: 5432 + database: <%= ENV.fetch("POSTGRES_DB") { "northwind" } %> + username: <%= ENV.fetch("POSTGRES_USER") { "admin_panel_comparison" } %> + password: <%= ENV.fetch("POSTGRES_PASSWORD") { "" } %> + host: <%= ENV.fetch("POSTGRES_HOST") { "localhost" } %> + port: <%= ENV.fetch("POSTGRES_PORT") { "5432" } %> # Schema search path. The server defaults to $user,public #schema_search_path: myapp,sharedapp,public diff --git a/config/docker.env.example b/config/docker.env.example new file mode 100644 index 0000000..e2fe2af --- /dev/null +++ b/config/docker.env.example @@ -0,0 +1,31 @@ +# Configurações do Banco de Dados PostgreSQL +POSTGRES_DB=northwind +POSTGRES_USER=admin_panel_user +POSTGRES_PASSWORD=admin_panel_password +POSTGRES_HOST=postgres +POSTGRES_PORT=5432 + +# URL completa do banco (alternativa) +DATABASE_URL=postgresql://admin_panel_user:admin_panel_password@postgres:5432/northwind + +# Configurações do Rails +RAILS_ENV=development +RAILS_SERVE_STATIC_FILES=true +RAILS_LOG_TO_STDOUT=true + +# Configurações do pgAdmin +PGADMIN_DEFAULT_EMAIL=admin@admin.com +PGADMIN_DEFAULT_PASSWORD=admin_panel_password + +# Portas do host (para mapeamento Docker) +RAILS_PORT=3030 +POSTGRES_EXTERNAL_PORT=3031 +PGADMIN_EXTERNAL_PORT=3032 + +# Configurações do Forest Admin +FOREST_ENV_SECRET=development_secret_key_change_in_production +FOREST_AUTH_SECRET=development_auth_key_change_in_production +DATABASE_REJECT_UNAUTHORIZED=false + +# Chave secreta do Rails +SECRET_KEY_BASE=development_secret_key_base_at_least_30_characters_long_for_security \ No newline at end of file diff --git a/config/initializers/forest_liana.rb b/config/initializers/forest_liana.rb index 8393899..15f47a1 100644 --- a/config/initializers/forest_liana.rb +++ b/config/initializers/forest_liana.rb @@ -1,4 +1,16 @@ if Object.const_defined?('ForestLiana') ForestLiana.env_secret = Rails.application.secrets.forest_env_secret ForestLiana.auth_secret = Rails.application.secrets.forest_auth_secret + end +ForestLiana.env_secret = + ENV.fetch( + "FOREST_ENV_SECRET", + Rails.application.credentials.forest_env_secret, + ) +ForestLiana.auth_secret = + ENV.fetch( + "FOREST_AUTH_SECRET", + Rails.application.credentials.forest_auth_secret, + ) + ForestLiana.env_secret = ENV['FOREST_ENV_SECRET'] \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 13699cb..4279a38 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,7 @@ Rails.application.routes.draw do + # Root route - Home page with admin panels list + root 'home#index' + # Various Administrative Panels # (ordered alphabetically) diff --git a/db/migrate/20250722161043_create_spina_tables.spina.rb b/db/migrate/20250722161043_create_spina_tables.spina.rb new file mode 100644 index 0000000..55384ae --- /dev/null +++ b/db/migrate/20250722161043_create_spina_tables.spina.rb @@ -0,0 +1,130 @@ +# This migration comes from spina (originally 1) +class CreateSpinaTables < ActiveRecord::Migration[4.2] + def change + create_table "spina_accounts", force: :cascade do |t| + t.string "name" + t.string "address" + t.string "postal_code" + t.string "city" + t.string "phone" + t.string "email" + t.text "preferences" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "robots_allowed", default: false + end + + create_table "spina_attachment_collections", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "spina_attachment_collections_attachments", force: :cascade do |t| + t.integer "attachment_collection_id" + t.integer "attachment_id" + end + + create_table "spina_attachments", force: :cascade do |t| + t.string "file" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "spina_layout_parts", force: :cascade do |t| + t.string "title" + t.string "name" + t.integer "layout_partable_id" + t.string "layout_partable_type" + t.datetime "created_at" + t.datetime "updated_at" + t.integer "account_id" + end + + create_table "spina_lines", force: :cascade do |t| + t.string "content" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "spina_page_parts", force: :cascade do |t| + t.string "title" + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "page_id" + t.integer "page_partable_id" + t.string "page_partable_type" + end + + create_table "spina_pages", force: :cascade do |t| + t.string "title" + t.string "menu_title" + t.string "description" + t.boolean "show_in_menu", default: true + t.string "slug" + t.boolean "deletable", default: true + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name" + t.string "seo_title" + t.boolean "skip_to_first_child", default: false + t.string "view_template" + t.string "layout_template" + t.boolean "draft", default: false + t.string "link_url" + t.string "ancestry" + t.integer "position" + t.string "materialized_path" + t.boolean "active", default: true + end + + create_table "spina_structure_items", force: :cascade do |t| + t.integer "structure_id" + t.integer "position" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "spina_structure_items", ["structure_id"], name: "index_spina_structure_items_on_structure_id", using: :btree + + create_table "spina_structure_parts", force: :cascade do |t| + t.integer "structure_item_id" + t.integer "structure_partable_id" + t.string "structure_partable_type" + t.string "name" + t.string "title" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "spina_structure_parts", ["structure_item_id"], name: "index_spina_structure_parts_on_structure_item_id", using: :btree + add_index "spina_structure_parts", ["structure_partable_id"], name: "index_spina_structure_parts_on_structure_partable_id", using: :btree + + create_table "spina_structures", force: :cascade do |t| + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "spina_texts", force: :cascade do |t| + t.text "content" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "spina_users", force: :cascade do |t| + t.string "name" + t.string "email" + t.string "password_digest" + t.boolean "admin", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.datetime "last_logged_in" + end + + create_table "spina_rewrite_rules", force: :cascade do |t| + t.string :old_path + t.string :new_path + t.timestamps + end + end +end diff --git a/db/migrate/20250722161044_create_spina_translation_tables.spina.rb b/db/migrate/20250722161044_create_spina_translation_tables.spina.rb new file mode 100644 index 0000000..98fe322 --- /dev/null +++ b/db/migrate/20250722161044_create_spina_translation_tables.spina.rb @@ -0,0 +1,44 @@ +# This migration comes from spina (originally 2) +class CreateSpinaTranslationTables < ActiveRecord::Migration[4.2] + def up + create_table "spina_page_translations", force: :cascade do |t| + t.integer "spina_page_id", null: false + t.string "locale", null: false + t.string "title" + t.string "menu_title" + t.string "description" + t.string "seo_title" + t.string "materialized_path" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["locale"], name: "index_spina_page_translations_on_locale" + t.index ["spina_page_id"], name: "index_spina_page_translations_on_spina_page_id" + end + + create_table "spina_line_translations", force: :cascade do |t| + t.integer "spina_line_id", null: false + t.string "locale", null: false + t.string "content" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["locale"], name: "index_spina_line_translations_on_locale" + t.index ["spina_line_id"], name: "index_spina_line_translations_on_spina_line_id" + end + + create_table "spina_text_translations", force: :cascade do |t| + t.integer "spina_text_id", null: false + t.string "locale", null: false + t.text "content" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["locale"], name: "index_spina_text_translations_on_locale" + t.index ["spina_text_id"], name: "index_spina_text_translations_on_spina_text_id" + end + end + + def down + drop_table "spina_page_translations" + drop_table "spina_text_translations" + drop_table "spina_line_translations" + end +end diff --git a/db/migrate/20250722161045_create_spina_navigations.spina.rb b/db/migrate/20250722161045_create_spina_navigations.spina.rb new file mode 100644 index 0000000..0654c1a --- /dev/null +++ b/db/migrate/20250722161045_create_spina_navigations.spina.rb @@ -0,0 +1,22 @@ +# This migration comes from spina (originally 3) +class CreateSpinaNavigations < ActiveRecord::Migration[4.2] + def change + create_table "spina_navigations", force: :cascade do |t| + t.string "name", null: false + t.string "label", null: false + t.boolean "auto_add_pages", null: false, default: false + t.integer "position", default: 0, null: false + t.timestamps + t.index ["name"], name: "index_spina_navigations_on_name", unique: true, using: :btree + end + + create_table "spina_navigation_items", force: :cascade do |t| + t.integer "page_id", null: false + t.integer "navigation_id", null: false + t.integer "position", default: 0, null: false + t.string "ancestry" + t.timestamps + t.index ["page_id", "navigation_id"], name: "index_spina_navigation_items_on_page_id_and_navigation_id", unique: true, using: :btree + end + end +end diff --git a/db/migrate/20250722161046_add_password_reset_token_to_spina_users.spina.rb b/db/migrate/20250722161046_add_password_reset_token_to_spina_users.spina.rb new file mode 100644 index 0000000..ce51b30 --- /dev/null +++ b/db/migrate/20250722161046_add_password_reset_token_to_spina_users.spina.rb @@ -0,0 +1,7 @@ +# This migration comes from spina (originally 4) +class AddPasswordResetTokenToSpinaUsers < ActiveRecord::Migration[5.0] + def change + add_column :spina_users, :password_reset_token, :string + add_column :spina_users, :password_reset_sent_at, :datetime + end +end diff --git a/db/migrate/20250722161047_remove_translated_columns.spina.rb b/db/migrate/20250722161047_remove_translated_columns.spina.rb new file mode 100644 index 0000000..1d8fc6a --- /dev/null +++ b/db/migrate/20250722161047_remove_translated_columns.spina.rb @@ -0,0 +1,12 @@ +# This migration comes from spina (originally 5) +class RemoveTranslatedColumns < ActiveRecord::Migration[5.0] + def change + remove_column :spina_lines, :content + remove_column :spina_texts, :content + remove_column :spina_pages, :title + remove_column :spina_pages, :menu_title + remove_column :spina_pages, :description + remove_column :spina_pages, :seo_title + remove_column :spina_pages, :materialized_path + end +end diff --git a/db/migrate/20250722161048_create_spina_options.spina.rb b/db/migrate/20250722161048_create_spina_options.spina.rb new file mode 100644 index 0000000..f045c8b --- /dev/null +++ b/db/migrate/20250722161048_create_spina_options.spina.rb @@ -0,0 +1,9 @@ +# This migration comes from spina (originally 6) +class CreateSpinaOptions < ActiveRecord::Migration[5.0] + def change + create_table :spina_options do |t| + t.string :value + t.timestamps + end + end +end diff --git a/db/migrate/20250722161049_create_spina_settings.spina.rb b/db/migrate/20250722161049_create_spina_settings.spina.rb new file mode 100644 index 0000000..249b78b --- /dev/null +++ b/db/migrate/20250722161049_create_spina_settings.spina.rb @@ -0,0 +1,12 @@ +# This migration comes from spina (originally 7) +class CreateSpinaSettings < ActiveRecord::Migration[5.0] + def change + create_table :spina_settings do |t| + t.string :plugin + t.jsonb :preferences, default: {} + t.timestamps + end + + add_index :spina_settings, :plugin + end +end diff --git a/db/migrate/20250722161050_create_spina_media_folders.spina.rb b/db/migrate/20250722161050_create_spina_media_folders.spina.rb new file mode 100644 index 0000000..16dbd02 --- /dev/null +++ b/db/migrate/20250722161050_create_spina_media_folders.spina.rb @@ -0,0 +1,9 @@ +# This migration comes from spina (originally 8) +class CreateSpinaMediaFolders < ActiveRecord::Migration[5.1] + def change + create_table :spina_media_folders do |t| + t.string :name + t.timestamps + end + end +end diff --git a/db/migrate/20250722161051_create_spina_images.spina.rb b/db/migrate/20250722161051_create_spina_images.spina.rb new file mode 100644 index 0000000..483e09f --- /dev/null +++ b/db/migrate/20250722161051_create_spina_images.spina.rb @@ -0,0 +1,11 @@ +# This migration comes from spina (originally 9) +class CreateSpinaImages < ActiveRecord::Migration[5.2] + def change + create_table :spina_images do |t| + t.integer :media_folder_id + + t.timestamps + end + add_index :spina_images, :media_folder_id + end +end diff --git a/db/migrate/20250722161052_create_spina_image_collections.spina.rb b/db/migrate/20250722161052_create_spina_image_collections.spina.rb new file mode 100644 index 0000000..3906de4 --- /dev/null +++ b/db/migrate/20250722161052_create_spina_image_collections.spina.rb @@ -0,0 +1,17 @@ +# This migration comes from spina (originally 10) +class CreateSpinaImageCollections < ActiveRecord::Migration[5.2] + def change + create_table :spina_image_collections do |t| + t.timestamps + end + + create_table "spina_image_collections_images", id: :serial, force: :cascade do |t| + t.integer "image_collection_id" + t.integer "image_id" + t.integer "position" + end + + add_index :spina_image_collections_images, :image_collection_id + add_index :spina_image_collections_images, :image_id + end +end diff --git a/db/migrate/20250722161053_create_spina_resources.spina.rb b/db/migrate/20250722161053_create_spina_resources.spina.rb new file mode 100644 index 0000000..fac4a8d --- /dev/null +++ b/db/migrate/20250722161053_create_spina_resources.spina.rb @@ -0,0 +1,14 @@ +# This migration comes from spina (originally 11) +class CreateSpinaResources < ActiveRecord::Migration[5.1] + def change + create_table :spina_resources do |t| + t.string :name, null: false, unique: true + t.string :label + t.string :view_template + t.string :order_by + t.timestamps + end + add_column :spina_pages, :resource_id, :integer, null: true + add_index :spina_pages, :resource_id + end +end diff --git a/db/migrate/20250722161054_add_url_title_to_spina_page_translations.spina.rb b/db/migrate/20250722161054_add_url_title_to_spina_page_translations.spina.rb new file mode 100644 index 0000000..5f51396 --- /dev/null +++ b/db/migrate/20250722161054_add_url_title_to_spina_page_translations.spina.rb @@ -0,0 +1,6 @@ +# This migration comes from spina (originally 12) +class AddUrlTitleToSpinaPageTranslations < ActiveRecord::Migration[5.2] + def change + add_column :spina_page_translations, :url_title, :string + end +end diff --git a/db/migrate/20250722161055_add_json_attributes_to_spina_accounts.spina.rb b/db/migrate/20250722161055_add_json_attributes_to_spina_accounts.spina.rb new file mode 100644 index 0000000..2210b67 --- /dev/null +++ b/db/migrate/20250722161055_add_json_attributes_to_spina_accounts.spina.rb @@ -0,0 +1,6 @@ +# This migration comes from spina (originally 13) +class AddJsonAttributesToSpinaAccounts < ActiveRecord::Migration[5.2] + def change + add_column :spina_accounts, :json_attributes, :jsonb + end +end diff --git a/db/migrate/20250722161056_add_json_attributes_to_spina_pages.spina.rb b/db/migrate/20250722161056_add_json_attributes_to_spina_pages.spina.rb new file mode 100644 index 0000000..7b8abfb --- /dev/null +++ b/db/migrate/20250722161056_add_json_attributes_to_spina_pages.spina.rb @@ -0,0 +1,6 @@ +# This migration comes from spina (originally 14) +class AddJsonAttributesToSpinaPages < ActiveRecord::Migration[5.2] + def change + add_column :spina_pages, :json_attributes, :jsonb + end +end diff --git a/db/migrate/20250722161057_add_slug_to_spina_resources.spina.rb b/db/migrate/20250722161057_add_slug_to_spina_resources.spina.rb new file mode 100644 index 0000000..0c93539 --- /dev/null +++ b/db/migrate/20250722161057_add_slug_to_spina_resources.spina.rb @@ -0,0 +1,6 @@ +# This migration comes from spina (originally 15) +class AddSlugToSpinaResources < ActiveRecord::Migration[5.2] + def change + add_column :spina_resources, :slug, :jsonb + end +end diff --git a/db/migrate/20250722161058_add_ancestry_cache_columns_to_spina_pages.spina.rb b/db/migrate/20250722161058_add_ancestry_cache_columns_to_spina_pages.spina.rb new file mode 100644 index 0000000..6624415 --- /dev/null +++ b/db/migrate/20250722161058_add_ancestry_cache_columns_to_spina_pages.spina.rb @@ -0,0 +1,7 @@ +# This migration comes from spina (originally 16) +class AddAncestryCacheColumnsToSpinaPages < ActiveRecord::Migration[6.0] + def change + add_column :spina_pages, :ancestry_depth, :integer, default: 0 + add_column :spina_pages, :ancestry_children_count, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 7b1d7a9..03f1350 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_03_02_064251) do +ActiveRecord::Schema[7.0].define(version: 2025_07_22_161058) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -28,34 +28,6 @@ t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource" end - create_table "active_storage_attachments", force: :cascade do |t| - t.string "name", null: false - t.string "record_type", null: false - t.bigint "record_id", null: false - t.bigint "blob_id", null: false - t.datetime "created_at", null: false - t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" - t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true - end - - create_table "active_storage_blobs", force: :cascade do |t| - t.string "key", null: false - t.string "filename", null: false - t.string "content_type" - t.text "metadata" - t.string "service_name", null: false - t.bigint "byte_size", null: false - t.string "checksum" - t.datetime "created_at", null: false - t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true - end - - create_table "active_storage_variant_records", force: :cascade do |t| - t.bigint "blob_id", null: false - t.string "variation_digest", null: false - t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true - end - create_table "categories", id: :serial, force: :cascade do |t| t.string "category_name" t.string "description" @@ -96,31 +68,6 @@ t.index ["reports_to_id"], name: "index_employees_on_reports_to_id" end - create_table "mobility_string_translations", force: :cascade do |t| - t.string "locale", null: false - t.string "key", null: false - t.string "value" - t.string "translatable_type" - t.bigint "translatable_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_string_translations_on_translatable_attribute" - t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_string_translations_on_keys", unique: true - t.index ["translatable_type", "key", "value", "locale"], name: "index_mobility_string_translations_on_query_keys" - end - - create_table "mobility_text_translations", force: :cascade do |t| - t.string "locale", null: false - t.string "key", null: false - t.text "value" - t.string "translatable_type" - t.bigint "translatable_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["translatable_id", "translatable_type", "key"], name: "index_mobility_text_translations_on_translatable_attribute" - t.index ["translatable_id", "translatable_type", "locale", "key"], name: "index_mobility_text_translations_on_keys", unique: true - end - create_table "motor_alert_locks", force: :cascade do |t| t.bigint "alert_id", null: false t.string "lock_timestamp", null: false @@ -586,8 +533,6 @@ t.datetime "password_reset_sent_at", precision: nil end - add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" - add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "motor_alert_locks", "motor_alerts", column: "alert_id" add_foreign_key "motor_alerts", "motor_queries", column: "query_id" add_foreign_key "motor_note_tag_tags", "motor_note_tags", column: "tag_id" diff --git a/docker-compose.with-seeds.yml b/docker-compose.with-seeds.yml new file mode 100644 index 0000000..e1236b5 --- /dev/null +++ b/docker-compose.with-seeds.yml @@ -0,0 +1,85 @@ +version: '3.8' + +networks: + admin_panel_network: + driver: bridge + +services: + postgres: + image: postgres:15 + container_name: admin_panel_postgres + environment: + POSTGRES_DB: northwind + POSTGRES_USER: admin_panel_user + POSTGRES_PASSWORD: admin_panel_password + PGDATA: /var/lib/postgresql/data/pgdata + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "3031:5432" + networks: + - admin_panel_network + restart: unless-stopped + + pgadmin: + image: dpage/pgadmin4:latest + container_name: admin_panel_pgadmin + environment: + PGADMIN_DEFAULT_EMAIL: admin@admin.com + PGADMIN_DEFAULT_PASSWORD: admin_panel_password + PGADMIN_CONFIG_SERVER_MODE: 'False' + volumes: + - pgadmin_data:/var/lib/pgadmin + ports: + - "3032:80" + networks: + - admin_panel_network + depends_on: + - postgres + restart: unless-stopped + + web: + build: . + container_name: admin_panel_web + ports: + - "3030:3030" + volumes: + - .:/app + - bundle_cache:/usr/local/bundle + environment: + - RAILS_ENV=development + - RAILS_SERVE_STATIC_FILES=true + - RAILS_LOG_TO_STDOUT=true + - DATABASE_URL=postgresql://admin_panel_user:admin_panel_password@postgres:5432/northwind + - POSTGRES_HOST=postgres + - POSTGRES_DB=northwind + - POSTGRES_USER=admin_panel_user + - POSTGRES_PASSWORD=admin_panel_password + - FOREST_ENV_SECRET=development_secret_key_change_in_production + - FOREST_AUTH_SECRET=development_auth_key_change_in_production + - DISABLE_PRY=true + stdin_open: true + tty: true + user: rails + working_dir: /app + networks: + - admin_panel_network + depends_on: + - postgres + command: > + bash -c " + bundle install && + dockerize -wait tcp://postgres:5432 -timeout 60s && + bundle exec rails db:create db:migrate db:seed && + bin/rails spina:install:migrations && + bin/rails spina:silent_first_deploy && + bin/rails spina:first_deploy && + + bundle exec rails server -b 0.0.0.0 -p 3030 + " + env_file: .env + +volumes: + bundle_cache: + postgres_data: + pgadmin_data: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..798869b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,83 @@ +version: '3.8' + +networks: + admin_panel_network: + driver: bridge + +services: + postgres: + image: postgres:15 + container_name: admin_panel_postgres + environment: + POSTGRES_DB: northwind + POSTGRES_USER: admin_panel_user + POSTGRES_PASSWORD: admin_panel_password + PGDATA: /var/lib/postgresql/data/pgdata + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "3031:5432" + networks: + - admin_panel_network + restart: unless-stopped + + pgadmin: + image: dpage/pgadmin4:latest + container_name: admin_panel_pgadmin + environment: + PGADMIN_DEFAULT_EMAIL: admin@admin.com + PGADMIN_DEFAULT_PASSWORD: admin_panel_password + PGADMIN_CONFIG_SERVER_MODE: 'False' + volumes: + - pgadmin_data:/var/lib/pgadmin + ports: + - "3032:80" + networks: + - admin_panel_network + depends_on: + - postgres + restart: unless-stopped + profiles: + - dev + - development + + web: + build: . + container_name: admin_panel_web + ports: + - "3030:3030" + volumes: + - .:/app + - bundle_cache:/usr/local/bundle + environment: + - RAILS_ENV=development + - RAILS_SERVE_STATIC_FILES=true + - RAILS_LOG_TO_STDOUT=true + - DATABASE_URL=postgresql://admin_panel_user:admin_panel_password@postgres:5432/northwind + - POSTGRES_HOST=postgres + - POSTGRES_DB=northwind + - POSTGRES_USER=admin_panel_user + - POSTGRES_PASSWORD=admin_panel_password + - FOREST_ENV_SECRET=development_secret_key_change_in_production + - FOREST_AUTH_SECRET=development_auth_key_change_in_production + stdin_open: true + tty: true + user: rails + working_dir: /app + networks: + - admin_panel_network + depends_on: + - postgres + command: > + bash -c " + bundle install && + dockerize -wait tcp://postgres:5432 -timeout 60s && + bundle exec rails db:create db:migrate && + bundle exec rails server -b 0.0.0.0 -p 3030 + " + env_file: .env + +volumes: + bundle_cache: + postgres_data: + pgadmin_data: \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..9663072 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +# Remover servidor PID existente se existir +if [ -f tmp/pids/server.pid ]; then + rm tmp/pids/server.pid +fi + +# Executar comando passado para o script +exec "$@" \ No newline at end of file