solid_queue_dashboard 0.0.1 → 0.1.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +5 -1
  4. data/Procfile.dev +2 -0
  5. data/README.md +73 -21
  6. data/app/assets/javascripts/solid_queue_dashboard/alpine.js +5 -0
  7. data/app/assets/javascripts/solid_queue_dashboard/application.js +46 -0
  8. data/app/assets/stylesheets/solid_queue_dashboard/application.css +2049 -0
  9. data/app/assets/stylesheets/solid_queue_dashboard/tailwind.css +554 -0
  10. data/app/controllers/solid_queue_dashboard/appearance_controller.rb +8 -0
  11. data/app/controllers/solid_queue_dashboard/application_controller.rb +7 -0
  12. data/app/controllers/solid_queue_dashboard/dashboard_controller.rb +8 -0
  13. data/app/controllers/solid_queue_dashboard/jobs_controller.rb +42 -0
  14. data/app/controllers/solid_queue_dashboard/processes_controller.rb +28 -0
  15. data/app/controllers/solid_queue_dashboard/recurring_tasks_controller.rb +30 -0
  16. data/app/helpers/solid_queue_dashboard/appearance_helper.rb +7 -0
  17. data/app/helpers/solid_queue_dashboard/application_helper.rb +7 -0
  18. data/app/helpers/solid_queue_dashboard/icons_helper.rb +231 -0
  19. data/app/helpers/solid_queue_dashboard/jobs_helper.rb +51 -0
  20. data/app/helpers/solid_queue_dashboard/pagination_helper.rb +38 -0
  21. data/app/helpers/solid_queue_dashboard/processes_helper.rb +35 -0
  22. data/app/helpers/solid_queue_dashboard/recurring_tasks_helper.rb +33 -0
  23. data/app/views/layouts/solid_queue_dashboard/application.html.erb +21 -0
  24. data/app/views/solid_queue_dashboard/application/_flash_messages.html.erb +38 -0
  25. data/app/views/solid_queue_dashboard/application/_footer.html.erb +11 -0
  26. data/app/views/solid_queue_dashboard/application/_navbar.html.erb +50 -0
  27. data/app/views/solid_queue_dashboard/application/_pagination.html.erb +19 -0
  28. data/app/views/solid_queue_dashboard/dashboard/index.html.erb +41 -0
  29. data/app/views/solid_queue_dashboard/jobs/_filters.html.erb +87 -0
  30. data/app/views/solid_queue_dashboard/jobs/_table.html.erb +19 -0
  31. data/app/views/solid_queue_dashboard/jobs/_table_row.html.erb +82 -0
  32. data/app/views/solid_queue_dashboard/jobs/index.html.erb +57 -0
  33. data/app/views/solid_queue_dashboard/jobs/show.html.erb +192 -0
  34. data/app/views/solid_queue_dashboard/processes/_filters.html.erb +64 -0
  35. data/app/views/solid_queue_dashboard/processes/_table.html.erb +18 -0
  36. data/app/views/solid_queue_dashboard/processes/_table_row.html.erb +32 -0
  37. data/app/views/solid_queue_dashboard/processes/index.html.erb +27 -0
  38. data/app/views/solid_queue_dashboard/processes/show.html.erb +79 -0
  39. data/app/views/solid_queue_dashboard/recurring_tasks/_filters.html.erb +26 -0
  40. data/app/views/solid_queue_dashboard/recurring_tasks/_table.html.erb +19 -0
  41. data/app/views/solid_queue_dashboard/recurring_tasks/_table_row.html.erb +31 -0
  42. data/app/views/solid_queue_dashboard/recurring_tasks/index.html.erb +21 -0
  43. data/app/views/solid_queue_dashboard/recurring_tasks/show.html.erb +129 -0
  44. data/bun.lockb +0 -0
  45. data/config/routes.rb +18 -0
  46. data/lib/solid_queue_dashboard/configuration.rb +17 -0
  47. data/lib/solid_queue_dashboard/decorators/job_decorator.rb +63 -0
  48. data/lib/solid_queue_dashboard/decorators/jobs_decorator.rb +74 -0
  49. data/lib/solid_queue_dashboard/decorators/process_decorator.rb +13 -0
  50. data/lib/solid_queue_dashboard/decorators/processes_decorator.rb +15 -0
  51. data/lib/solid_queue_dashboard/decorators/recurring_task_decorator.rb +22 -0
  52. data/lib/solid_queue_dashboard/decorators/recurring_tasks_decorator.rb +26 -0
  53. data/lib/solid_queue_dashboard/engine.rb +13 -0
  54. data/lib/solid_queue_dashboard/job.rb +26 -0
  55. data/lib/solid_queue_dashboard/process.rb +24 -0
  56. data/lib/solid_queue_dashboard/recurring_task.rb +14 -0
  57. data/lib/solid_queue_dashboard/version.rb +1 -1
  58. data/lib/solid_queue_dashboard.rb +39 -1
  59. data/package.json +13 -0
  60. data/tailwind.config.js +83 -0
  61. metadata +73 -4
@@ -0,0 +1,21 @@
1
+ <div class="card">
2
+ <div class="card-header border-b">
3
+ <h2 class="card-title">Recurring Tasks</h2>
4
+
5
+ <div class="!mt-4">
6
+ <%= render "solid_queue_dashboard/recurring_tasks/filters" %>
7
+ </div>
8
+ </div>
9
+
10
+ <div class="card-content !p-0">
11
+ <% if @recurring_tasks.any? %>
12
+ <%= render "solid_queue_dashboard/recurring_tasks/table", recurring_tasks: @recurring_tasks %>
13
+ <% else %>
14
+ <div class="px-6 py-12">
15
+ <p class="text-muted-foreground text-center">
16
+ No recurring tasks found
17
+ </p>
18
+ </div>
19
+ <% end %>
20
+ </div>
21
+ </div>
@@ -0,0 +1,129 @@
1
+ <h1 class="text-4xl flex items-center gap-2">
2
+ <%= recurring_task_circle(@recurring_task.type, class: "size-4") %>
3
+
4
+ <span class="tracking-tighter font-bold">
5
+ Recurring Task #<%= @recurring_task.id %>
6
+ </span>
7
+ </h1>
8
+
9
+ <div class="card mt-6">
10
+ <div class="card-content pt-6 grid grid-cols-1 sm:grid-cols-2 sm:gap-16">
11
+ <div class="space-y-4">
12
+ <div class="info-line">
13
+ <span class="info-line-label">Type</span>
14
+ <div class="info-line-separator"></div>
15
+ <span class="info-line-value"><%= recurring_task_type_badge(@recurring_task.type) %></span>
16
+ </div>
17
+
18
+ <div class="info-line">
19
+ <span class="info-line-label">Key</span>
20
+ <div class="info-line-separator"></div>
21
+ <span class="info-line-value"><%= @recurring_task.key %></span>
22
+ </div>
23
+
24
+ <div class="info-line">
25
+ <span class="info-line-label">Schedule</span>
26
+ <div class="info-line-separator"></div>
27
+ <span class="info-line-value"><%= @recurring_task.schedule %></span>
28
+ </div>
29
+
30
+ <% if @recurring_task.queue_name? %>
31
+ <div class="info-line">
32
+ <span class="info-line-label">Queue Name</span>
33
+ <div class="info-line-separator"></div>
34
+ <span class="info-line-value"><%= @recurring_task.queue_name %></span>
35
+ </div>
36
+ <% end %>
37
+ </div>
38
+
39
+ <div class="space-y-4">
40
+ <% if @recurring_task.priority? %>
41
+ <div class="info-line">
42
+ <span class="info-line-label">Priority</span>
43
+ <div class="info-line-separator"></div>
44
+ <span class="info-line-value"><%= @recurring_task.priority %></span>
45
+ </div>
46
+ <% end %>
47
+
48
+ <div class="info-line">
49
+ <span class="info-line-label">Static</span>
50
+ <div class="info-line-separator"></div>
51
+ <span class="info-line-value"><%= @recurring_task.static ? "Yes" : "No" %></span>
52
+ </div>
53
+
54
+ <div class="info-line">
55
+ <span class="info-line-label">Created At</span>
56
+ <div class="info-line-separator"></div>
57
+ <span class="info-line-value" data-date><%= @recurring_task.created_at.to_fs(:database) %></span>
58
+ </div>
59
+
60
+ <div class="info-line">
61
+ <span class="info-line-label">Updated At</span>
62
+ <div class="info-line-separator"></div>
63
+ <span class="info-line-value" data-date><%= @recurring_task.updated_at.to_fs(:database) %></span>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+
69
+ <% if @recurring_task.command.present? %>
70
+ <div class="card mt-8 overflow-hidden">
71
+ <div class="card-header border-b border-t-4 border-t-amber-500 dark:border-t-amber-600">
72
+ <h3 class="card-title">Command</h3>
73
+ <p class="card-description">The command to be executed</p>
74
+ </div>
75
+ <div class="card-content pt-6">
76
+ <pre><%= @recurring_task.command %></pre>
77
+ </div>
78
+ </div>
79
+ <% end %>
80
+
81
+ <% if @recurring_task.class_name.present? %>
82
+ <div class="card mt-8 overflow-hidden">
83
+ <div class="card-header border-b border-t-4 border-t-blue-500 dark:border-t-blue-600">
84
+ <h3 class="card-title">Job Class</h3>
85
+ <p class="card-description">The job class to be executed</p>
86
+ </div>
87
+ <div class="card-content pt-6">
88
+ <pre><%= @recurring_task.class_name %></pre>
89
+ </div>
90
+ </div>
91
+ <% end %>
92
+
93
+ <% if @recurring_task.arguments.present? %>
94
+ <div class="card mt-8 overflow-hidden">
95
+ <div class="card-header border-b border-t-4 border-t-purple-500 dark:border-t-purple-600">
96
+ <h3 class="card-title">Arguments</h3>
97
+ <p class="card-description">The arguments to be passed to the job</p>
98
+ </div>
99
+ <div class="card-content pt-6">
100
+ <pre><%= JSON.pretty_generate(@recurring_task.arguments) %></pre>
101
+ </div>
102
+ </div>
103
+ <% end %>
104
+
105
+ <% if @recurring_task.description.present? %>
106
+ <div class="card mt-8 overflow-hidden">
107
+ <div class="card-header border-b border-t-4 border-t-amber-500 dark:border-t-amber-600">
108
+ <h3 class="card-title">Description</h3>
109
+ <p class="card-description">Additional information about this recurring task</p>
110
+ </div>
111
+ <div class="card-content pt-6">
112
+ <pre><%= @recurring_task.description %></pre>
113
+ </div>
114
+ </div>
115
+ <% end %>
116
+
117
+ <div class="card mt-8 overflow-hidden">
118
+ <div class="card-header border-b border-t-4 border-t-zinc-500 dark:border-t-zinc-600">
119
+ <h3 class="card-title">Next Runs</h3>
120
+ <p class="card-description">Based on the current schedule</p>
121
+ </div>
122
+ <div class="card-content pt-5">
123
+ <ul class="list-decimal marker:text-sm marker:text-muted-foreground ml-4 space-y-1">
124
+ <% @recurring_task.next_runs.each do |run_time| %>
125
+ <li data-date><%= run_time.strftime("%Y-%m-%d %H:%M:%S %Z") %></li>
126
+ <% end %>
127
+ </ul>
128
+ </div>
129
+ </div>
data/bun.lockb ADDED
Binary file
data/config/routes.rb ADDED
@@ -0,0 +1,18 @@
1
+ SolidQueueDashboard::Engine.routes.draw do
2
+ resources :jobs, only: [ :index, :show ] do
3
+ member do
4
+ post :retry
5
+ end
6
+ end
7
+
8
+ resources :processes, only: [ :index, :show ]
9
+
10
+ resources :recurring_tasks, path: "recurring-tasks", only: [ :index, :show ] do
11
+ member do
12
+ post :enqueue
13
+ end
14
+ end
15
+
16
+ post "appearance/toggle", to: "appearance#toggle", as: :toggle_appearance
17
+ root "dashboard#index"
18
+ end
@@ -0,0 +1,17 @@
1
+ module SolidQueueDashboard
2
+ class Configuration
3
+ attr_accessor :title
4
+
5
+ def initialize
6
+ @title = "Solid Queue Dashboard"
7
+ end
8
+ end
9
+
10
+ def self.configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ def self.configure
15
+ yield(configuration)
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ module SolidQueueDashboard
2
+ module Decorators
3
+ class JobDecorator < SimpleDelegator
4
+ def color
5
+ Job.status_color(status)
6
+ end
7
+
8
+ def status
9
+ return @status if defined?(@status)
10
+
11
+ @status = if retried?
12
+ Job::RETRIED
13
+ elsif failed?
14
+ Job::FAILED
15
+ elsif success?
16
+ Job::SUCCESS
17
+ elsif scheduled?
18
+ Job::SCHEDULED
19
+ else
20
+ Job::PENDING
21
+ end
22
+ end
23
+
24
+ def success?
25
+ return @success if defined?(@success)
26
+ @success = finished_at.present? && !failed? && !retried?
27
+ end
28
+
29
+ def retried?
30
+ return @retried if defined?(@retried)
31
+ @retried = finished_at.present? && !failed_execution.present? &&
32
+ (arguments["executions"].to_i > 0 || execution_history.where(scheduled_at: finished_at..).any?)
33
+ end
34
+
35
+ def failed?
36
+ return @failed if defined?(@failed)
37
+ @failed = failed_execution.present?
38
+ end
39
+
40
+ def scheduled?
41
+ return @scheduled if defined?(@scheduled)
42
+ @scheduled = scheduled_at.present? && scheduled_at > Time.current
43
+ end
44
+
45
+ def pending?
46
+ return @pending if defined?(@pending)
47
+ @pending = !finished_at.present?
48
+ end
49
+
50
+ def execution_history
51
+ SolidQueue::Job.where(active_job_id: active_job_id)
52
+ end
53
+
54
+ def error_message
55
+ return @error_message if defined?(@error_message)
56
+
57
+ @error_message = failed_execution ?
58
+ "#{failed_execution.error["exception_class"]}: #{failed_execution.error["message"]}" :
59
+ nil
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,74 @@
1
+ module SolidQueueDashboard
2
+ module Decorators
3
+ class JobsDecorator < SimpleDelegator
4
+ def with_status(status)
5
+ case status.to_sym
6
+ when Job::SUCCESS
7
+ success
8
+ when Job::FAILED
9
+ failed
10
+ when Job::SCHEDULED
11
+ scheduled
12
+ when Job::PENDING
13
+ pending
14
+ when Job::RETRIED
15
+ retried
16
+ else
17
+ raise "Invalid status: #{status}"
18
+ end
19
+ end
20
+
21
+ def success
22
+ where.not(finished_at: nil)
23
+ .where.not(id: failed)
24
+ .where.not(id: retried)
25
+ end
26
+
27
+ def scheduled
28
+ where(finished_at: nil, scheduled_at: Time.current..)
29
+ end
30
+
31
+ def pending
32
+ where(finished_at: nil, scheduled_at: ..Time.current)
33
+ .where.not(id: failed)
34
+ end
35
+
36
+ def retried
37
+ where(finished_at: ..Time.current)
38
+ .where.not(id: failed)
39
+ .where(
40
+ active_job_id: SolidQueue::Job
41
+ .select(:active_job_id)
42
+ .group(:active_job_id)
43
+ .having("COUNT(*) > 1")
44
+ )
45
+ .where.not(
46
+ id: SolidQueue::Job
47
+ .select("MAX(id)")
48
+ .group(:active_job_id)
49
+ )
50
+ end
51
+
52
+ def failure_rate
53
+ success_count = success.count
54
+ retried_count = retried.count
55
+ failed_count = failed.count
56
+
57
+ total = success_count + retried_count + failed_count
58
+ return 0 if total.zero?
59
+
60
+ (failed_count + retried_count).to_f / total * 100
61
+ end
62
+
63
+ def each
64
+ super do |job|
65
+ yield JobDecorator.new(job)
66
+ end
67
+ end
68
+
69
+ def to_a
70
+ super.map { |job| JobDecorator.new(job) }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,13 @@
1
+ module SolidQueueDashboard
2
+ module Decorators
3
+ class ProcessDecorator < SimpleDelegator
4
+ def color
5
+ Process.kind_color(kind)
6
+ end
7
+
8
+ def dead?
9
+ last_heartbeat_at < Process::HEARTBEAT_DEAD_THRESHOLD.ago
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module SolidQueueDashboard
2
+ module Decorators
3
+ class ProcessesDecorator < SimpleDelegator
4
+ def each
5
+ super do |job|
6
+ yield ProcessDecorator.new(job)
7
+ end
8
+ end
9
+
10
+ def to_a
11
+ super.map { |job| ProcessDecorator.new(job) }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module SolidQueueDashboard
2
+ module Decorators
3
+ class RecurringTaskDecorator < SimpleDelegator
4
+ def type
5
+ if command.present?
6
+ RecurringTask::COMMAND
7
+ elsif class_name.present?
8
+ RecurringTask::JOB
9
+ else
10
+ "Unknown"
11
+ end
12
+ end
13
+
14
+ def next_runs(count: 5)
15
+ cron = Fugit.parse(schedule)
16
+ return [] unless cron
17
+
18
+ cron.next.take(count)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ module SolidQueueDashboard
2
+ module Decorators
3
+ class RecurringTasksDecorator < SimpleDelegator
4
+ def with_type(type)
5
+ case type.to_sym
6
+ when RecurringTask::JOB
7
+ where.not(class_name: nil)
8
+ when RecurringTask::COMMAND
9
+ where.not(command: nil)
10
+ else
11
+ raise "Unknown type: #{type}"
12
+ end
13
+ end
14
+
15
+ def each
16
+ super do |task|
17
+ yield RecurringTaskDecorator.new(task)
18
+ end
19
+ end
20
+
21
+ def to_a
22
+ super.map { |task| RecurringTaskDecorator.new(task) }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ module SolidQueueDashboard
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace SolidQueueDashboard
4
+
5
+ initializer "solid_queue_dashboard.assets.precompile" do |app|
6
+ app.config.assets.precompile += %w[
7
+ solid_queue_dashboard/alpine.js
8
+ solid_queue_dashboard/application.js
9
+ solid_queue_dashboard/application.css
10
+ ]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module SolidQueueDashboard
2
+ module Job
3
+ # Constants
4
+ SUCCESS = :success
5
+ RETRIED = :retried
6
+ FAILED = :failed
7
+ PENDING = :pending
8
+ SCHEDULED = :scheduled
9
+
10
+ STATUSES = [ SUCCESS, RETRIED, FAILED, SCHEDULED, PENDING ]
11
+
12
+ STATUS_COLORS = {
13
+ SUCCESS => "green",
14
+ RETRIED => "amber",
15
+ FAILED => "red",
16
+ SCHEDULED => "blue",
17
+ PENDING => "zinc"
18
+ }
19
+
20
+ COMMAND_CLASS_NAME = "SolidQueue::RecurringJob"
21
+
22
+ def self.status_color(status)
23
+ STATUS_COLORS[status] || "zinc"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module SolidQueueDashboard
2
+ module Process
3
+ # Constants
4
+ SUPERVISOR = "Supervisor"
5
+ DISPATCHER = "Dispatcher"
6
+ WORKER = "Worker"
7
+ SCHEDULER = "Scheduler"
8
+
9
+ KINDS = [ SUPERVISOR, DISPATCHER, WORKER, SCHEDULER ]
10
+
11
+ KIND_COLORS = {
12
+ SUPERVISOR => "blue",
13
+ DISPATCHER => "green",
14
+ WORKER => "yellow",
15
+ SCHEDULER => "purple"
16
+ }
17
+
18
+ HEARTBEAT_DEAD_THRESHOLD = 1.minute
19
+
20
+ def self.kind_color(kind)
21
+ KIND_COLORS[kind] || "zinc"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module SolidQueueDashboard
2
+ module RecurringTask
3
+ # Constants
4
+ COMMAND = :command
5
+ JOB = :job
6
+
7
+ TYPES = [ COMMAND, JOB ]
8
+
9
+ TYPE_COLORS = {
10
+ COMMAND => "amber",
11
+ JOB => "sky"
12
+ }
13
+ end
14
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidQueueDashboard
4
- VERSION = "0.0.1"
4
+ VERSION = "0.1.1"
5
5
  end
@@ -1,8 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rails"
3
4
  require_relative "solid_queue_dashboard/version"
5
+ require_relative "solid_queue_dashboard/configuration"
6
+ require_relative "solid_queue_dashboard/engine"
7
+ require_relative "solid_queue_dashboard/job"
8
+ require_relative "solid_queue_dashboard/process"
9
+ require_relative "solid_queue_dashboard/recurring_task"
10
+ require_relative "solid_queue_dashboard/decorators/job_decorator"
11
+ require_relative "solid_queue_dashboard/decorators/jobs_decorator"
12
+ require_relative "solid_queue_dashboard/decorators/process_decorator"
13
+ require_relative "solid_queue_dashboard/decorators/processes_decorator"
14
+ require_relative "solid_queue_dashboard/decorators/recurring_task_decorator"
15
+ require_relative "solid_queue_dashboard/decorators/recurring_tasks_decorator"
4
16
 
5
17
  module SolidQueueDashboard
6
18
  class Error < StandardError; end
7
- # Your code goes here...
19
+
20
+ def self.job_queue_names
21
+ SolidQueue::Job.distinct.pluck(:queue_name)
22
+ end
23
+
24
+ def self.job_class_names
25
+ SolidQueue::Job.distinct.pluck(:class_name)
26
+ end
27
+
28
+ def self.decorate(object)
29
+ case object
30
+ when SolidQueue::Job
31
+ Decorators::JobDecorator.new(object)
32
+ when SolidQueue::Job.const_get(:ActiveRecord_Relation)
33
+ Decorators::JobsDecorator.new(object)
34
+ when SolidQueue::Process
35
+ Decorators::ProcessDecorator.new(object)
36
+ when SolidQueue::Process.const_get(:ActiveRecord_Relation)
37
+ Decorators::ProcessesDecorator.new(object)
38
+ when SolidQueue::RecurringTask
39
+ Decorators::RecurringTaskDecorator.new(object)
40
+ when SolidQueue::RecurringTask.const_get(:ActiveRecord_Relation)
41
+ Decorators::RecurringTasksDecorator.new(object)
42
+ else
43
+ raise Error, "Cannot decorate #{object.class}"
44
+ end
45
+ end
8
46
  end
data/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "private": true,
3
+ "scripts": {
4
+ "build": "tailwindcss -i ./app/assets/stylesheets/solid_queue_dashboard/tailwind.css -o ./app/assets/stylesheets/solid_queue_dashboard/application.css",
5
+ "watch": "tailwindcss -i ./app/assets/stylesheets/solid_queue_dashboard/tailwind.css -o ./app/assets/stylesheets/solid_queue_dashboard/application.css --watch"
6
+ },
7
+ "devDependencies": {
8
+ "@tailwindcss/forms": "^0.5.9",
9
+ "@tailwindcss/typography": "^0.5.15",
10
+ "tailwindcss": "^3.4.13",
11
+ "tailwindcss-animate": "^1.0.7"
12
+ }
13
+ }
@@ -0,0 +1,83 @@
1
+ const { fontFamily } = require("tailwindcss/defaultTheme")
2
+
3
+ /** @type {import('tailwindcss').Config} */
4
+ module.exports = {
5
+ darkMode: ["class"],
6
+ content: [
7
+ "./app/views/**/*.html.erb",
8
+ "./app/helpers/**/*.rb",
9
+ "./app/javascript/**/*.js",
10
+ ],
11
+ theme: {
12
+ container: {
13
+ center: true,
14
+ padding: "2rem",
15
+ screens: {
16
+ "2xl": "1400px",
17
+ },
18
+ },
19
+ extend: {
20
+ colors: {
21
+ border: "hsl(var(--border))",
22
+ input: "hsl(var(--input))",
23
+ ring: "hsl(var(--ring))",
24
+ background: "hsl(var(--background))",
25
+ foreground: "hsl(var(--foreground))",
26
+ primary: {
27
+ DEFAULT: "hsl(var(--primary))",
28
+ foreground: "hsl(var(--primary-foreground))",
29
+ },
30
+ secondary: {
31
+ DEFAULT: "hsl(var(--secondary))",
32
+ foreground: "hsl(var(--secondary-foreground))",
33
+ },
34
+ destructive: {
35
+ DEFAULT: "hsl(var(--destructive))",
36
+ foreground: "hsl(var(--destructive-foreground))",
37
+ },
38
+ muted: {
39
+ DEFAULT: "hsl(var(--muted))",
40
+ foreground: "hsl(var(--muted-foreground))",
41
+ },
42
+ accent: {
43
+ DEFAULT: "hsl(var(--accent))",
44
+ foreground: "hsl(var(--accent-foreground))",
45
+ },
46
+ popover: {
47
+ DEFAULT: "hsl(var(--popover))",
48
+ foreground: "hsl(var(--popover-foreground))",
49
+ },
50
+ card: {
51
+ DEFAULT: "hsl(var(--card))",
52
+ foreground: "hsl(var(--card-foreground))",
53
+ },
54
+ },
55
+ borderRadius: {
56
+ lg: `var(--radius)`,
57
+ md: `calc(var(--radius) - 2px)`,
58
+ sm: "calc(var(--radius) - 4px)",
59
+ },
60
+ opacity: {
61
+ 7.5: "0.075",
62
+ },
63
+ keyframes: {
64
+ "accordion-down": {
65
+ from: { height: "0" },
66
+ to: { height: "var(--radix-accordion-content-height)" },
67
+ },
68
+ "accordion-up": {
69
+ from: { height: "var(--radix-accordion-content-height)" },
70
+ to: { height: "0" },
71
+ },
72
+ },
73
+ animation: {
74
+ "accordion-down": "accordion-down 0.2s ease-out",
75
+ "accordion-up": "accordion-up 0.2s ease-out",
76
+ },
77
+ },
78
+ },
79
+ plugins: [
80
+ require("tailwindcss-animate"),
81
+ require("@tailwindcss/forms")({ strategy: "class" }),
82
+ ],
83
+ }