databasium 0.1.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 (125) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +32 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +109 -0
  5. data/Rakefile +6 -0
  6. data/app/assets/builds/application.js +9045 -0
  7. data/app/assets/builds/application.js.map +7 -0
  8. data/app/assets/builds/databasium.css +2 -0
  9. data/app/assets/config/databasium_manifest.js +1 -0
  10. data/app/assets/javascript/databasium/application.js +2 -0
  11. data/app/assets/javascript/databasium/controllers/attribute_controller.js +27 -0
  12. data/app/assets/javascript/databasium/controllers/collapse_controller.js +18 -0
  13. data/app/assets/javascript/databasium/controllers/error_controller.js +15 -0
  14. data/app/assets/javascript/databasium/controllers/filter_controller.js +224 -0
  15. data/app/assets/javascript/databasium/controllers/flash_controller.js +18 -0
  16. data/app/assets/javascript/databasium/controllers/graph_controller.js +193 -0
  17. data/app/assets/javascript/databasium/controllers/index.js +7 -0
  18. data/app/assets/javascript/databasium/controllers/layout_controller.js +13 -0
  19. data/app/assets/javascript/databasium/controllers/model_controller.js +32 -0
  20. data/app/assets/javascript/databasium/controllers/new_migration_controller.js +107 -0
  21. data/app/assets/javascript/databasium/controllers/relation_controller.js +10 -0
  22. data/app/assets/javascript/databasium/controllers/search_controller.js +23 -0
  23. data/app/assets/javascript/databasium/controllers/table_controller.js +283 -0
  24. data/app/assets/javascript/databasium/controllers/table_select_controller.js +19 -0
  25. data/app/assets/javascript/databasium/controllers/toggle_controller.js +28 -0
  26. data/app/assets/javascript/databasium/controllers/validation_controller.js +78 -0
  27. data/app/assets/javascript/databasium/shapes/erd_table_shape.js +54 -0
  28. data/app/assets/stylesheets/databasium/application.css +15 -0
  29. data/app/assets/stylesheets/databasium/colors.css +55 -0
  30. data/app/assets/stylesheets/databasium/custom.css +36 -0
  31. data/app/assets/stylesheets/databasium/databasium_engine.css +6 -0
  32. data/app/assets/stylesheets/databasium/pagy-tailwind.css +66 -0
  33. data/app/components/base.rb +50 -0
  34. data/app/components/databasium/collapsable.rb +62 -0
  35. data/app/components/databasium/forms/model.rb +147 -0
  36. data/app/components/databasium/forms/search.rb +31 -0
  37. data/app/components/databasium/global/error.rb +60 -0
  38. data/app/components/databasium/global/flash.rb +73 -0
  39. data/app/components/databasium/global/header_actions.rb +36 -0
  40. data/app/components/databasium/global/sidebar.rb +45 -0
  41. data/app/components/databasium/global/suggestion.rb +25 -0
  42. data/app/components/databasium/migrations/action.rb +39 -0
  43. data/app/components/databasium/migrations/file.rb +58 -0
  44. data/app/components/databasium/migrations/form.rb +222 -0
  45. data/app/components/databasium/migrations/header_actions.rb +87 -0
  46. data/app/components/databasium/migrations/migration_status.rb +22 -0
  47. data/app/components/databasium/migrations/preview.rb +29 -0
  48. data/app/components/databasium/migrations/show_turbo_stream.rb +19 -0
  49. data/app/components/databasium/migrations/sidebar.rb +28 -0
  50. data/app/components/databasium/models/attributes.rb +49 -0
  51. data/app/components/databasium/models/form.rb +100 -0
  52. data/app/components/databasium/models/header_actions.rb +51 -0
  53. data/app/components/databasium/models/model_preview.rb +31 -0
  54. data/app/components/databasium/models/sidebar.rb +25 -0
  55. data/app/components/databasium/models/templates/attribute.rb +99 -0
  56. data/app/components/databasium/models/templates/base.rb +6 -0
  57. data/app/components/databasium/models/templates/relation.rb +56 -0
  58. data/app/components/databasium/models/templates/validation.rb +285 -0
  59. data/app/components/databasium/navigation/base_icon.rb +32 -0
  60. data/app/components/databasium/navigation/frontend_icon.rb +17 -0
  61. data/app/components/databasium/navigation/get_icon.rb +26 -0
  62. data/app/components/databasium/navigation/icon.rb +28 -0
  63. data/app/components/databasium/navigation/icon_panel.rb +26 -0
  64. data/app/components/databasium/navigation/post_icon.rb +25 -0
  65. data/app/components/databasium/navigation/put_icon.rb +18 -0
  66. data/app/components/databasium/records/filter.rb +73 -0
  67. data/app/components/databasium/records/foreign_records.rb +84 -0
  68. data/app/components/databasium/records/header_actions.rb +110 -0
  69. data/app/components/databasium/records/show_turbo_stream.rb +75 -0
  70. data/app/components/databasium/records/sidebar.rb +23 -0
  71. data/app/components/databasium/records/table/record_panel.rb +60 -0
  72. data/app/components/databasium/records/table/row.rb +104 -0
  73. data/app/components/databasium/records/table.rb +125 -0
  74. data/app/components/databasium/records/table_turbo_frame.rb +37 -0
  75. data/app/components/databasium/records/utilities.rb +25 -0
  76. data/app/components/databasium/schemas/header_actions.rb +99 -0
  77. data/app/components/databasium/schemas/sidebar.rb +25 -0
  78. data/app/components/databasium/search_results/migrations.rb +37 -0
  79. data/app/components/databasium/search_results/models.rb +36 -0
  80. data/app/components/databasium/search_results/schema_models.rb +37 -0
  81. data/app/components/databasium/search_results/tables.rb +31 -0
  82. data/app/components/databasium/type_select.rb +35 -0
  83. data/app/controllers/databasium/application_controller.rb +68 -0
  84. data/app/controllers/databasium/homepage_controller.rb +5 -0
  85. data/app/controllers/databasium/migrations_controller.rb +186 -0
  86. data/app/controllers/databasium/models_controller.rb +105 -0
  87. data/app/controllers/databasium/records_controller.rb +156 -0
  88. data/app/controllers/databasium/schemas_controller.rb +52 -0
  89. data/app/helpers/databasium/application_helper.rb +4 -0
  90. data/app/helpers/databasium/heroicon_helper.rb +21 -0
  91. data/app/helpers/databasium/models_helper.rb +4 -0
  92. data/app/jobs/databasium/application_job.rb +4 -0
  93. data/app/mailers/databasium/application_mailer.rb +6 -0
  94. data/app/models/databasium/application_record.rb +5 -0
  95. data/app/models/model.json +0 -0
  96. data/app/services/databasium/migration.rb +176 -0
  97. data/app/services/databasium/model.rb +182 -0
  98. data/app/services/databasium/record.rb +65 -0
  99. data/app/services/databasium/schema.rb +146 -0
  100. data/app/views/base.rb +13 -0
  101. data/app/views/databasium/errors/non_development.rb +21 -0
  102. data/app/views/databasium/homepage/index.rb +29 -0
  103. data/app/views/databasium/migrations/index.rb +33 -0
  104. data/app/views/databasium/migrations/new.rb +29 -0
  105. data/app/views/databasium/models/index.rb +31 -0
  106. data/app/views/databasium/models/new.rb +37 -0
  107. data/app/views/databasium/records/index.rb +24 -0
  108. data/app/views/databasium/schemas/index.rb +39 -0
  109. data/app/views/layouts/databasium/application.rb +56 -0
  110. data/config/importmap.rb +12 -0
  111. data/config/initializers/heroicon.rb +12 -0
  112. data/config/initializers/pagy.rb +48 -0
  113. data/config/initializers/phlex.rb +19 -0
  114. data/config/routes.rb +31 -0
  115. data/config/tailwind.config.js +10 -0
  116. data/lib/databasium/engine.rb +57 -0
  117. data/lib/databasium/engine_mount.rb +37 -0
  118. data/lib/databasium/middleware/conditional_check_pending.rb +27 -0
  119. data/lib/databasium/templates/create_table_migration.rb.tt +29 -0
  120. data/lib/databasium/templates/migration.rb.tt +48 -0
  121. data/lib/databasium/templates/model.rb.tt +23 -0
  122. data/lib/databasium/version.rb +3 -0
  123. data/lib/databasium.rb +11 -0
  124. data/lib/tasks/databasium_tasks.rake +4 -0
  125. metadata +272 -0
@@ -0,0 +1,31 @@
1
+ module Components
2
+ module Databasium
3
+ class Models::ModelPreview < Components::Base
4
+ def initialize(content:)
5
+ @content = content
6
+ end
7
+
8
+ def view_template
9
+ div(id: "model_preview") do
10
+ div(class: "bg-panel border-1 border-border rounded-xl p-4 min-h-full flex-1") do
11
+ if @content
12
+ h1(class: "text-xl font-semibold mb-3 border-b-2 border-border pb-3") do
13
+ "Preview for Model"
14
+ end
15
+ pre(class: "bg-panel border-1 border-border rounded-xl p-4") { @content }
16
+ button(
17
+ type: "submit",
18
+ form: "model_form",
19
+ name: "commit",
20
+ value: "Create model file",
21
+ class: "bg-accent shadow-accent rounded-xl p-1 px-4 py-2 mt-2"
22
+ ) { "Create model file" }
23
+ else
24
+ h1(class: "text-xl font-semibold") { "Please fill out the form to see the preview" }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Models::Sidebar < Components::Base
6
+ include Phlex::Rails::Helpers::TurboFrameTag
7
+
8
+ def initialize
9
+ end
10
+
11
+ def view_template
12
+ div(class: "flex flex-col gap-2", data: { controller: "search" }) do
13
+ render Components::Databasium::Forms::Search.new(
14
+ url: databasium.sidebar_models_path,
15
+ turbo_frame: "results",
16
+ placeholder: "Search for a model"
17
+ )
18
+ turbo_frame_tag("results", src: databasium.sidebar_models_path) do
19
+ p(class: "mt-2 animate-pulse") { "Loading models..." }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,99 @@
1
+ module Components
2
+ module Databasium
3
+ class Models::Templates::Attribute < Models::Templates::Base
4
+ def initialize(name: nil, params: nil)
5
+ @name = name
6
+ @params = params
7
+ end
8
+
9
+ def view_template
10
+ div(data: { controller: "attribute" }) { render_attribute_fields }
11
+ end
12
+
13
+ private
14
+
15
+ def render_attribute_fields
16
+ render_collapsable(
17
+ name: @name || "Attribute",
18
+ form: nil,
19
+ name_params: {
20
+ data: {
21
+ attribute_target: "name"
22
+ }
23
+ },
24
+ class_name: ""
25
+ ) do
26
+ div(class: "flex flex-col gap-2 bg-background p-3") do
27
+ div(class: "flex items-center gap-2") do
28
+ input(
29
+ type: "text",
30
+ name: "model[attributes][][name]",
31
+ value: @name,
32
+ data: {
33
+ action: "input->attribute#updateName",
34
+ attribute_target: "nameInput"
35
+ },
36
+ placeholder: "Attribute Name (e.g. email)",
37
+ class:
38
+ "border-2 rounded-xl px-2 py-1 border-border w-full h-full bg-background focus:outline-none"
39
+ )
40
+ render Components::Databasium::TypeSelect.new(
41
+ name: "model[attributes][][type]",
42
+ value: @params&.fetch(:type, nil)
43
+ )
44
+ end
45
+ attr_validations = @params&.fetch(:validations, nil)
46
+ render_validations(validations: attr_validations)
47
+ render_remove_button
48
+ end
49
+ end
50
+ end
51
+
52
+ def render_remove_button
53
+ button(
54
+ type: "button",
55
+ class: "text-red-500",
56
+ data: {
57
+ action: "click->attribute#remove"
58
+ }
59
+ ) { span { "Remove" } }
60
+ end
61
+
62
+ def render_validations(validations: nil)
63
+ render_collapsable(
64
+ name: "Validations",
65
+ form: nil,
66
+ data_targets: {
67
+ data: {
68
+ attribute_target: "validations"
69
+ }
70
+ },
71
+ class_name: "border-collapse border-1 border-border py-2 rounded-xl"
72
+ ) do
73
+ div(class: "flex items-center gap-2 py-1 px-3") do
74
+ span(class: "font-semibold") { "Add new Validation" }
75
+ button(
76
+ data: {
77
+ action: "click->model#add click->attribute#updateValidationName",
78
+ model_target_param: "validation",
79
+ model_container_param: "validationsContainer"
80
+ },
81
+ type: "button",
82
+ class: "text-blue-500"
83
+ ) { heroicon "plus-circle", variant: :outline, options: { class: "w-8 h-8" } }
84
+ end
85
+ div(
86
+ class: "flex flex-col gap-2 divide-y divide-border",
87
+ data: {
88
+ model_target: "validationsContainer"
89
+ }
90
+ ) do
91
+ validations&.each do |validation|
92
+ render Models::Templates::Validation.new(validation: validation, name: @name)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,6 @@
1
+ module Components
2
+ module Databasium
3
+ class Models::Templates::Base < Components::Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,56 @@
1
+ # </template>
2
+
3
+ module Components
4
+ module Databasium
5
+ class Models::Templates::Relation < Models::Templates::Base
6
+ def initialize(relation: nil, models: nil)
7
+ @relation = relation
8
+ @models = models
9
+ end
10
+
11
+ def view_template
12
+ selected_relation = @relation&.fetch(:name, nil)
13
+ selected_model = @relation&.fetch(:type, nil)&.classify
14
+ div(class: "flex gap-2") do
15
+ select(
16
+ name: "model[relations][][type]",
17
+ class: "border-2 rounded-xl p-1 border-border w-full bg-background focus:outline-none"
18
+ ) do
19
+ [
20
+ [ "belongs_to", "Belongs To" ],
21
+ [ "has_many", "Has Many" ],
22
+ [ "has_one", "Has One" ],
23
+ [ "has_and_belongs_to_many", "Has And Belongs To Many" ]
24
+ ].each do |value, label|
25
+ option(value: value, selected: selected_relation == value) { label }
26
+ end
27
+ end
28
+ input(
29
+ type: "search",
30
+ name: "model[relations][][table_name]",
31
+ placeholder: "Search or select model",
32
+ class: "border-2 rounded-xl p-1 border-border w-full bg-background focus:outline-none",
33
+ value: selected_model,
34
+ list: "models_datalist"
35
+ ) { }
36
+ button(
37
+ type: "button",
38
+ class: "text-red-500",
39
+ data: {
40
+ action: "click->relation#removeRelation"
41
+ }
42
+ ) { heroicon "x-mark", variant: :solid, options: { class: "w-8 h-8" } }
43
+ end
44
+ render_models_datalist
45
+ end
46
+
47
+ private
48
+
49
+ def render_models_datalist
50
+ datalist(id: "models_datalist") do
51
+ @models&.each { |model| option(value: model.classify) { model } }
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Models::Templates::Validation < Models::Templates::Base
6
+ TYPES = {
7
+ presence: "Presence",
8
+ uniqueness: "Uniqueness",
9
+ inclusion: "Inclusion",
10
+ exclusion: "Exclusion",
11
+ validates_associated: "Validates Associated",
12
+ numericality: "Numericality",
13
+ length: "Length",
14
+ acceptance: "Acceptance",
15
+ absence: "Absence",
16
+ confirmation: "Confirmation",
17
+ comparison: "Comparison",
18
+ format: "Format"
19
+ }.freeze
20
+
21
+ def initialize(validation: nil, name: nil)
22
+ @validation = validation
23
+ @name = name
24
+ @selected_validation = @validation&.fetch(:type, nil)
25
+ @validation_value = @validation&.fetch(:value, nil)
26
+ end
27
+
28
+ def view_template
29
+ div(
30
+ class: "p-2",
31
+ data: {
32
+ controller: "validation",
33
+ validation_selected_type_value: @selected_validation
34
+ }
35
+ ) do
36
+ div(class: "flex gap-2") do
37
+ render_validation_name_input
38
+ div(class: "relative") do
39
+ render_validation_type_select
40
+ div(
41
+ class: "absolute -top-1.5 -right-[0.5rem]",
42
+ data: {
43
+ action:
44
+ "mouseenter->validation#showBasicInfo mouseleave->validation#hideBasicInfo"
45
+ }
46
+ ) do
47
+ heroicon "information-circle", variant: :solid, options: { class: "w-5 h-5" }
48
+ render_basic_info
49
+ end
50
+ end
51
+ render_acceptance_suggestions
52
+ render_length_suggestions
53
+ render_uniqueness_suggestions
54
+ render_absence_suggestions
55
+ render_confirmation_suggestions
56
+ render_comparison_suggestions
57
+ render_format_suggestions
58
+ render_inclusion_suggestions
59
+ render_exclusion_suggestions
60
+ render_numericality_suggestions
61
+ render_validates_associated_suggestions
62
+ render_presence_suggestions
63
+ render_validation_value_input
64
+ render_x_button(action: "click->validation#remove")
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def render_basic_info
72
+ div(
73
+ data: {
74
+ validation_target: "basicInfo"
75
+ },
76
+ class:
77
+ "hidden absolute z-20 w-64 p-4 mt-2 text-sm text-text bg-background border border-border rounded-xl shadow-xl bottom-full left-1/2 -translate-x-1/2"
78
+ ) do
79
+ div(class: "relative") do
80
+ p(class: "font-semibold") { "Basic use case" }
81
+ p(class: "text-text", data: { validation_target: "basicInfoText" }) { "" }
82
+ div(
83
+ class:
84
+ "absolute -bottom-5.5 left-1/2 -translate-x-1/2 w-3 h-3 bg-background border-b border-r border-border rotate-45"
85
+ )
86
+ end
87
+ end
88
+ end
89
+
90
+ def render_validation_value_input
91
+ div(class: "flex gap-2 px-2 mt-1 relative w-full") do
92
+ input(
93
+ type: "search",
94
+ list: "acceptance-options",
95
+ name: "model[attributes][][validations][][value]",
96
+ class: "border-1 p-1 border-border w-full bg-background focus:outline-none",
97
+ placeholder: "Value (e.g. true)",
98
+ value: @validation_value,
99
+ data: {
100
+ validation_target: "valueInput"
101
+ }
102
+ )
103
+ div(class: "absolute -top-4 right-0 flex gap-1") do
104
+ render_add_additional_options_button(action: "allowNil", text: "Allow nil")
105
+ render_add_additional_options_button(action: "allowBlank", text: "Allow blank")
106
+ render_add_additional_options_button(action: "onAction", text: "On Action")
107
+ end
108
+ end
109
+ end
110
+
111
+ def render_validation_type_select
112
+ select(
113
+ name: "model[attributes][][validations][][type]",
114
+ class: "border-2 rounded-xl p-1 border-border w-fill bg-background focus:outline-none",
115
+ data: {
116
+ action: "change->validation#updateType"
117
+ }
118
+ ) do
119
+ TYPES.each do |value, label|
120
+ option(value: value.to_s, selected: @selected_validation == value.to_s) { label }
121
+ end
122
+ end
123
+ end
124
+
125
+ def render_validation_name_input
126
+ input(
127
+ type: "text",
128
+ name: "model[attributes][][validations][][name]",
129
+ value: @name,
130
+ data: {
131
+ attribute_target: "nameValidationInput"
132
+ },
133
+ class: "hidden"
134
+ )
135
+ end
136
+
137
+ def render_absence_suggestions
138
+ datalist(id: "absence-options") { render_base_boolean_values }
139
+ end
140
+
141
+ def render_acceptance_suggestions
142
+ datalist(id: "acceptance-options") do
143
+ render_base_boolean_values
144
+ option(value: '{ message: "must be agreed to" }') { "With message" }
145
+ option(value: '{ accept: "yes" }') { "Accept" }
146
+ end
147
+ end
148
+
149
+ def render_confirmation_suggestions
150
+ datalist(id: "confirmation-options") do
151
+ render_base_boolean_values
152
+ option(value: "{ case_sensitive: false }") { "Case insensitive" }
153
+ end
154
+ end
155
+
156
+ def render_comparison_suggestions
157
+ datalist(id: "comparison-options") do
158
+ option(value: "{ greater_than: 10 }") { "Greater than" }
159
+ option(value: "{ greater_than_or_equal_to: 10 }") { "Greater than or equal to" }
160
+ option(value: "{ equal_to: 10 }") { "Equal to" }
161
+ option(value: "{ less_than: 10 }") { "Less than" }
162
+ option(value: "{ less_than_or_equal_to: 10 }") { "Less than or equal to" }
163
+ option(value: "{ other_than: 10 }") { "Other than" }
164
+ end
165
+ end
166
+
167
+ def render_format_suggestions
168
+ datalist(id: "format-options") do
169
+ option(value: "{ with: /[A-Z]/ }") { "With" }
170
+ option(value: "{ without: /[A-Z]/ }") { "Without" }
171
+ option(value: "{ with: /[A-Z]/, message: 'must be uppercase' }") { "With and message" }
172
+ option(value: "{ without: /[A-Z]/, message: 'must be lowercase' }") do
173
+ "Without and message"
174
+ end
175
+ end
176
+ end
177
+
178
+ def render_inclusion_suggestions
179
+ datalist(id: "inclusion-options") do
180
+ option(value: "{ in: %w[a b c] }") { "Inclusion" }
181
+ option(value: "{ in: ->(i) { i.method } }") { "Inclusion with proc" }
182
+ option(value: "{ in: %w[a b c], message: 'must be a, b, or c' }") do
183
+ "Inclusion and message"
184
+ end
185
+ end
186
+ end
187
+
188
+ def render_exclusion_suggestions
189
+ datalist(id: "exclusion-options") do
190
+ option(value: "{ in: %w[a b c] }") { "Exclusion" }
191
+ option(value: "{ in: ->(i) { i.method } }") { "Exclusion with proc" }
192
+ option(value: "{ in: %w[a b c], message: 'must be a, b, or c' }") do
193
+ "Exclusion and message"
194
+ end
195
+ end
196
+ end
197
+
198
+ def render_length_suggestions
199
+ datalist(id: "length-options") do
200
+ option(value: "{ minimum: 2 }") { "Minimum" }
201
+ option(value: "{ maximum: 10 }") { "Maximum" }
202
+ option(value: "{ in: 2..10 }") { "Minimum and maximum" }
203
+ option(value: "{ is: 10 }") { "Exact length" }
204
+ option(value: "{ in: 2..10, too_short: 'is too short', too_long: 'is too long' }") do
205
+ "Minimum and maximum with messages"
206
+ end
207
+ option(value: "{ in: 2..10, wrong_length: 'is the wrong length' }") do
208
+ "Minimum and maximum with generic message"
209
+ end
210
+ end
211
+ end
212
+
213
+ def render_numericality_suggestions
214
+ datalist(id: "numericality-options") do
215
+ render_base_boolean_values
216
+ option(value: "{ only_integer: true }") { "Only integer" }
217
+ option(value: "{ greater_than: 10 }") { "Greater than" }
218
+ option(value: "{ greater_than_or_equal_to: 10 }") { "Greater than or equal to" }
219
+ option(value: "{ equal_to: 10 }") { "Equal to" }
220
+ option(value: "{ less_than: 10 }") { "Less than" }
221
+ option(value: "{ less_than_or_equal_to: 10 }") { "Less than or equal to" }
222
+ option(value: "{ other_than: 10 }") { "Other than" }
223
+ option(value: "{ in: 1..10 }") { "In range" }
224
+ option(value: "{ odd: true }") { "Odd" }
225
+ option(value: "{ even: true }") { "Even" }
226
+ end
227
+ end
228
+
229
+ def render_presence_suggestions
230
+ datalist(id: "presence-options") { render_base_boolean_values }
231
+ end
232
+
233
+ def render_uniqueness_suggestions
234
+ datalist(id: "uniqueness-options") do
235
+ render_base_boolean_values
236
+ option(value: "{ scope: :column_name }") { "Scope" }
237
+ option(value: "{ scope: :column_name, message: 'must be unique' }") do
238
+ "Scope and with message"
239
+ end
240
+ option(value: "{ case_sensitive: false }") { "Case insensitive" }
241
+ option(value: '{ conditions: -> { where(column: "value") } }') { "Conditions" }
242
+ end
243
+ end
244
+
245
+ def render_validates_associated_suggestions
246
+ datalist(id: "validates_associated-options") do
247
+ option(value: ":model_name") { "Validates associated" }
248
+ end
249
+ end
250
+
251
+ def render_base_boolean_values
252
+ option(value: "true") { "If you woud like to true" }
253
+ option(value: "false") { "False" }
254
+ end
255
+
256
+ def render_add_additional_options_button(action: nil, text: nil)
257
+ button(
258
+ type: "button",
259
+ class:
260
+ "text-text text-xs p-0.5 px-2 border-1 border-border rounded-xl bg-panel hover:bg-panel-hover cursor-pointer",
261
+ data: {
262
+ action: "validation##{action}"
263
+ }
264
+ ) { text }
265
+ end
266
+
267
+ def render_string_value_suggestions
268
+ datalist(id: "string-options") do
269
+ option(value: "true") { "If you woud like to true" }
270
+ option(value: "false") { "False" }
271
+ end
272
+ end
273
+
274
+ def render_button_suggestion(value: nil, type: nil)
275
+ button(
276
+ type: "button",
277
+ class: "text-text",
278
+ data: {
279
+ validation_target: "#{value}#{type&.upcase}"
280
+ }
281
+ ) { value }
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,32 @@
1
+ module Components
2
+ module Databasium
3
+ class Navigation::BaseIcon < Components::Base
4
+ include Phlex::Rails::Helpers::LinkTo
5
+ include ::Databasium::HeroiconHelper
6
+
7
+ attr_reader :element
8
+
9
+ def initialize(element:)
10
+ @element = element
11
+ end
12
+
13
+ protected
14
+
15
+ def icon_classes
16
+ class_names(
17
+ "flex justify-between items-center gap-2
18
+ border-1 border-border p-1 px-3 rounded-xl cursor-pointer hover:border-hover",
19
+ "bg-accent shadow-accent" => element[:active]
20
+ )
21
+ end
22
+
23
+ def render_icon(icon)
24
+ heroicon icon, variant: :outline, options: { class: "w-6 h-6" }
25
+ end
26
+
27
+ def render_text(text)
28
+ p(class: "text-main-text text-base text-nowrap") { text }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,17 @@
1
+ module Components
2
+ module Databasium
3
+ class Navigation::FrontendIcon < Navigation::BaseIcon
4
+ def initialize(element:, data_params: {})
5
+ super(element: element)
6
+ @data_params = data_params
7
+ end
8
+
9
+ def view_template
10
+ div(class: icon_classes, data: @data_params) do
11
+ render_icon(element[:icon])
12
+ render_text(element[:text])
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module Components
2
+ module Databasium
3
+ class Navigation::GetIcon < Navigation::BaseIcon
4
+ include Phlex::Rails::Helpers::LinkTo
5
+
6
+ def initialize(element:, turbo_frame: nil)
7
+ super(element: element)
8
+ @turbo_frame = turbo_frame
9
+ end
10
+
11
+ def view_template
12
+ link_to(
13
+ element[:path],
14
+ class: icon_classes,
15
+ data: {
16
+ turbo_method: :get,
17
+ turbo_stream: true
18
+ }
19
+ ) do
20
+ render_icon(element[:icon])
21
+ render_text(element[:text])
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ module Components
2
+ module Databasium
3
+ class Navigation::Icon < Components::Base
4
+ include Phlex::Rails::Helpers::LinkTo
5
+
6
+ attr_reader :element
7
+
8
+ def initialize(element:)
9
+ @element = element
10
+ end
11
+
12
+ def view_template
13
+ case element[:method]
14
+ when :frontend
15
+ render Navigation::FrontendIcon.new(element: element, data_params: element[:data_params])
16
+ when :get
17
+ render Navigation::GetIcon.new(element: element, turbo_frame: element[:turbo_frame])
18
+ when :post
19
+ render Navigation::PostIcon.new(element: element)
20
+ when :put
21
+ render Navigation::PutIcon.new(element: element)
22
+ else
23
+ p { "Please provide method to icon component" }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Navigation::IconPanel < Components::Base
6
+ include Phlex::Rails::Helpers::LinkTo
7
+
8
+ # element format: { icon: "icon-name", text: "text", path: "path", method: :get | :post | frontend, turbo_frame: "frame_id" }
9
+ def initialize(icons_with_text: [])
10
+ @icons_with_text = icons_with_text
11
+ end
12
+
13
+ def view_template(&block)
14
+ form(&block)
15
+ end
16
+
17
+ private
18
+
19
+ def form(&block)
20
+ div(class: "align-items-center justify-items-center flex gap-2") do
21
+ @icons_with_text.each { |element| render Navigation::Icon.new(element: element) }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module Components
2
+ module Databasium
3
+ class Navigation::PostIcon < Navigation::BaseIcon
4
+ include Phlex::Rails::Helpers::LinkTo
5
+
6
+ def initialize(element:)
7
+ super(element: element)
8
+ end
9
+
10
+ def view_template
11
+ link_to(
12
+ element[:path],
13
+ class: icon_classes,
14
+ data: {
15
+ turbo_method: :post,
16
+ turbo_frame: "_top"
17
+ }
18
+ ) do
19
+ render_icon(element[:icon])
20
+ render_text(element[:text])
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module Components
2
+ module Databasium
3
+ class Navigation::PutIcon < Navigation::BaseIcon
4
+ include Phlex::Rails::Helpers::LinkTo
5
+
6
+ def initialize(element:)
7
+ super(element: element)
8
+ end
9
+
10
+ def view_template
11
+ link_to(element[:path], class: icon_classes, data: { turbo_method: :put }) do
12
+ render_icon(element[:icon])
13
+ render_text(element[:text])
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end