panda-core 0.2.4 → 0.6.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/tailwind/application.css +199 -7
  3. data/app/assets/tailwind/tailwind.config.js +8 -0
  4. data/app/components/panda/core/UI/badge.rb +107 -0
  5. data/app/components/panda/core/UI/button.rb +110 -0
  6. data/app/components/panda/core/UI/card.rb +88 -0
  7. data/app/components/panda/core/admin/breadcrumb_component.rb +133 -0
  8. data/app/components/panda/core/admin/button_component.rb +46 -28
  9. data/app/components/panda/core/admin/container_component.rb +75 -4
  10. data/app/components/panda/core/admin/file_gallery_component.rb +157 -0
  11. data/app/components/panda/core/admin/flash_message_component.rb +98 -15
  12. data/app/components/panda/core/admin/form_error_component.rb +48 -0
  13. data/app/components/panda/core/admin/form_input_component.rb +50 -0
  14. data/app/components/panda/core/admin/form_select_component.rb +68 -0
  15. data/app/components/panda/core/admin/heading_component.rb +53 -24
  16. data/app/components/panda/core/admin/page_header_component.rb +107 -0
  17. data/app/components/panda/core/admin/panel_component.rb +33 -4
  18. data/app/components/panda/core/admin/slideover_component.rb +66 -4
  19. data/app/components/panda/core/admin/statistics_component.rb +19 -0
  20. data/app/components/panda/core/admin/tab_bar_component.rb +101 -0
  21. data/app/components/panda/core/admin/table_component.rb +92 -11
  22. data/app/components/panda/core/admin/tag_component.rb +58 -16
  23. data/app/components/panda/core/admin/user_activity_component.rb +43 -0
  24. data/app/components/panda/core/admin/user_display_component.rb +77 -0
  25. data/app/components/panda/core/base.rb +122 -0
  26. data/app/controllers/panda/core/admin/base_controller.rb +68 -0
  27. data/app/controllers/panda/core/admin/dashboard_controller.rb +5 -3
  28. data/app/controllers/panda/core/admin/my_profile_controller.rb +4 -4
  29. data/app/controllers/panda/core/admin/sessions_controller.rb +15 -8
  30. data/app/controllers/panda/core/admin/test_sessions_controller.rb +60 -0
  31. data/app/helpers/panda/core/asset_helper.rb +31 -5
  32. data/app/helpers/panda/core/sessions_helper.rb +27 -2
  33. data/app/javascript/panda/core/application.js +8 -1
  34. data/app/javascript/panda/core/controllers/alert_controller.js +38 -0
  35. data/app/javascript/panda/core/controllers/index.js +3 -3
  36. data/app/javascript/panda/core/controllers/toggle_controller.js +41 -0
  37. data/app/javascript/panda/core/tailwindplus-elements.js +31 -0
  38. data/app/javascript/panda/core/vendor/@hotwired--stimulus.js +4 -0
  39. data/app/javascript/panda/core/vendor/@hotwired--turbo.js +160 -0
  40. data/app/javascript/panda/core/vendor/@rails--actioncable--src.js +4 -0
  41. data/app/models/panda/core/user.rb +61 -14
  42. data/app/services/panda/core/attach_avatar_service.rb +67 -0
  43. data/app/views/layouts/panda/core/admin.html.erb +40 -3
  44. data/app/views/layouts/panda/core/admin_simple.html.erb +6 -0
  45. data/app/views/panda/core/admin/dashboard/_default_content.html.erb +4 -4
  46. data/app/views/panda/core/admin/dashboard/show.html.erb +2 -2
  47. data/app/views/panda/core/admin/my_profile/edit.html.erb +36 -25
  48. data/app/views/panda/core/admin/sessions/new.html.erb +9 -10
  49. data/app/views/panda/core/admin/shared/_breadcrumbs.html.erb +27 -34
  50. data/app/views/panda/core/admin/shared/_flash.html.erb +4 -30
  51. data/app/views/panda/core/admin/shared/_sidebar.html.erb +41 -20
  52. data/app/views/panda/core/shared/_header.html.erb +13 -5
  53. data/config/importmap.rb +19 -6
  54. data/config/routes.rb +10 -3
  55. data/db/migrate/20250810120000_add_current_theme_to_panda_core_users.rb +7 -0
  56. data/db/migrate/20250811120000_add_oauth_avatar_url_to_panda_core_users.rb +7 -0
  57. data/lib/panda/core/asset_loader.rb +23 -8
  58. data/lib/panda/core/configuration.rb +12 -9
  59. data/lib/panda/core/debug.rb +47 -0
  60. data/lib/panda/core/engine.rb +55 -9
  61. data/lib/panda/core/services/base_service.rb +19 -4
  62. data/lib/panda/core/version.rb +1 -1
  63. data/lib/panda/core.rb +2 -0
  64. data/lib/tasks/panda_core_users.rake +158 -0
  65. metadata +103 -14
  66. data/app/components/panda/core/admin/container_component.html.erb +0 -12
  67. data/app/components/panda/core/admin/flash_message_component.html.erb +0 -31
  68. data/app/components/panda/core/admin/panel_component.html.erb +0 -7
  69. data/app/components/panda/core/admin/slideover_component.html.erb +0 -9
  70. data/app/components/panda/core/admin/table_component.html.erb +0 -29
  71. data/app/controllers/panda/core/admin_controller.rb +0 -30
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Panda
4
+ module Core
5
+ module Debug
6
+ class << self
7
+ # Check if debug mode is enabled via PANDA_DEBUG environment variable
8
+ def enabled?
9
+ ENV["PANDA_DEBUG"].to_s.downcase == "true" || ENV["PANDA_DEBUG"] == "1"
10
+ end
11
+
12
+ # Log a debug message if debug mode is enabled
13
+ def log(message, prefix: "PANDA")
14
+ return unless enabled?
15
+
16
+ timestamp = Time.current.strftime("%Y-%m-%d %H:%M:%S")
17
+ puts "[#{prefix} DEBUG #{timestamp}] #{message}"
18
+ end
19
+
20
+ # Log an object with pretty printing (using awesome_print if available)
21
+ def inspect(object, label: nil, prefix: "PANDA")
22
+ return unless enabled?
23
+
24
+ timestamp = Time.current.strftime("%Y-%m-%d %H:%M:%S")
25
+ header = label ? "#{label}: " : ""
26
+
27
+ puts "\n[#{prefix} DEBUG #{timestamp}] #{header}"
28
+ if defined?(AwesomePrint)
29
+ ap object
30
+ else
31
+ pp object
32
+ end
33
+ puts
34
+ end
35
+
36
+ # Enable HTTP debugging for Net::HTTP requests
37
+ def enable_http_debug!
38
+ return unless enabled? || ENV["DEBUG_HTTP"].to_s.downcase == "true"
39
+
40
+ require "net/http"
41
+ Net::HTTP.set_debug_output($stdout)
42
+ log("HTTP debugging enabled - all HTTP requests will be logged")
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -13,21 +13,32 @@ module Panda
13
13
  config.eager_load_namespaces << Panda::Core::Engine
14
14
 
15
15
  # Add engine's app directories to autoload paths
16
+ # Note: Only add the root directories, not nested subdirectories
17
+ # Zeitwerk will automatically discover nested modules from these roots
16
18
  config.autoload_paths += Dir[root.join("app", "models")]
17
19
  config.autoload_paths += Dir[root.join("app", "controllers")]
18
20
  config.autoload_paths += Dir[root.join("app", "builders")]
19
21
  config.autoload_paths += Dir[root.join("app", "components")]
22
+ config.autoload_paths += Dir[root.join("app", "services")]
20
23
 
21
24
  # Make files in public available to the main app (e.g. /panda-core-assets/panda-logo.png)
22
- config.app_middleware.use(
23
- Rack::Static,
25
+ config.middleware.use Rack::Static,
24
26
  urls: ["/panda-core-assets"],
25
27
  root: Panda::Core::Engine.root.join("public"),
26
28
  header_rules: [
27
29
  # Disable caching in development for instant CSS updates
28
30
  [:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000"}]
29
31
  ]
30
- )
32
+
33
+ # Make JavaScript files available for importmap
34
+ # Serve from app/javascript with proper MIME types
35
+ config.middleware.use Rack::Static,
36
+ urls: ["/panda", "/panda/core"],
37
+ root: Panda::Core::Engine.root.join("app/javascript"),
38
+ header_rules: [
39
+ [:all, {"Cache-Control" => Rails.env.development? ? "no-cache, no-store, must-revalidate" : "public, max-age=31536000",
40
+ "Content-Type" => "text/javascript; charset=utf-8"}]
41
+ ]
31
42
 
32
43
  config.generators do |g|
33
44
  g.test_framework :rspec
@@ -43,7 +54,7 @@ module Panda
43
54
  end
44
55
  end
45
56
 
46
- initializer "panda_core.configuration" do |app|
57
+ initializer "panda_core.config" do |app|
47
58
  # Configuration is already initialized with defaults in Configuration class
48
59
  end
49
60
 
@@ -64,18 +75,18 @@ module Panda
64
75
  end
65
76
  end
66
77
 
67
-
68
78
  initializer "panda_core.omniauth" do |app|
69
79
  # Mount OmniAuth at configurable admin path
70
80
  app.middleware.use OmniAuth::Builder do
71
81
  # Configure OmniAuth to use the configured admin path
72
82
  configure do |config|
73
- config.path_prefix = "#{Panda::Core.configuration.admin_path}/auth"
74
- # Allow POST requests for request phase (required for CSRF protection)
75
- config.allowed_request_methods = [:get, :post]
83
+ config.path_prefix = "#{Panda::Core.config.admin_path}/auth"
84
+ # POST-only for CSRF protection (CVE-2015-9284)
85
+ # All login forms use POST via form_tag method: "post"
86
+ config.allowed_request_methods = [:post]
76
87
  end
77
88
 
78
- Panda::Core.configuration.authentication_providers.each do |provider_name, settings|
89
+ Panda::Core.config.authentication_providers.each do |provider_name, settings|
79
90
  # Build provider options, allowing custom path name override
80
91
  provider_options = settings[:options] || {}
81
92
 
@@ -97,6 +108,41 @@ module Panda
97
108
  end
98
109
  end
99
110
  end
111
+
112
+ # Load Phlex base component after Rails application is initialized
113
+ # This ensures Rails.application.routes is available
114
+ initializer "panda_core.phlex_base", after: :load_config_initializers do
115
+ require "phlex"
116
+ require "phlex-rails"
117
+ require "literal"
118
+ require "tailwind_merge"
119
+
120
+ # Load the base component
121
+ require root.join("app/components/panda/core/base")
122
+ end
123
+
124
+ # Set up ViewComponent and Lookbook previews
125
+ initializer "panda_core.view_component" do |app|
126
+ app.config.view_component.preview_paths ||= []
127
+ app.config.view_component.preview_paths << root.join("spec/components/previews")
128
+
129
+ # Add preview directories to autoload paths in development
130
+ if Rails.env.development?
131
+ # Handle frozen autoload_paths array
132
+ if app.config.autoload_paths.frozen?
133
+ app.config.autoload_paths = app.config.autoload_paths.dup
134
+ end
135
+ app.config.autoload_paths << root.join("spec/components/previews")
136
+ end
137
+ end
138
+
139
+ # Create AdminController alias after controllers are loaded
140
+ # This allows other gems to inherit from Panda::Core::AdminController
141
+ initializer "panda_core.admin_controller_alias", after: :load_config_initializers do
142
+ ActiveSupport.on_load(:action_controller_base) do
143
+ Panda::Core.const_set(:AdminController, Panda::Core::Admin::BaseController) unless Panda::Core.const_defined?(:AdminController)
144
+ end
145
+ end
100
146
  end
101
147
  end
102
148
  end
@@ -2,18 +2,33 @@ module Panda
2
2
  module Core
3
3
  module Services
4
4
  class BaseService
5
- def self.call(*args)
6
- new(*args).call
5
+ # Simple result object for service responses
6
+ class Result
7
+ attr_reader :payload, :errors
8
+
9
+ def initialize(success:, payload: {}, errors: nil)
10
+ @success = success
11
+ @payload = payload
12
+ @errors = errors
13
+ end
14
+
15
+ def success?
16
+ @success
17
+ end
18
+ end
19
+
20
+ def self.call(**kwargs)
21
+ new(**kwargs).call
7
22
  end
8
23
 
9
24
  private
10
25
 
11
26
  def success(payload = {})
12
- OpenStruct.new(success?: true, payload: payload)
27
+ Result.new(success: true, payload: payload)
13
28
  end
14
29
 
15
30
  def failure(errors)
16
- OpenStruct.new(success?: false, errors: errors)
31
+ Result.new(success: false, errors: errors)
17
32
  end
18
33
  end
19
34
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Panda
4
4
  module Core
5
- VERSION = "0.2.4"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
data/lib/panda/core.rb CHANGED
@@ -13,4 +13,6 @@ end
13
13
  require_relative "core/version"
14
14
  require_relative "core/configuration"
15
15
  require_relative "core/asset_loader"
16
+ require_relative "core/debug"
17
+ require_relative "core/services/base_service"
16
18
  require_relative "core/engine" if defined?(Rails)
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :panda do
4
+ namespace :core do
5
+ namespace :users do
6
+ desc "List all users with their admin status"
7
+ task list: :environment do
8
+ users = Panda::Core::User.all.order(:email)
9
+
10
+ if users.empty?
11
+ puts "No users found."
12
+ else
13
+ puts "\nUsers:"
14
+ puts "-" * 80
15
+ users.each do |user|
16
+ admin_badge = user.admin? ? "[ADMIN]" : ""
17
+ puts "#{user.email.ljust(40)} #{admin_badge}"
18
+ end
19
+ puts "-" * 80
20
+ puts "Total: #{users.count} (#{Panda::Core::User.admins.count} admins)"
21
+ end
22
+ end
23
+
24
+ desc "Grant admin privileges to a user by email (EMAIL=user@example.com)"
25
+ task grant_admin: :environment do
26
+ email = ENV["EMAIL"]
27
+
28
+ unless email
29
+ puts "Error: EMAIL parameter is required"
30
+ puts "Usage: rails panda:core:users:grant_admin EMAIL=user@example.com"
31
+ exit 1
32
+ end
33
+
34
+ user = Panda::Core::User.find_by(email: email.downcase)
35
+
36
+ unless user
37
+ puts "Error: User with email '#{email}' not found"
38
+ puts "\nExisting users:"
39
+ Panda::Core::User.all.each do |u|
40
+ puts " - #{u.email}"
41
+ end
42
+ exit 1
43
+ end
44
+
45
+ if user.admin?
46
+ puts "User '#{user.email}' is already an admin"
47
+ else
48
+ user.update!(is_admin: true)
49
+ puts "✓ Granted admin privileges to '#{user.email}'"
50
+ end
51
+ end
52
+
53
+ desc "Revoke admin privileges from a user by email (EMAIL=user@example.com)"
54
+ task revoke_admin: :environment do
55
+ email = ENV["EMAIL"]
56
+
57
+ unless email
58
+ puts "Error: EMAIL parameter is required"
59
+ puts "Usage: rails panda:core:users:revoke_admin EMAIL=user@example.com"
60
+ exit 1
61
+ end
62
+
63
+ user = Panda::Core::User.find_by(email: email.downcase)
64
+
65
+ unless user
66
+ puts "Error: User with email '#{email}' not found"
67
+ exit 1
68
+ end
69
+
70
+ if user.admin?
71
+ # Safety check: prevent revoking the last admin
72
+ if Panda::Core::User.admins.count == 1
73
+ puts "Error: Cannot revoke admin privileges from the last admin user"
74
+ puts "Please create another admin first"
75
+ exit 1
76
+ end
77
+
78
+ user.update!(is_admin: false)
79
+ puts "✓ Revoked admin privileges from '#{user.email}'"
80
+ else
81
+ puts "User '#{user.email}' is not an admin"
82
+ end
83
+ end
84
+
85
+ desc "Create a new admin user (EMAIL=user@example.com FIRSTNAME=John LASTNAME=Doe)"
86
+ task create_admin: :environment do
87
+ email = ENV["EMAIL"]
88
+ firstname = ENV["FIRSTNAME"] || "Admin"
89
+ lastname = ENV["LASTNAME"] || "User"
90
+
91
+ unless email
92
+ puts "Error: EMAIL parameter is required"
93
+ puts "Usage: rails panda:core:users:create_admin EMAIL=user@example.com FIRSTNAME=John LASTNAME=Doe"
94
+ exit 1
95
+ end
96
+
97
+ existing_user = Panda::Core::User.find_by(email: email.downcase)
98
+
99
+ if existing_user
100
+ puts "Error: User with email '#{email}' already exists"
101
+ if existing_user.admin?
102
+ puts "This user is already an admin"
103
+ else
104
+ puts "Use 'rails panda:core:users:grant_admin EMAIL=#{email}' to make them an admin"
105
+ end
106
+ exit 1
107
+ end
108
+
109
+ # Build attributes hash based on schema
110
+ attributes = {
111
+ email: email.downcase,
112
+ is_admin: true
113
+ }
114
+
115
+ # Add name attributes based on schema
116
+ if Panda::Core::User.column_names.include?("name")
117
+ attributes[:name] = "#{firstname} #{lastname}".strip
118
+ elsif Panda::Core::User.column_names.include?("firstname") && Panda::Core::User.column_names.include?("lastname")
119
+ attributes[:firstname] = firstname
120
+ attributes[:lastname] = lastname
121
+ end
122
+
123
+ user = Panda::Core::User.create!(attributes)
124
+ puts "✓ Created admin user '#{user.email}'"
125
+ puts " Name: #{user.name}" if user.respond_to?(:name)
126
+ puts " Admin: #{user.admin?}"
127
+ end
128
+
129
+ desc "Delete a user by email (EMAIL=user@example.com)"
130
+ task delete: :environment do
131
+ email = ENV["EMAIL"]
132
+
133
+ unless email
134
+ puts "Error: EMAIL parameter is required"
135
+ puts "Usage: rails panda:core:users:delete EMAIL=user@example.com"
136
+ exit 1
137
+ end
138
+
139
+ user = Panda::Core::User.find_by(email: email.downcase)
140
+
141
+ unless user
142
+ puts "Error: User with email '#{email}' not found"
143
+ exit 1
144
+ end
145
+
146
+ # Safety check: prevent deleting the last admin
147
+ if user.admin? && Panda::Core::User.admins.count == 1
148
+ puts "Error: Cannot delete the last admin user"
149
+ puts "Please create another admin first"
150
+ exit 1
151
+ end
152
+
153
+ user.destroy!
154
+ puts "✓ Deleted user '#{email}'"
155
+ end
156
+ end
157
+ end
158
+ end
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: panda-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
- - Tasty Bamboo
7
+ - Otaina Limited
8
8
  - James Inman
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2025-10-27 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: importmap-rails
@@ -165,6 +164,62 @@ dependencies:
165
164
  - - ">="
166
165
  - !ruby/object:Gem::Version
167
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: phlex
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '2.3'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '2.3'
181
+ - !ruby/object:Gem::Dependency
182
+ name: phlex-rails
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '2.3'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '2.3'
195
+ - !ruby/object:Gem::Dependency
196
+ name: literal
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '1.8'
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '1.8'
209
+ - !ruby/object:Gem::Dependency
210
+ name: tailwind_merge
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: '1.3'
216
+ type: :runtime
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: '1.3'
168
223
  - !ruby/object:Gem::Dependency
169
224
  name: pg
170
225
  requirement: !ruby/object:Gem::Requirement
@@ -193,6 +248,20 @@ dependencies:
193
248
  - - ">="
194
249
  - !ruby/object:Gem::Version
195
250
  version: '0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: rails-controller-testing
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - ">="
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
196
265
  - !ruby/object:Gem::Dependency
197
266
  name: capybara
198
267
  requirement: !ruby/object:Gem::Requirement
@@ -364,7 +433,7 @@ dependencies:
364
433
  description: Shared development tools, configurations, and utilities for Panda CMS
365
434
  and its related projects
366
435
  email:
367
- - bamboo@pandacms.io
436
+ - james@otaina.co.uk
368
437
  executables: []
369
438
  extensions: []
370
439
  extra_rdoc_files: []
@@ -375,36 +444,54 @@ files:
375
444
  - app/assets/tailwind/application.css
376
445
  - app/assets/tailwind/tailwind.config.js
377
446
  - app/builders/panda/core/form_builder.rb
447
+ - app/components/panda/core/UI/badge.rb
448
+ - app/components/panda/core/UI/button.rb
449
+ - app/components/panda/core/UI/card.rb
450
+ - app/components/panda/core/admin/breadcrumb_component.rb
378
451
  - app/components/panda/core/admin/button_component.rb
379
- - app/components/panda/core/admin/container_component.html.erb
380
452
  - app/components/panda/core/admin/container_component.rb
381
- - app/components/panda/core/admin/flash_message_component.html.erb
453
+ - app/components/panda/core/admin/file_gallery_component.rb
382
454
  - app/components/panda/core/admin/flash_message_component.rb
455
+ - app/components/panda/core/admin/form_error_component.rb
456
+ - app/components/panda/core/admin/form_input_component.rb
457
+ - app/components/panda/core/admin/form_select_component.rb
383
458
  - app/components/panda/core/admin/heading_component.rb
384
- - app/components/panda/core/admin/panel_component.html.erb
459
+ - app/components/panda/core/admin/page_header_component.rb
385
460
  - app/components/panda/core/admin/panel_component.rb
386
- - app/components/panda/core/admin/slideover_component.html.erb
387
461
  - app/components/panda/core/admin/slideover_component.rb
388
- - app/components/panda/core/admin/table_component.html.erb
462
+ - app/components/panda/core/admin/statistics_component.rb
463
+ - app/components/panda/core/admin/tab_bar_component.rb
389
464
  - app/components/panda/core/admin/table_component.rb
390
465
  - app/components/panda/core/admin/tag_component.rb
466
+ - app/components/panda/core/admin/user_activity_component.rb
467
+ - app/components/panda/core/admin/user_display_component.rb
468
+ - app/components/panda/core/base.rb
391
469
  - app/constraints/panda/core/admin_constraint.rb
470
+ - app/controllers/panda/core/admin/base_controller.rb
392
471
  - app/controllers/panda/core/admin/dashboard_controller.rb
393
472
  - app/controllers/panda/core/admin/my_profile_controller.rb
394
473
  - app/controllers/panda/core/admin/sessions_controller.rb
395
- - app/controllers/panda/core/admin_controller.rb
474
+ - app/controllers/panda/core/admin/test_sessions_controller.rb
396
475
  - app/controllers/panda/core/application_controller.rb
397
476
  - app/helpers/panda/core/asset_helper.rb
398
477
  - app/helpers/panda/core/sessions_helper.rb
399
478
  - app/javascript/panda/core/application.js
479
+ - app/javascript/panda/core/controllers/alert_controller.js
400
480
  - app/javascript/panda/core/controllers/index.js
401
481
  - app/javascript/panda/core/controllers/theme_form_controller.js
482
+ - app/javascript/panda/core/controllers/toggle_controller.js
402
483
  - app/javascript/panda/core/tailwindcss-stimulus-components.js
484
+ - app/javascript/panda/core/tailwindplus-elements.js
485
+ - app/javascript/panda/core/vendor/@hotwired--stimulus.js
486
+ - app/javascript/panda/core/vendor/@hotwired--turbo.js
487
+ - app/javascript/panda/core/vendor/@rails--actioncable--src.js
403
488
  - app/models/panda/core/application_record.rb
404
489
  - app/models/panda/core/breadcrumb.rb
405
490
  - app/models/panda/core/current.rb
406
491
  - app/models/panda/core/user.rb
492
+ - app/services/panda/core/attach_avatar_service.rb
407
493
  - app/views/layouts/panda/core/admin.html.erb
494
+ - app/views/layouts/panda/core/admin_simple.html.erb
408
495
  - app/views/panda/core/admin/dashboard/_default_content.html.erb
409
496
  - app/views/panda/core/admin/dashboard/show.html.erb
410
497
  - app/views/panda/core/admin/my_profile/edit.html.erb
@@ -419,6 +506,8 @@ files:
419
506
  - config/initializers/panda_core.rb
420
507
  - config/routes.rb
421
508
  - db/migrate/20250809000001_create_panda_core_users.rb
509
+ - db/migrate/20250810120000_add_current_theme_to_panda_core_users.rb
510
+ - db/migrate/20250811120000_add_oauth_avatar_url_to_panda_core_users.rb
422
511
  - lib/generators/panda/core/authentication/templates/reek_spec.rb
423
512
  - lib/generators/panda/core/dev_tools/USAGE
424
513
  - lib/generators/panda/core/dev_tools/templates/lefthook.yml
@@ -433,6 +522,7 @@ files:
433
522
  - lib/panda/core/authentication.rb
434
523
  - lib/panda/core/component_registry.rb
435
524
  - lib/panda/core/configuration.rb
525
+ - lib/panda/core/debug.rb
436
526
  - lib/panda/core/engine.rb
437
527
  - lib/panda/core/media.rb
438
528
  - lib/panda/core/notifications.rb
@@ -451,6 +541,7 @@ files:
451
541
  - lib/tasks/panda/core/migrations.rake
452
542
  - lib/tasks/panda_core.rake
453
543
  - lib/tasks/panda_core_tasks.rake
544
+ - lib/tasks/panda_core_users.rake
454
545
  homepage: https://github.com/tastybamboo/panda-core
455
546
  licenses:
456
547
  - BSD-3-Clause
@@ -458,7 +549,6 @@ metadata:
458
549
  homepage_uri: https://github.com/tastybamboo/panda-core
459
550
  source_code_uri: https://github.com/tastybamboo/panda-core
460
551
  changelog_uri: https://github.com/tastybamboo/panda-core/blob/main/CHANGELOG.md
461
- post_install_message:
462
552
  rdoc_options: []
463
553
  require_paths:
464
554
  - lib
@@ -473,8 +563,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
473
563
  - !ruby/object:Gem::Version
474
564
  version: '0'
475
565
  requirements: []
476
- rubygems_version: 3.5.22
477
- signing_key:
566
+ rubygems_version: 3.6.9
478
567
  specification_version: 4
479
568
  summary: Core libraries and development tools for Tasty Bamboo projects
480
569
  test_files: []
@@ -1,12 +0,0 @@
1
- <main class="overflow-auto flex-1 h-full min-h-full max-h-full">
2
- <div class="overflow-auto px-2 pt-4 mx-auto sm:px-6 lg:px-6">
3
- <%= heading %>
4
- <%= tab_bar %>
5
- <section class="flex-auto h-[calc(100vh-10rem)]">
6
- <div class="flex-1 mt-4 w-full h-full">
7
- <%= content %>
8
- </div>
9
- <%= slideover %>
10
- </section>
11
- </div>
12
- </main>
@@ -1,31 +0,0 @@
1
- <div class="fixed top-2 right-2 z-[9999] p-2 space-y-4 w-full max-w-sm sm:items-end"
2
- data-controller="alert"
3
- <% if @temporary %> data-alert-dismiss-after-value="3000"<% end %>
4
- data-transition-enter="ease-in-out duration-500"
5
- data-transition-enter-from="translate-x-full opacity-0"
6
- data-transition-enter-to="translate-x-0 opacity-100"
7
- data-transition-leave="ease-in-out duration-500"
8
- data-transition-leave-from="translate-x-0 opacity-100"
9
- data-transition-leave-to="translate-x-full opacity-0">
10
- <div class="overflow-hidden w-full max-w-sm bg-white rounded-lg ring-1 ring-black ring-opacity-5 shadow-lg">
11
- <div class="p-4">
12
- <div class="flex items-start">
13
- <div class="flex-shrink-0">
14
- <i class="fa-regular text-xl <%= icon_css %> <%= text_colour_css %>"></i>
15
- </div>
16
- <div class="flex-1 pt-0.5 ml-3 w-0">
17
- <p class="mb-1 text-sm font-medium flash-message-title <%= text_colour_css %>"><%= kind.to_s.titleize %></p>
18
- <p class="mt-1 mb-0 text-sm text-gray-500 flash-message-text"><%= message %></p>
19
- </div>
20
- <div class="flex flex-shrink-0 ml-4">
21
- <button data-action="alert#close" type="button" class="inline-flex text-gray-400 bg-white rounded-md transition duration-150 ease-in-out hover:text-gray-500 focus:ring-2 focus:ring-offset-2 focus:outline-none focus:ring-sky-500">
22
- <span class="sr-only">Close</span>
23
- <svg class="w-5 h-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
24
- <path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
25
- </svg>
26
- </button>
27
- </div>
28
- </div>
29
- </div>
30
- </div>
31
- </div>
@@ -1,7 +0,0 @@
1
- <div class="col-span-3 mt-5 rounded-lg shadow-md bg-gray-500 shadow-inherit/20">
2
- <%= heading %>
3
-
4
- <div class="p-4 text-black bg-white rounded-b-lg">
5
- <%= content %>
6
- </div>
7
- </div>
@@ -1,9 +0,0 @@
1
- <% content_for :sidebar do %>
2
- <aside class="hidden overflow-y-auto w-96 h-full bg-white lg:block">
3
- <%= content %>
4
- </aside>
5
- <% end %>
6
-
7
- <% content_for :sidebar_title do %>
8
- <%= title %>
9
- <% end %>
@@ -1,29 +0,0 @@
1
- <div class="table overflow-x-auto mb-12 w-full rounded-lg border border-gray-500">
2
- <div class="table-header-group">
3
- <div class="table-row text-base font-medium text-white bg-gray-500">
4
- <% columns.each_with_index do |column, i| %>
5
- <div class="table-cell sticky top-0 z-10 p-4 <% if i.zero? %>rounded-tl-md<% elsif i == columns.size - 1 %>rounded-tr-md<% end %>"><%= column.label %></div>
6
- <% end %>
7
- </div>
8
- </div>
9
-
10
- <% if @rows.any? %>
11
- <div class="table-row-group">
12
- <% @rows.each do |row| %>
13
- <div class="table-row relative bg-gray-500/5 hover:bg-gray-500/20" data-post-id="<%= row.id %>">
14
- <% @columns.each do |column| %>
15
- <div class="table-cell py-5 px-3 h-20 text-sm align-middle whitespace-nowrap border-b border-gray-500/20">
16
- <%= view_context.capture(row, &column.cell) %>
17
- </div>
18
- <% end %>
19
- </div>
20
- <% end %>
21
- </div>
22
- </div>
23
- <% else %>
24
- </div>
25
- <div class="text-center mx-12 block border border-dashed py-12 rounded-lg">
26
- <h3 class="py-1 text-xl font-semibold text-gray-900">No <%= @term.pluralize %></h3>
27
- <p class="py-1 text-base text-gray-500">Get started by creating a new <%= @term %>.</p>
28
- </div>
29
- <% end %>