oversee 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +53 -14
  3. data/app/components/oversee/{application_component.rb → base.rb} +1 -1
  4. data/app/components/oversee/card.rb +13 -0
  5. data/app/components/oversee/dashboard/filters.rb +54 -0
  6. data/app/components/oversee/dashboard/header.rb +41 -0
  7. data/app/components/oversee/dashboard/index.rb +24 -0
  8. data/app/components/oversee/dashboard/javascript.rb +9 -0
  9. data/app/components/oversee/dashboard/pagination.rb +23 -0
  10. data/app/components/oversee/dashboard/sidebar.rb +53 -0
  11. data/app/components/oversee/dashboard/tailwind.rb +33 -0
  12. data/app/components/oversee/field/display.rb +29 -0
  13. data/app/components/oversee/field/form.rb +49 -0
  14. data/app/components/oversee/field/input/boolean.rb +23 -0
  15. data/app/components/oversee/field/input/datetime.rb +20 -0
  16. data/app/components/oversee/field/input/integer.rb +20 -0
  17. data/app/components/oversee/field/input/string.rb +20 -0
  18. data/app/components/oversee/field/input.rb +26 -0
  19. data/app/components/oversee/field/label.rb +24 -0
  20. data/app/components/oversee/field/value/boolean.rb +50 -0
  21. data/app/components/oversee/field/value/datetime.rb +9 -0
  22. data/app/components/oversee/field/value/enum.rb +9 -0
  23. data/app/components/oversee/field/value/integer.rb +9 -0
  24. data/app/components/oversee/field/value/string.rb +20 -0
  25. data/app/components/oversee/field/value/text.rb +9 -0
  26. data/app/components/oversee/field/value.rb +34 -0
  27. data/app/components/oversee/resources/base.rb +4 -0
  28. data/app/components/oversee/resources/errors.rb +41 -0
  29. data/app/components/oversee/resources/form.rb +49 -0
  30. data/app/components/oversee/resources/index.rb +26 -0
  31. data/app/components/oversee/resources/new.rb +17 -0
  32. data/app/components/oversee/resources/show.rb +126 -0
  33. data/app/components/oversee/resources/table.rb +109 -0
  34. data/app/components/oversee/table/body.rb +13 -0
  35. data/app/components/oversee/table/data.rb +5 -0
  36. data/app/components/oversee/table/head.rb +9 -0
  37. data/app/components/oversee/table/row.rb +9 -0
  38. data/app/components/oversee/table.rb +21 -0
  39. data/app/controllers/oversee/dashboard_controller.rb +4 -0
  40. data/app/controllers/oversee/resources_controller.rb +62 -23
  41. data/app/models/oversee/resource.rb +36 -0
  42. data/app/oversee/filter.rb +39 -0
  43. data/app/oversee/search.rb +34 -0
  44. data/app/views/layouts/oversee/application.html.erb +5 -5
  45. data/config/locales/en.yml +4 -0
  46. data/config/locales/oversee.en.yml +4 -0
  47. data/config/routes.rb +10 -10
  48. data/lib/oversee/configuration.rb +3 -0
  49. data/lib/oversee/version.rb +1 -1
  50. data/lib/oversee.rb +14 -2
  51. metadata +71 -47
  52. data/app/assets/config/oversee_manifest.js +0 -1
  53. data/app/assets/stylesheets/oversee/application.css +0 -15
  54. data/app/components/oversee/card_component.rb +0 -15
  55. data/app/components/oversee/dashboard/sidebar_component.rb +0 -127
  56. data/app/components/oversee/field_component.rb +0 -39
  57. data/app/components/oversee/field_label_component.rb +0 -155
  58. data/app/components/oversee/fields/display_row_component.rb +0 -58
  59. data/app/components/oversee/fields/input/boolean_component.rb +0 -26
  60. data/app/components/oversee/fields/input/datetime_component.rb +0 -27
  61. data/app/components/oversee/fields/input/integer_component.rb +0 -26
  62. data/app/components/oversee/fields/input/string_component.rb +0 -26
  63. data/app/components/oversee/fields/value/boolean_component.rb +0 -44
  64. data/app/components/oversee/fields/value/datetime_component.rb +0 -16
  65. data/app/components/oversee/fields/value/enum_component.rb +0 -16
  66. data/app/components/oversee/fields/value/integer_component.rb +0 -16
  67. data/app/components/oversee/fields/value/string_component.rb +0 -23
  68. data/app/components/oversee/fields/value/text_component.rb +0 -16
  69. data/app/helpers/oversee/application_helper.rb +0 -5
  70. data/app/mailers/oversee/application_mailer.rb +0 -6
  71. data/app/views/oversee/application/_javascript.html.erb +0 -17
  72. data/app/views/oversee/dashboard/show.html.erb +0 -18
  73. data/app/views/oversee/resources/_form.html.erb +0 -10
  74. data/app/views/oversee/resources/_input_field.html.erb +0 -20
  75. data/app/views/oversee/resources/edit.html.erb +0 -30
  76. data/app/views/oversee/resources/index.html.erb +0 -59
  77. data/app/views/oversee/resources/input_field.html.erb +0 -1
  78. data/app/views/oversee/resources/new.html.erb +0 -28
  79. data/app/views/oversee/resources/show.html.erb +0 -51
  80. data/app/views/oversee/resources/update.turbo_stream.erb +0 -3
  81. data/app/views/shared/_sidebar.html.erb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eda2b577791e666d84d3b2af260c06604489d84936c8ff907f51573f5441e7e7
4
- data.tar.gz: 83ca5ac7da8e9b5f66bc19523e2b78875634e669b0acd7e34bad2b2dcb76887d
3
+ metadata.gz: af99387ad6126bddd510d80a2fd929a776c48ef101e61a3ecfd93c1f8106fa87
4
+ data.tar.gz: 1d8f34bc6c2fd6225b00edf30b833b4ba6bf52ced03c4c5b16be9a33162ad1a7
5
5
  SHA512:
6
- metadata.gz: 0a7fc59d7b8f317e15927ec883017b374f5216aeaa5208bdb29d67f3cbf03dd944e57b7a90e5eac161cde60257276def41ebfa5e34399f3f41db43e47c3e63d8
7
- data.tar.gz: a441505a216cf3df44dc9a58be3e135a5741564b3b8a51e1e486de08a53dfdfe0f640dcc2126813c1dd3d32d21deb806480f52a03b8deab54a6713af3e33b6ad
6
+ metadata.gz: 577e608656e45f6797397728ec9820cd39854eccffa550d7d60b3735581f8480817a3d4ce235c49bcceab3b9394b06356637ec17c3c2808f2549158ad7a32ea1
7
+ data.tar.gz: 4ef646cc6bef74b8ffccfbd97c796bc147d6f9a144ff5f4068630235e1967ff6fe6f9b4e08bfcdb899c49bdbe106e8a37cbde59603377edf2fd7c8866c115cbc
data/README.md CHANGED
@@ -1,40 +1,79 @@
1
- [![Gem Version](https://badge.fury.io/rb/oversee.svg)](https://badge.fury.io/rb/oversee)
2
1
 
3
- # 👓 Oversee
2
+ # Oversee
3
+
4
4
  Plug & play admin dashboard for Rails applications.
5
5
 
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/oversee.svg)](https://badge.fury.io/rb/oversee)
8
+
9
+ ---
10
+
11
+ ### Features
12
+
13
+ - Minimal, if any, configuration needed
14
+ - Tailored and pleasant user interface
15
+ - Geared for performance
16
+
17
+ ---
18
+
19
+ ![Oversee Screenshot](docs/images/screenshot.png)
20
+
6
21
  Developed by [Primevise](https://primevise.com)
7
22
 
8
- > [!NOTE]
23
+ > [!NOTE]
9
24
  > Oversee is still rather incomplete and only has the very basic features. It might significantly change and break things before a stable release.
10
25
 
11
26
  ## Installation
12
- Add it to your application by running
27
+
28
+ #### Add gem
29
+
30
+ Simply add the gem to your Gemfile by running the following command
13
31
 
14
32
  ```bash
15
33
  $ bundle add oversee
16
34
  ```
17
35
 
36
+ > [!TIP]
37
+ > Currently, we don't release new gem versions too often due to the fragile nature of the gem. However, you can use the edge version of the gem by pointing directly to the git repository.
38
+ > ```ruby
39
+ > gem "oversee", git: "https://github.com/primevise/oversee", branch: :main
40
+ > ```
18
41
 
19
- or add this line to your application's Gemfile:
42
+ ---
20
43
 
21
- ```ruby
22
- gem "oversee"
23
- ```
44
+ #### Mount it on your application
24
45
 
25
- And then run:
26
- ```bash
27
- $ bundle
46
+ After you have the gem installed, you can then mount the engine in your `config/routes.rb`:
47
+
48
+ ```ruby
49
+ Rails.application.routes.draw do
50
+ mount Oversee::Engine => "/oversee"
51
+ end
28
52
  ```
29
53
 
30
- ## Contributing
31
- TBA
54
+ Ideally, you'd want to limit access to the dashboard to only authorized users.
32
55
 
33
56
  ## Notable mentions
34
- This gem is inspired by other open source admin dashboards. A big thank you goes to these projects:
57
+
58
+ This gem would not be possible without the work of others. A big thank you goes to these projects:
35
59
 
36
60
  - [madmin](https://github.com/excid3/madmin)
37
61
  - [Avo](https://github.com/avo-hq/avo)
62
+ - [Iconoir](https://github.com/iconoir-icons/iconoir)
63
+
64
+ ## Who uses Oversee?
65
+
66
+ - [Mintis](https://mintis.app)
67
+ - [No Logo X](https://nologox.com)
68
+ - [Release Server](https://releaseserver.com)
69
+ - [College Life Work](https://work.collegelife.co)
70
+
71
+ Do you use Oversee in your project? Let us know!
72
+
73
+ ## Contributing
74
+
75
+ TBA
38
76
 
39
77
  ## License
78
+
40
79
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,4 +1,4 @@
1
- class Oversee::ApplicationComponent < Phlex::HTML
1
+ class Oversee::Base < Phlex::HTML
2
2
  include Phlex::Rails::Helpers::DOMID
3
3
  include Phlex::Rails::Helpers::Routes
4
4
  include Phlex::Rails::Helpers::LinkTo
@@ -0,0 +1,13 @@
1
+ class Oversee::Card < Phlex::HTML
2
+ def initialize(card_name:)
3
+ @card_name = card_name
4
+ @card = card_name.constantize.new
5
+ end
6
+
7
+ def view_template
8
+ div(class: "bg-gray-100 rounded-md p-4") do
9
+ p(class: "text-xs uppercase text-gray-600") { @card.label }
10
+ h4(class: "mt-2 text-gray-900 text-2xl") { @card.value }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,54 @@
1
+ class Oversee::Dashboard::Filters < Oversee::Base
2
+ def initialize(params: nil)
3
+
4
+ @params = params
5
+
6
+ puts "params: #{params}"
7
+ puts "sortless_path: #{sortless_path}"
8
+ end
9
+
10
+ def view_template(&)
11
+ div(class: "border-b p-4") do
12
+ div(class: "flex items-center justify-between") do
13
+ div(class: "flex items-center gap-2") do
14
+ if show_action_section?
15
+ button(class:"rounded-full bg-gray-100 inline-flex gap-2 items-center text-xs px-4 py-2 font-medium hover:bg-gray-200") do
16
+ render Phlex::Icons::Iconoir::FilterAlt.new(class: "size-3")
17
+ plain "Filters"
18
+ end
19
+ end
20
+ if false
21
+ a(class:"rounded-full bg-gray-100 inline-flex gap-2 items-center text-xs px-4 py-2 font-medium hover:bg-gray-200") do
22
+ render Phlex::Icons::Iconoir::XMark.new(class: "size-4 text-gray-500")
23
+ plain "Clear sorting"
24
+ end
25
+ end
26
+ end
27
+ div(class: "flex items-center gap-4") do
28
+ form(action: "", class: "flex items-center gap-2") do
29
+ input(type: :search, name: :query, class: "flex bg-gray-100 min-w-80 h-10 items-center pl-4 py-2 placeholder:text-gray-500 rounded-sm text-sm", placeholder: search_placeholder, value: @params[:query])
30
+ button(class: "size-10 inline-flex items-center justify-center bg-gray-100 hover:bg-gray-200 transition-colors") { render Phlex::Icons::Iconoir::Search.new(class: "size-4 text-gray-600") }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def show_action_section?
40
+ Rails.env.development? || @params[:experimental] == "true"
41
+ end
42
+
43
+ def sortless_path
44
+ sortless_query_params = @params.except(:sort_attribute, :sort_direction, :controller, :action)
45
+ # helpers.resources_path(sortless_query_params)
46
+ end
47
+
48
+ def search_placeholder
49
+ context = Search.new(collection: nil, resource_class: @params[:resource_class_name].constantize)
50
+ attr = context.default_searchable_attribute
51
+
52
+ "Search by #{attr}"
53
+ end
54
+ end
@@ -0,0 +1,41 @@
1
+ class Oversee::Dashboard::Header < Oversee::Base
2
+ def initialize(title: nil, subtitle: nil, return_path: nil, show_back_button: true)
3
+ @title = title || "Dashboard"
4
+ @subtitle = subtitle || "Manage your account"
5
+ @return_path = return_path
6
+ @show_back_button = show_back_button
7
+ end
8
+
9
+ def view_template(&)
10
+ div(id: "dashboard_header", class: "p-8 border-b flex items-center justify-between") do
11
+ div(class: "flex items-center gap-4") do
12
+ back_button
13
+ div do
14
+ h1(class: "text-xl text-gray-800") { @title }
15
+ p(class: "text-sm text-gray-600") { @subtitle }
16
+ end
17
+ end
18
+ div(class: "flex items-center gap-2") do
19
+ yield if block_given?
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def back_button
27
+ if @show_back_button
28
+ a(
29
+ href: return_button_path,
30
+ class: "size-10 inline-flex items-center justify-center bg-gray-50 text-gray-500 hover:text-gray-900 hover:bg-gray-200 transition-colors",
31
+ data: { controller: "back", action: "back#navigate", turbo_action: "replace" }
32
+ ) do
33
+ render Phlex::Icons::Iconoir::ArrowLeft.new(class: "size-4 text-gray-900")
34
+ end
35
+ end
36
+ end
37
+
38
+ def return_button_path
39
+ @return_path || root_path
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Dashboard::Index < Oversee::Base
4
+ def view_template
5
+ div(class: "p-8") do
6
+ div(class: "flex items-center justify-between") do
7
+ div do
8
+ p(class: "text-xs uppercase font-medium text-gray-400") { "Dashboard" }
9
+ h1(class: "text-xl") { "Welcome" }
10
+ end
11
+ end
12
+ end
13
+
14
+ if Oversee.card_class_names.present?
15
+ div(class: "p-8") do
16
+ div(class: "grid grid-cols-4 gap-4") do
17
+ Oversee.card_class_names.each do |card_name|
18
+ render Oversee::Card.new(card_name: card_name)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Dashboard::Javascript < Phlex::HTML
2
+ def view_template
3
+ script(async: true, src:"https://unpkg.com/es-module-shims/dist/es-module-shims.js")
4
+ script(type: "importmap", data_turbo_track: "reload") do
5
+ raw %({"imports":{"@hotwired/stimulus":"https://unpkg.com/@hotwired/stimulus/dist/stimulus.js","@hotwired/turbo":"https://unpkg.com/@hotwired/turbo","@hotwired/turbo-rails":"https://unpkg.com/@hotwired/turbo-rails"}}).html_safe
6
+ end
7
+ script(type: "module") { raw %(import * as Turbo from "@hotwired/turbo-rails").html_safe }
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ class Oversee::Dashboard::Pagination < Oversee::Base
3
+ include Pagy::Frontend
4
+ include Phlex::Rails::Helpers::Request
5
+
6
+ def initialize(pagy:, params:)
7
+ @pagy = pagy
8
+ @params = params
9
+ end
10
+
11
+ def view_template
12
+ div(class:"p-4 border-t flex items-center justify-between") do
13
+
14
+ div(class: "font-regular text-xs") do
15
+ raw pagy_info(@pagy).html_safe
16
+ end
17
+
18
+ div(class: "flex items-center gap-4") do
19
+ raw pagy_nav(@pagy).html_safe
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Oversee::Dashboard::Sidebar < Oversee::Base
4
+ def view_template
5
+ div(class: "bg-white p-4 rounded-lg") do
6
+ button(class: "mb-4 ") { collapse_icon } unless true
7
+
8
+ p(class: "text-[0.7rem] uppercase text-gray-400 font-medium mb-2") { "Menu" }
9
+ ul(class: "text-sm text-gray-700") do
10
+ li do
11
+ a(href: root_path, class:"flex items-center gap-2 hover:bg-gray-50 p-2") do
12
+ render Phlex::Icons::Iconoir::LayoutLeft.new(class: "size-4 text-gray-400")
13
+ span { "Dashboard" }
14
+ end
15
+ end
16
+ li do
17
+ a(href: helpers.main_app.root_path, class: "flex items-center gap-2 hover:bg-gray-50 p-2 rounded-lg") do
18
+ render Phlex::Icons::Iconoir::HomeAltSlimHoriz.new(class: "size-4 text-gray-400")
19
+ span { "Return to app" }
20
+ end
21
+ end
22
+ end
23
+
24
+ hr(class: "my-4 -mx-4")
25
+ details(open: true, class: "group") do
26
+ summary(class: "flex items-center justify-between cursor-pointer") do
27
+ p(class: "text-[0.7rem] uppercase text-gray-400 font-medium") { "Resources" }
28
+ render Phlex::Icons::Iconoir::NavArrowDown.new(class: "size-4 text-gray-400 transform transition-transform group-open:rotate-180 group-hover:text-blue-500")
29
+ end
30
+ ul(class: "mt-2 text-sm text-gray-700 overflow-x-hidden") do
31
+ Oversee.application_resource_names.sort.each do |resource_class_name|
32
+ li do
33
+ a(href: helpers.resources_path(resource_class_name:), class: "flex items-center gap-2 hover:bg-gray-50 p-2 truncate") do
34
+ render Phlex::Icons::Iconoir::Folder.new(class: "size-4 text-gray-400")
35
+ span { resource_class_name }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ hr(class: "my-4 -mx-4")
43
+ p(class: "text-xs text-gray-500") do
44
+ plain("Powered by ")
45
+ a(
46
+ href: "https://github.com/primevise/oversee",
47
+ class: "text-blue-500 hover:underline",
48
+ target: "_blank"
49
+ ) { "Oversee" }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,33 @@
1
+ class Oversee::Dashboard::Tailwind < Phlex::HTML
2
+ def view_template
3
+ script(src: "https://cdn.tailwindcss.com?plugins=typography")
4
+ style(type:"text/tailwindcss") do
5
+ raw pagy_css.html_safe
6
+ end
7
+ end
8
+
9
+ def pagy_css
10
+ <<~CSS
11
+ .pagy {
12
+ @apply flex gap-2 items-center text-sm text-gray-500;
13
+ a:not(.gap) {
14
+ @apply inline-flex items-center justify-center h-8 min-w-8 font-normal;
15
+ &:hover { @apply bg-gray-100; }
16
+ &:not([href]) { /* disabled links */
17
+ @apply text-gray-300 cursor-not-allowed;
18
+ }
19
+ &.current {
20
+ @apply font-semibold bg-gray-100 text-gray-900;
21
+ }
22
+ }
23
+
24
+ label {
25
+ @apply inline-block whitespace-nowrap bg-gray-200 px-3 py-0.5;
26
+ input {
27
+ @apply bg-gray-100 border-none rounded-md;
28
+ }
29
+ }
30
+ }
31
+ CSS
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ class Oversee::Field::Display < Oversee::Base
2
+ def initialize(resource:, key: nil, value: nil, datatype: :string, interactive: true)
3
+ @resource = resource
4
+ @key = key
5
+ @value = value
6
+ @datatype = datatype
7
+ @interactive = interactive
8
+ end
9
+
10
+ def view_template
11
+ wrapper_tag(
12
+ id: dom_id(@resource, @key),
13
+ href: helpers.resource_input_field_path(resource_class_name: resource_class_name, key: @key),
14
+ class: "bg-gray-100 h-10 flex items-center px-4 py-2 hover:bg-gray-200 transition-colors w-full cursor-pointer",
15
+ data: { turbo_stream: true }) do
16
+ render Oversee::Field::Value.new(key: @key, value: @resource.send(@key), datatype: @datatype)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def resource_class_name
23
+ @resource.class.name
24
+ end
25
+
26
+ def wrapper_tag(...)
27
+ @interactive ? a(...) : div(...)
28
+ end
29
+ end
@@ -0,0 +1,49 @@
1
+ class Oversee::Field::Form < Oversee::Base
2
+ include Phlex::Rails::Helpers::FormWith
3
+
4
+ def initialize(resource:, key: nil, value: nil, datatype: :string, method: :patch)
5
+ @resource = resource
6
+ @key = key
7
+ @value = value
8
+ @datatype = datatype
9
+ @method = method
10
+ end
11
+
12
+ def view_template
13
+ form_with(id: dom_id(@resource, @key), model: @resource, url: helpers.update_resource_path(resource_class_name: resource_class_name, id: @resource.id), method: @method, class: "flex items-center w-full gap-4") do |form|
14
+ input type: :hidden, id: "oversee_key", name: "oversee_key", value: @key.to_s
15
+ input type: :hidden, id: "oversee_datatype", name: "oversee_datatype", value: @datatype
16
+ render Oversee::Field::Input.new(key: @key, value: @value, datatype: @datatype)
17
+ button(class:"h-9 bg-gray-900 hover:bg-gray-700 text-white inline-flex items-center cursor-pointer gap-2 px-4 rounded-full text-xs font-medium") {
18
+ save_icon
19
+ plain "Save"
20
+ }
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def resource_class_name
27
+ @resource.class.name
28
+ end
29
+
30
+ def save_icon
31
+ svg(
32
+ stroke_width: "2.5",
33
+ viewbox: "0 0 24 24",
34
+ fill: "none",
35
+ xmlns: "http://www.w3.org/2000/svg",
36
+ class: "size-4",
37
+ color: "currentColor"
38
+ ) do |s|
39
+ s.path(
40
+ d: "M5 13L9 17L19 7",
41
+ stroke: "currentColor",
42
+ stroke_width: "2.5",
43
+ stroke_linecap: "round",
44
+ stroke_linejoin: "round"
45
+ )
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,23 @@
1
+ class Oversee::Field::Input::Boolean < Phlex::HTML
2
+ def initialize(key:, value:)
3
+ @key = key
4
+ @value = value
5
+ end
6
+
7
+ def view_template
8
+ select(id: field_id, name: field_name, class: "flex w-full border rounded-sm px-4 py-2 text-sm") do
9
+ option(value: 1, selected: @value) { "True" }
10
+ option(value: 0, selected: !@value) { "False" }
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def field_id
17
+ "resource_#{@key.to_s}"
18
+ end
19
+
20
+ def field_name
21
+ "resource[#{@key.to_s}]"
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ class Oversee::Field::Input::Datetime < Phlex::HTML
2
+ def initialize(key:, value:)
3
+ @key = key
4
+ @value = value
5
+ end
6
+
7
+ def view_template
8
+ input type: "datetime-local", id: field_id, name: field_name, value: @value&.strftime("%Y-%m-%dT%T"), class: "flex w-full border rounded-sm px-4 py-2 text-sm"
9
+ end
10
+
11
+ private
12
+
13
+ def field_id
14
+ "resource_#{@key.to_s}"
15
+ end
16
+
17
+ def field_name
18
+ "resource[#{@key.to_s}]"
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class Oversee::Field::Input::Integer < Phlex::HTML
2
+ def initialize(key:, value:)
3
+ @key = key
4
+ @value = value
5
+ end
6
+
7
+ def view_template
8
+ input type: "number", id: field_id, name: field_name, value: @value, class: "flex w-full border rounded-sm px-4 py-2 text-sm"
9
+ end
10
+
11
+ private
12
+
13
+ def field_id
14
+ "resource_#{@key.to_s}"
15
+ end
16
+
17
+ def field_name
18
+ "resource[#{@key.to_s}]"
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class Oversee::Field::Input::String < Phlex::HTML
2
+ def initialize(key:, value:)
3
+ @key = key
4
+ @value = value
5
+ end
6
+
7
+ def view_template
8
+ input type: "text", id: field_id, name: field_name, value: @value, class: "flex border px-4 py-2 text-sm w-full rounded-sm"
9
+ end
10
+
11
+ private
12
+
13
+ def field_id
14
+ "resource_#{@key.to_s}"
15
+ end
16
+
17
+ def field_name
18
+ "resource[#{@key.to_s}]"
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ class Oversee::Field::Input < Oversee::Base
2
+ MAP = {
3
+ string: Oversee::Field::Input::String,
4
+ boolean: Oversee::Field::Input::Boolean,
5
+ integer: Oversee::Field::Input::Integer,
6
+ datetime: Oversee::Field::Input::Datetime,
7
+ text: Oversee::Field::Input::String,
8
+ enum: Oversee::Field::Input::String
9
+ }
10
+
11
+ def initialize(key: nil, value: nil, datatype: :string)
12
+ @key = key
13
+ @value = value
14
+ @datatype = datatype
15
+ end
16
+
17
+ def view_template
18
+ render component_class.new(key: @key, value: @value)
19
+ end
20
+
21
+ private
22
+
23
+ def component_class
24
+ MAP[@datatype.to_sym] || Oversee::Field::Input::String
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ class Oversee::Field::Label < Oversee::Base
2
+ ICON_MAP = {
3
+ string: Phlex::Icons::Iconoir::Text,
4
+ text: Phlex::Icons::Iconoir::TextSquare,
5
+ integer: Phlex::Icons::Iconoir::Number0Square,
6
+ datetime: Phlex::Icons::Iconoir::Calendar,
7
+ boolean: Phlex::Icons::Iconoir::SwitchOn,
8
+ data: Phlex::Icons::Iconoir::Page,
9
+ }
10
+
11
+ def initialize(datatype: :string, key: nil)
12
+ @datatype = datatype
13
+ @key = key
14
+ end
15
+
16
+ def view_template
17
+ div(class:"inline-flex items-center space-x-2") do
18
+ div(class: "size-5 bg-gray-100 inline-flex items-center justify-center") do
19
+ render ICON_MAP[@datatype] ? ICON_MAP[@datatype].new(class: "size-3") : ICON_MAP[:data].new(class: "size-3")
20
+ end
21
+ label(class: "uppercase text-xs text-gray-š00 font-medium block") { @key.to_s.humanize(keep_id_suffix: true) }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ class Oversee::Field::Value::Boolean < Phlex::HTML
2
+ def initialize(key: nil, value: nil, kind: :value)
3
+ @value = value
4
+ end
5
+
6
+ def view_template
7
+ @value ? check_icon : x_icon
8
+ end
9
+
10
+ private
11
+
12
+ def check_icon
13
+ svg(
14
+ stroke_width: "2",
15
+ viewbox: "0 0 24 24",
16
+ fill: "none",
17
+ xmlns: "http://www.w3.org/2000/svg",
18
+ color: "currentColor",
19
+ class: "size-4 text-emerald-500"
20
+ ) do |s|
21
+ s.path(
22
+ d: "M5 13L9 17L19 7",
23
+ stroke: "currentColor",
24
+ stroke_width: "2",
25
+ stroke_linecap: "round",
26
+ stroke_linejoin: "round"
27
+ )
28
+ end
29
+ end
30
+
31
+ def x_icon
32
+ svg(
33
+ stroke_width: "1.5",
34
+ viewbox: "0 0 24 24",
35
+ fill: "none",
36
+ xmlns: "http://www.w3.org/2000/svg",
37
+ color: "currentColor",
38
+ class: "size-4 text-rose-500"
39
+ ) do |s|
40
+ s.path(
41
+ d:
42
+ "M6.75827 17.2426L12.0009 12M17.2435 6.75736L12.0009 12M12.0009 12L6.75827 6.75736M12.0009 12L17.2435 17.2426",
43
+ stroke: "currentColor",
44
+ stroke_width: "1.5",
45
+ stroke_linecap: "round",
46
+ stroke_linejoin: "round"
47
+ )
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Field::Value::Datetime < Phlex::HTML
2
+ def initialize(key: nil, value: nil, kind: :value)
3
+ @value = value
4
+ end
5
+
6
+ def view_template
7
+ time(title: @value) { @value&.to_fs(:long) || "N/A" }
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Field::Value::Enum < Phlex::HTML
2
+ def initialize(key: nil, value: nil, kind: :value)
3
+ @value = value
4
+ end
5
+
6
+ def view_template
7
+ p(class:"inline-flex px-2 py-1 text-red-500") { @value.to_s }
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Oversee::Field::Value::Integer < Phlex::HTML
2
+ def initialize(key: nil, value: nil, kind: :value)
3
+ @value = value
4
+ end
5
+
6
+ def view_template
7
+ p { @value }
8
+ end
9
+ end