panda-core 0.4.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/tailwind/application.css +95 -0
  3. data/app/assets/tailwind/tailwind.config.js +8 -0
  4. data/app/builders/panda/core/form_builder.rb +163 -11
  5. data/app/components/panda/core/UI/button.rb +45 -24
  6. data/app/components/panda/core/admin/breadcrumb_component.rb +133 -0
  7. data/app/components/panda/core/admin/button_component.rb +27 -12
  8. data/app/components/panda/core/admin/container_component.rb +40 -5
  9. data/app/components/panda/core/admin/file_gallery_component.rb +157 -0
  10. data/app/components/panda/core/admin/flash_message_component.rb +54 -36
  11. data/app/components/panda/core/admin/heading_component.rb +28 -19
  12. data/app/components/panda/core/admin/page_header_component.rb +107 -0
  13. data/app/components/panda/core/admin/panel_component.rb +1 -1
  14. data/app/components/panda/core/admin/slideover_component.rb +92 -4
  15. data/app/components/panda/core/admin/table_component.rb +11 -11
  16. data/app/components/panda/core/admin/tag_component.rb +39 -2
  17. data/app/components/panda/core/admin/user_display_component.rb +4 -5
  18. data/app/controllers/panda/core/admin/my_profile_controller.rb +10 -3
  19. data/app/controllers/panda/core/admin/sessions_controller.rb +6 -2
  20. data/app/controllers/panda/core/admin/test_sessions_controller.rb +60 -0
  21. data/app/helpers/panda/core/asset_helper.rb +33 -5
  22. data/app/helpers/panda/core/sessions_helper.rb +26 -1
  23. data/app/javascript/panda/core/application.js +8 -1
  24. data/app/javascript/panda/core/controllers/alert_controller.js +38 -0
  25. data/app/javascript/panda/core/controllers/image_cropper_controller.js +158 -0
  26. data/app/javascript/panda/core/controllers/index.js +9 -3
  27. data/app/javascript/panda/core/controllers/navigation_toggle_controller.js +60 -0
  28. data/app/javascript/panda/core/controllers/toggle_controller.js +41 -0
  29. data/app/javascript/panda/core/tailwindplus-elements.js +31 -0
  30. data/app/models/panda/core/user.rb +60 -6
  31. data/app/services/panda/core/attach_avatar_service.rb +71 -0
  32. data/app/views/layouts/panda/core/admin.html.erb +39 -14
  33. data/app/views/layouts/panda/core/admin_simple.html.erb +1 -0
  34. data/app/views/panda/core/admin/dashboard/_default_content.html.erb +1 -1
  35. data/app/views/panda/core/admin/dashboard/show.html.erb +3 -3
  36. data/app/views/panda/core/admin/my_profile/edit.html.erb +26 -1
  37. data/app/views/panda/core/admin/sessions/new.html.erb +3 -4
  38. data/app/views/panda/core/admin/shared/_breadcrumbs.html.erb +14 -24
  39. data/app/views/panda/core/admin/shared/_sidebar.html.erb +69 -14
  40. data/app/views/panda/core/admin/shared/_slideover.html.erb +1 -1
  41. data/config/importmap.rb +20 -7
  42. data/config/routes.rb +10 -1
  43. data/db/migrate/20250811120000_add_oauth_avatar_url_to_panda_core_users.rb +7 -0
  44. data/lib/panda/core/asset_loader.rb +5 -2
  45. data/lib/panda/core/engine.rb +38 -28
  46. data/lib/panda/core/oauth_providers.rb +3 -3
  47. data/lib/panda/core/services/base_service.rb +19 -4
  48. data/lib/panda/core/version.rb +1 -1
  49. data/lib/panda/core.rb +1 -0
  50. data/lib/tasks/panda_core_users.rake +158 -0
  51. metadata +13 -69
  52. data/lib/generators/panda/core/authentication/templates/reek_spec.rb +0 -43
  53. data/lib/generators/panda/core/dev_tools/USAGE +0 -24
  54. data/lib/generators/panda/core/dev_tools/templates/lefthook.yml +0 -13
  55. data/lib/generators/panda/core/dev_tools/templates/spec_support_panda_core_helpers.rb +0 -18
  56. data/lib/generators/panda/core/dev_tools_generator.rb +0 -143
  57. data/lib/generators/panda/core/install_generator.rb +0 -41
  58. data/lib/generators/panda/core/templates/README +0 -25
  59. data/lib/generators/panda/core/templates/initializer.rb +0 -44
  60. data/lib/generators/panda/core/templates_generator.rb +0 -27
  61. data/lib/panda/core/testing/capybara_config.rb +0 -70
  62. data/lib/panda/core/testing/omniauth_helpers.rb +0 -52
  63. data/lib/panda/core/testing/rspec_config.rb +0 -72
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "panda/core/testing/rspec_config"
4
- require "panda/core/testing/omniauth_helpers"
5
- require "panda/core/testing/capybara_config"
6
-
7
- RSpec.configure do |config|
8
- # Apply Panda Core RSpec configuration
9
- Panda::Core::Testing::RSpecConfig.configure(config)
10
- Panda::Core::Testing::RSpecConfig.setup_matchers
11
-
12
- # Configure Capybara
13
- Panda::Core::Testing::CapybaraConfig.configure
14
-
15
- # Include helpers
16
- config.include Panda::Core::Testing::OmniAuthHelpers, type: :system
17
- config.include Panda::Core::Testing::CapybaraConfig::Helpers, type: :system
18
- end
@@ -1,143 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rails/generators"
4
-
5
- module Panda
6
- module Core
7
- module Generators
8
- class DevToolsGenerator < Rails::Generators::Base
9
- source_root File.expand_path("dev_tools/templates", __dir__)
10
-
11
- desc "Set up or update Panda Core development tools in your gem/application"
12
-
13
- class_option :force, type: :boolean, default: false,
14
- desc: "Overwrite existing files"
15
- class_option :skip_dependencies, type: :boolean, default: false,
16
- desc: "Skip adding dependencies to Gemfile"
17
-
18
- VERSION_FILE = ".panda-dev-tools-version"
19
- CURRENT_VERSION = "1.0.0"
20
-
21
- def check_for_updates
22
- if File.exist?(VERSION_FILE)
23
- installed_version = File.read(VERSION_FILE).strip
24
- if installed_version != CURRENT_VERSION
25
- say "Updating Panda Core dev tools from #{installed_version} to #{CURRENT_VERSION}", :yellow
26
- @updating = true
27
- else
28
- say "Panda Core dev tools are up to date (#{CURRENT_VERSION})", :green
29
- unless options[:force]
30
- say "Use --force to reinstall anyway"
31
- nil
32
- end
33
- end
34
- else
35
- say "Installing Panda Core dev tools #{CURRENT_VERSION}", :green
36
- @updating = false
37
- end
38
- end
39
-
40
- def copy_linting_configs
41
- say "Copying linting configurations..."
42
- copy_file ".standard.yml", force: options[:force] || @updating
43
- copy_file ".yamllint", force: options[:force] || @updating
44
- copy_file ".rspec", force: options[:force] || @updating
45
- copy_file "lefthook.yml", force: options[:force] || @updating
46
- end
47
-
48
- def copy_github_workflows
49
- say "Copying GitHub Actions workflows..."
50
- directory ".github", ".github", force: options[:force] || @updating
51
- end
52
-
53
- def create_version_file
54
- create_file VERSION_FILE, CURRENT_VERSION, force: true
55
- end
56
-
57
- def add_development_dependencies
58
- say "Adding development dependencies to gemspec..."
59
-
60
- if File.exist?("Gemfile")
61
- append_to_file "Gemfile" do
62
- <<~RUBY
63
-
64
- group :development, :test do
65
- # Panda Core development tools
66
- gem "standard"
67
- gem "brakeman"
68
- gem "bundler-audit"
69
- gem "yamllint"
70
- end
71
- RUBY
72
- end
73
- end
74
- end
75
-
76
- def create_spec_helper
77
- say "Creating spec helper with Panda Core testing configuration..."
78
-
79
- create_file "spec/support/panda_core_helpers.rb" do
80
- <<~RUBY
81
- # frozen_string_literal: true
82
-
83
- require 'panda/core/testing/rspec_config'
84
- require 'panda/core/testing/omniauth_helpers'
85
- require 'panda/core/testing/capybara_config'
86
-
87
- RSpec.configure do |config|
88
- # Apply Panda Core RSpec configuration
89
- Panda::Core::Testing::RSpecConfig.configure(config)
90
- Panda::Core::Testing::RSpecConfig.setup_matchers
91
-
92
- # Configure Capybara
93
- Panda::Core::Testing::CapybaraConfig.configure
94
-
95
- # Include helpers
96
- config.include Panda::Core::Testing::OmniAuthHelpers, type: :system
97
- config.include Panda::Core::Testing::CapybaraConfig::Helpers, type: :system
98
- end
99
- RUBY
100
- end
101
- end
102
-
103
- def add_rake_tasks
104
- say "Adding Panda Core rake tasks..."
105
-
106
- append_to_file "Rakefile" do
107
- <<~RUBY
108
-
109
- # Panda Core development tasks
110
- namespace :panda do
111
- desc "Run all linters"
112
- task :lint do
113
- sh "bundle exec standardrb"
114
- sh "yamllint -c .yamllint ."
115
- end
116
-
117
- desc "Run security checks"
118
- task :security do
119
- sh "bundle exec brakeman --quiet"
120
- sh "bundle exec bundle-audit --update"
121
- end
122
-
123
- desc "Run all quality checks"
124
- task quality: [:lint, :security]
125
- end
126
- RUBY
127
- end
128
- end
129
-
130
- def display_instructions
131
- say "\n✅ Panda Core development tools have been set up!", :green
132
- say "\nNext steps:"
133
- say " 1. Run 'bundle install' to install new dependencies"
134
- say " 2. Run 'bundle exec rake panda:quality' to check code quality"
135
- say " 3. Customize .github/workflows for your gem's needs"
136
- say " 4. Add 'require' statements to your spec_helper.rb or rails_helper.rb:"
137
- say " require 'support/panda_core_helpers'"
138
- say "\nFor more information, see: docs/development_tools.md"
139
- end
140
- end
141
- end
142
- end
143
- end
@@ -1,41 +0,0 @@
1
- module Panda
2
- module Core
3
- class InstallGenerator < Rails::Generators::Base
4
- include Rails::Generators::Migration
5
-
6
- source_root File.expand_path("templates", __dir__)
7
-
8
- namespace "panda:core:install"
9
-
10
- # Allow incompatible default types for Thor options
11
- def self.allow_incompatible_default_type!
12
- true
13
- end
14
-
15
- class_option :skip_migrations, type: :boolean, default: false,
16
- desc: "Skip migrations installation"
17
- class_option :orm, type: :string, default: "active_record",
18
- desc: "ORM to be used for migrations"
19
-
20
- def self.next_migration_number(dirname)
21
- next_migration_number = current_migration_number(dirname) + 1
22
- ActiveRecord::Migration.next_migration_number(next_migration_number)
23
- end
24
-
25
- def create_initializer
26
- template "initializer.rb", "config/initializers/panda.rb"
27
- end
28
-
29
- def mount_engine
30
- routes_file = File.join(destination_root, "config/routes.rb")
31
- return unless File.exist?(routes_file)
32
-
33
- route 'mount Panda::Core::Engine => "/"'
34
- end
35
-
36
- def show_readme
37
- readme "README" if behavior == :invoke
38
- end
39
- end
40
- end
41
- end
@@ -1,25 +0,0 @@
1
- ===============================================================================
2
-
3
- Panda Core has been installed!
4
-
5
- Next steps:
6
-
7
- 1. Install and run migrations:
8
-
9
- rails panda:core:install:migrations
10
- rails db:migrate
11
-
12
- 2. Configure authentication providers in config/initializers/panda.rb
13
- (Uncomment and configure the providers you want to use)
14
-
15
- 3. Add the required OAuth gems to your Gemfile:
16
-
17
- gem 'omniauth-google-oauth2' # For Google authentication
18
- gem 'omniauth-microsoft_graph' # For Microsoft authentication
19
- gem 'omniauth-github' # For GitHub authentication
20
-
21
- 4. Configure your OAuth credentials in Rails credentials:
22
-
23
- rails credentials:edit
24
-
25
- ===============================================================================
@@ -1,44 +0,0 @@
1
- Panda::Core.configure do |config|
2
- config.admin_path = "/admin"
3
-
4
- config.login_page_title = "Panda Admin"
5
- config.admin_title = "Panda Admin"
6
-
7
- # Configure authentication providers
8
- # Uncomment and configure the providers you want to use
9
- # Don't forget to add the corresponding gems (e.g., omniauth-google-oauth2)
10
- #
11
- # config.authentication_providers = {
12
- # google_oauth2: {
13
- # enabled: true,
14
- # name: "Google", # Display name for the button
15
- # icon: "google", # FontAwesome icon name (optional, auto-detected if not specified)
16
- # client_id: Rails.application.credentials.dig(:google, :client_id),
17
- # client_secret: Rails.application.credentials.dig(:google, :client_secret),
18
- # options: {
19
- # scope: "email,profile",
20
- # prompt: "select_account",
21
- # hd: "yourdomain.com" # Specify your domain here if you want to restrict admin logins
22
- # }
23
- # }
24
- # }
25
-
26
- # Configure the session token cookie name
27
- config.session_token_cookie = :panda_session
28
-
29
- # Configure the user class for the application
30
- config.user_class = "Panda::Core::User"
31
-
32
- # Configure the user identity class for the application
33
- config.user_identity_class = "Panda::Core::UserIdentity"
34
-
35
- # Configure the storage provider (default: :active_storage)
36
- # config.storage_provider = :active_storage
37
-
38
- # Configure the cache store (default: :memory_store)
39
- # config.cache_store = :memory_store
40
-
41
- # Configure EditorJS tools (optional)
42
- # config.editor_js_tools = []
43
- # config.editor_js_tool_config = {}
44
- end
@@ -1,27 +0,0 @@
1
- require "rails/generators"
2
- require "fileutils"
3
- require "pathname"
4
-
5
- module Panda
6
- module Core
7
- module Generators
8
- class TemplatesGenerator < Rails::Generators::Base
9
- include Thor::Actions
10
-
11
- desc "Copies shared configuration templates from panda_core"
12
-
13
- source_root File.expand_path("templates", __dir__)
14
-
15
- def copy_templates
16
- Dir.glob(File.join(self.class.source_root, "**/{.*,*}"), File::FNM_DOTMATCH).each do |file|
17
- next if File.directory?(file)
18
- next if File.basename(file) == "." || File.basename(file) == ".."
19
-
20
- relative_path = Pathname.new(file).relative_path_from(Pathname.new(self.class.source_root)).to_s
21
- copy_file relative_path
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "capybara/rspec"
4
-
5
- module Panda
6
- module Core
7
- module Testing
8
- module CapybaraConfig
9
- def self.configure
10
- Capybara.server = :puma, {Silent: true}
11
- Capybara.default_max_wait_time = 5
12
- Capybara.disable_animation = true
13
-
14
- # Register Chrome driver with sensible defaults
15
- if defined?(Cuprite)
16
- Capybara.register_driver :panda_chrome do |app|
17
- Cuprite::Driver.new(
18
- app,
19
- window_size: [1400, 1400],
20
- browser_options: {
21
- "no-sandbox": nil,
22
- "disable-gpu": nil,
23
- "disable-dev-shm-usage": nil
24
- },
25
- inspector: ENV["INSPECTOR"] == "true",
26
- headless: ENV["HEADLESS"] != "false"
27
- )
28
- end
29
-
30
- Capybara.javascript_driver = :panda_chrome
31
- Capybara.default_driver = :rack_test
32
- end
33
- end
34
-
35
- # Helper methods for system tests
36
- module Helpers
37
- def wait_for_ajax
38
- Timeout.timeout(Capybara.default_max_wait_time) do
39
- loop until page.evaluate_script("jQuery.active").zero?
40
- end
41
- rescue Timeout::Error
42
- # Ajax didn't finish, but continue anyway
43
- end
44
-
45
- def wait_for_turbo
46
- has_css?("html", wait: 0.1)
47
- return unless page.evaluate_script("typeof Turbo !== 'undefined'")
48
-
49
- page.evaluate_script("Turbo.session.drive = false")
50
- yield if block_given?
51
- page.evaluate_script("Turbo.session.drive = true")
52
- end
53
-
54
- def take_screenshot_on_failure
55
- return unless page.driver.respond_to?(:save_screenshot)
56
-
57
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
58
- filename = "screenshot_#{timestamp}_#{example.description.parameterize}.png"
59
- path = Rails.root.join("tmp", "screenshots", filename)
60
-
61
- FileUtils.mkdir_p(File.dirname(path))
62
- page.driver.save_screenshot(path)
63
-
64
- puts "\nScreenshot saved: #{path}"
65
- end
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Panda
4
- module Core
5
- module Testing
6
- module OmniAuthHelpers
7
- def mock_omniauth_login(email: "admin@example.com", name: "Test User", provider: :google_oauth2, uid: "123456789", is_admin: true)
8
- OmniAuth.config.test_mode = true
9
- OmniAuth.config.mock_auth[provider] = OmniAuth::AuthHash.new(
10
- provider: provider.to_s,
11
- uid: uid,
12
- info: {
13
- email: email,
14
- name: name,
15
- image: "https://example.com/avatar.jpg"
16
- }
17
- )
18
-
19
- # Create or update the user in the test database
20
- Panda::Core::User.find_or_create_by(email: email) do |u|
21
- # Split name into firstname and lastname
22
- parts = name.split(" ", 2)
23
- u.firstname = parts[0] || name
24
- u.lastname = parts[1] || ""
25
- u.admin = is_admin
26
- end
27
- end
28
-
29
- def login_as_admin(email: "admin@example.com", name: "Admin User")
30
- user = mock_omniauth_login(email: email, name: name, is_admin: true)
31
- visit "/admin/auth/google_oauth2"
32
- user
33
- end
34
-
35
- def login_as_user(email: "user@example.com", name: "Regular User")
36
- user = mock_omniauth_login(email: email, name: name, is_admin: false)
37
- visit "/admin/auth/google_oauth2"
38
- user
39
- end
40
-
41
- def logout
42
- visit "/admin/logout"
43
- end
44
-
45
- def mock_omniauth_failure(message = "Authentication failed")
46
- OmniAuth.config.test_mode = true
47
- OmniAuth.config.mock_auth[:google_oauth2] = :invalid_credentials
48
- end
49
- end
50
- end
51
- end
52
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Panda
4
- module Core
5
- module Testing
6
- module RSpecConfig
7
- def self.configure(config)
8
- # Add common RSpec configurations
9
- config.expect_with :rspec do |expectations|
10
- expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
- end
12
-
13
- config.mock_with :rspec do |mocks|
14
- mocks.verify_partial_doubles = true
15
- end
16
-
17
- config.shared_context_metadata_behavior = :apply_to_host_groups
18
- config.filter_run_when_matching :focus
19
- config.example_status_persistence_file_path = "spec/examples.txt"
20
- config.disable_monkey_patching!
21
-
22
- if config.files_to_run.one?
23
- config.default_formatter = "doc"
24
- end
25
-
26
- config.order = :random
27
- Kernel.srand config.seed
28
-
29
- # Database cleaner setup
30
- if defined?(DatabaseCleaner)
31
- config.before(:suite) do
32
- DatabaseCleaner.strategy = :transaction
33
- DatabaseCleaner.clean_with(:truncation)
34
- end
35
-
36
- config.around(:each) do |example|
37
- DatabaseCleaner.cleaning do
38
- example.run
39
- end
40
- end
41
- end
42
-
43
- # OmniAuth test mode
44
- if defined?(OmniAuth)
45
- config.before(:each) do
46
- OmniAuth.config.test_mode = true
47
- end
48
-
49
- config.after(:each) do
50
- OmniAuth.config.mock_auth.clear
51
- end
52
- end
53
- end
54
-
55
- # Common matchers for Panda gems
56
- def self.setup_matchers
57
- RSpec::Matchers.define :have_breadcrumb do |expected|
58
- match do |page|
59
- page.has_css?(".breadcrumb", text: expected)
60
- end
61
- end
62
-
63
- RSpec::Matchers.define :have_flash_message do |type, message|
64
- match do |page|
65
- page.has_css?(".flash-message.flash-#{type}", text: message)
66
- end
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end