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,58 @@
1
+ module Components
2
+ module Databasium
3
+ class Migrations::File < Components::Base
4
+ include Phlex::Rails::Helpers::TurboFrameTag
5
+ include Phlex::Rails::Helpers::LinkTo
6
+ include Phlex::Rails::Helpers::ButtonTo
7
+ include Phlex::Rails::Helpers::ContentFor
8
+
9
+ def initialize(migration:, content:)
10
+ @migration = migration
11
+ @content = content
12
+ end
13
+
14
+ def view_template
15
+ turbo_frame_tag(
16
+ "migration",
17
+ class: "flex flex-col flex-1 p-4 w-full min-h-0 overflow-hidden"
18
+ ) do
19
+ h1(class: "text-2xl font-bold mb-2 text-ellipsis break-all") { @migration.name }
20
+ render_content
21
+ render_extra_info
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def render_content
28
+ pre(
29
+ class:
30
+ "min-h-0 flex-1 border-1 border-border bg-panel text-main-text p-4 overflow-auto rounded-2xl scrollbar-thin"
31
+ ) { @content }
32
+ end
33
+
34
+ def render_extra_info
35
+ h2(class: "text-lg font-semibold my-2") { "Extra info: " }
36
+ div(class: "grid grid-cols-[max-content_1fr] gap-4 w-fill break-all") do
37
+ ul(class: "list-disc list-inside") do
38
+ li { "Timestamp: " }
39
+ li { "Last time modified: " }
40
+ li { "Name: " }
41
+ li { "File path: " }
42
+ end
43
+ div do
44
+ p(class: "font-bold text-wrap") { @migration.version }
45
+ p(class: "font-bold") do
46
+ Time
47
+ .strptime(@migration.version.to_s, "%Y%m%d%H%M%S")
48
+ .localtime
49
+ .strftime("%Y-%m-%d at %H:%M:%S")
50
+ end
51
+ p(class: "font-bold") { @migration.name }
52
+ p(class: "font-bold") { @migration.filename }
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Migrations::Form < Components::Base
6
+ include Phlex::Rails::Helpers::FormWith
7
+ include Phlex::Rails::Helpers::TurboFrameTag
8
+
9
+ def initialize(tables:, content:)
10
+ @tables = tables
11
+ @content = content
12
+ end
13
+
14
+ def view_template
15
+ form_with(
16
+ url: helpers.migrations_path,
17
+ method: :post,
18
+ id: "migration_form",
19
+ data: {
20
+ controller: "new-migration",
21
+ turbo_frame: "_top"
22
+ },
23
+ class:
24
+ "flex flex-col gap-4 min-w-fit w-1/3 bg-panel border-1 border-border rounded-xl p-4"
25
+ ) do |form|
26
+ render_migration_action(form)
27
+ render_table_name(form)
28
+ render_columns(form)
29
+ render_validations(form)
30
+ render_table_datalist
31
+ form.submit "Generate Preview",
32
+ class: "bg-blue-500 p-2 rounded-md w-fit",
33
+ name: "add_migration"
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def render_validations(form)
40
+ render_collapsable_with_button(
41
+ form: form,
42
+ name: "validations",
43
+ data_targets: {
44
+ new_migration_target: "validations"
45
+ },
46
+ button_text: "Add Validation",
47
+ button_action: "click->new-migration#addValidation",
48
+ name_params: {
49
+ new_migration_target: "validation"
50
+ },
51
+ hidden: true
52
+ ) do
53
+ select(
54
+ name: "validation[][column_name]",
55
+ class: "p-2 border-1 border-border rounded-md w-fit bg-background",
56
+ data: {
57
+ new_migration_target: "validation_column_name"
58
+ }
59
+ ) { option(value: "") { "Select column" } }
60
+ select(
61
+ name: "validation[][type]",
62
+ class: "p-2 border-1 border-border rounded-md w-fit bg-background",
63
+ data: {
64
+ new_migration_target: "validation_column_type"
65
+ }
66
+ ) do
67
+ option(value: "uniqueness") { "Uniqueness" }
68
+ option(value: "not_null") { "Not Null" }
69
+ end
70
+ render_remove_button(action: "click->new-migration#removeValidation")
71
+ end
72
+ end
73
+
74
+ def render_columns(form)
75
+ render_collapsable_with_button(
76
+ form: form,
77
+ name: "columns",
78
+ data_targets: {
79
+ new_migration_target: "columns"
80
+ },
81
+ button_text: "Add Column",
82
+ button_action: "click->new-migration#addColumn",
83
+ name_params: {
84
+ new_migration_target: "column"
85
+ },
86
+ hidden: true
87
+ ) do
88
+ div(class: "flex flex-col gap-2 relative mt-2") do
89
+ if @model
90
+ form.select :column_name,
91
+ @model.columns.map(&:name),
92
+ include_blank: "Select a column name",
93
+ class: "p-2 border-1 border-border rounded-md w-fit bg-background"
94
+ else
95
+ form.label "Column Name", class: minimalistic_label_class
96
+ form.text_field(
97
+ :column_name,
98
+ name: "columns[][column_name]",
99
+ class: "p-1 border-1 rounded-xl border-border h-fit bg-background",
100
+ data: {
101
+ action: "change->new-migration#updateColumnNames"
102
+ }
103
+ )
104
+ end
105
+ end
106
+ div(class: "flex flex-col gap-2 h-full relative") do
107
+ form.label "Column Name", class: minimalistic_label_class
108
+ render Components::Databasium::TypeSelect.new(name: "columns[][column_type]")
109
+ end
110
+ render_remove_button(action: "click->new-migration#removeColumn")
111
+ end
112
+ end
113
+
114
+ def render_table_name(form)
115
+ render_collapsable_with_button(
116
+ form: form,
117
+ name: "table_name",
118
+ data_targets: {
119
+ new_migration_target: "table_name"
120
+ }
121
+ ) do
122
+ form.text_field :table_name,
123
+ class: "p-2 border-1 border-border w-fit bg-background rounded-md"
124
+ end
125
+ end
126
+
127
+ def render_migration_action(form)
128
+ render_collapsable_with_button(
129
+ form: form,
130
+ name: "migration_action",
131
+ data_targets: {
132
+ new_migration_target: "migration_action"
133
+ }
134
+ ) do
135
+ div(class: "flex flex-col") do
136
+ form.label :migration_action, "Action", class: "text-sm font-semibold"
137
+ form.select :migration_action,
138
+ %w[create remove add],
139
+ {},
140
+ class: "p-2 border-1 border-border rounded-md w-fit bg-background",
141
+ data: {
142
+ action: "change->new-migration#set_action"
143
+ }
144
+ end
145
+ div(class: "flex flex-col", data: { new_migration_target: "add_model_container" }) do
146
+ form.label :add_model, "Add also model", class: "text-sm font-semibold"
147
+ div(class: "h-11 flex items-center justify-center") do
148
+ form.check_box :add_model,
149
+ checked: true,
150
+ class: "w-4 h-4",
151
+ data: {
152
+ new_migration_target: "add_model"
153
+ }
154
+ end
155
+ end
156
+
157
+ div(class: "flex flex-col hidden", data: { new_migration_target: "table_name_from" }) do
158
+ form.label :table_name_from, "Table to remove from", class: "text-sm font-semibold"
159
+ render_tables_search_field(form, :table_name_from)
160
+ end
161
+
162
+ div(class: "flex flex-col hidden", data: { new_migration_target: "table_name_to" }) do
163
+ form.label :table_name_to, "Table to add to", class: "text-sm font-semibold"
164
+ render_tables_search_field(form, :table_name_to)
165
+ end
166
+ end
167
+ end
168
+
169
+ def render_tables_search_field(form, name)
170
+ form.search_field name,
171
+ placeholder: "Search for a table",
172
+ list: "table_name_datalist",
173
+ class:
174
+ "p-2 border-1 border-border rounded-md w-fit bg-background leading-5"
175
+ end
176
+
177
+ def render_table_datalist
178
+ datalist(id: "table_name_datalist") do
179
+ @tables.each { |table| option(value: table) { table } }
180
+ end
181
+ end
182
+
183
+ def render_collapsable_with_button(
184
+ form:,
185
+ name:,
186
+ data_targets:,
187
+ button_text: nil,
188
+ button_action: nil,
189
+ name_params: {},
190
+ hidden: false,
191
+ &block
192
+ )
193
+ render_collapsable(
194
+ form: form,
195
+ name: name,
196
+ data_targets: data_targets,
197
+ class_name: "border-1 border-border rounded-xl py-1"
198
+ ) do
199
+ div(
200
+ class: "flex gap-2 items-center px-2 mt-2 mb-2 #{hidden ? "hidden" : ""}",
201
+ data: name_params
202
+ ) { yield if block_given? }
203
+ if button_text.present? && button_action.present?
204
+ button(
205
+ type: "button",
206
+ class: "text-accent ms-2 mt-1",
207
+ data: {
208
+ action: button_action
209
+ }
210
+ ) { heroicon "plus-circle", variant: :outline, options: { class: "w-8 h-8" } }
211
+ end
212
+ end
213
+ end
214
+
215
+ def render_remove_button(action: nil)
216
+ button(type: "button", class: "text-red-500 h-fit self-center", data: { action: action }) do
217
+ heroicon "x-mark", variant: :solid, options: { class: "w-8 h-8" }
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Migrations::HeaderActions < Components::Base
6
+ include Phlex::Rails::Helpers::LinkTo
7
+ include Phlex::Rails::Helpers::Routes
8
+ include Phlex::Rails::Helpers::TurboFrameTag
9
+ include Phlex::Rails::Helpers::ButtonTo
10
+ include Phlex::Rails::Helpers::FormWith
11
+
12
+ def initialize(migration:)
13
+ @migration = migration
14
+ end
15
+
16
+ def view_template
17
+ div(id: "header_actions", class: "max-w-full overflow-x-auto flex") do
18
+ render Components::Databasium::Navigation::IconPanel.new(
19
+ icons_with_text: [
20
+ *(
21
+ if @migration
22
+ render_migration_actions
23
+ else
24
+ []
25
+ end
26
+ ),
27
+ {
28
+ icon: "document-plus",
29
+ text: "New Migration",
30
+ method: :get,
31
+ path: databasium.new_migration_path,
32
+ turbo_frame: "main"
33
+ },
34
+ {
35
+ icon: "forward",
36
+ text: "Run Pending Migrations",
37
+ method: :post,
38
+ path: databasium.run_pending_migrations_migrations_path,
39
+ turbo_frame: "main"
40
+ }
41
+ ]
42
+ )
43
+ form_with(
44
+ url: databasium.rollback_migration_migrations_path,
45
+ method: :post,
46
+ class: "w-fit flex gap-2 border-1 border-border rounded-xl bg-background text-sm mx-2"
47
+ ) do |form|
48
+ form.number_field :rollback_steps,
49
+ placeholder: "Rollback steps",
50
+ class: "ps-4 w-35 focus:outline-none"
51
+ form.submit "Rollback",
52
+ class: "hover:cursor-pointer hover:text-hover text-accent w-fit pe-4"
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def render_migration_actions
60
+ [
61
+ {
62
+ icon: "arrow-up",
63
+ method: :post,
64
+ text: "Run Migration",
65
+ path: databasium.run_migration_migrations_path(version: @migration.version)
66
+ },
67
+ {
68
+ icon: "arrow-uturn-left",
69
+ method: :post,
70
+ text: "Rollback",
71
+ path: databasium.rollback_migration_migrations_path(version: @migration.version)
72
+ },
73
+ {
74
+ icon: "backward",
75
+ method: :post,
76
+ text: "Rollback till this migration",
77
+ path:
78
+ databasium.rollback_migration_migrations_path(
79
+ version: @migration.version,
80
+ till_this_migration: true
81
+ )
82
+ }
83
+ ]
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Migrations::MigrationStatus < Components::Base
6
+ def initialize(status:, version:)
7
+ @status = status
8
+ @version = version
9
+ end
10
+
11
+ def view_template
12
+ div(id: "migration_#{@version}_status") do
13
+ if @status == "pending"
14
+ span(class: "text-red-500") { "Pending" }
15
+ else
16
+ span(class: "text-green-500") { "Applied" }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module Components
2
+ module Databasium
3
+ class Migrations::Preview < Components::Base
4
+ def initialize(content:)
5
+ @content = content
6
+ end
7
+
8
+ def view_template
9
+ div(id: "migration_preview") do
10
+ div(class: "bg-panel border-1 border-border p-4 rounded-2xl min-h-full") do
11
+ if @content
12
+ h1(class: "text-xl font-semibold mb-3") { "Preview Migration" }
13
+ pre { @content }
14
+ button(
15
+ type: "submit",
16
+ form: "migration_form",
17
+ name: "add_migration",
18
+ value: "Save",
19
+ class: "bg-accent p-2 rounded-md w-fit mt-4"
20
+ ) { "Save" }
21
+ else
22
+ h1(class: "text-xl font-semibold") { "Please fill out the form to see the preview" }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ class Components::Databasium::Migrations::ShowTurboStream < Components::Base
2
+ include Phlex::Rails::Helpers::TurboStream
3
+
4
+ def initialize(migration:, content:)
5
+ @migration = migration
6
+ @content = content
7
+ end
8
+
9
+ def view_template
10
+ turbo_stream.replace(
11
+ "migration",
12
+ Components::Databasium::Migrations::File.new(migration: @migration, content: @content)
13
+ )
14
+ turbo_stream.replace(
15
+ "header_actions",
16
+ Components::Databasium::Migrations::HeaderActions.new(migration: @migration)
17
+ )
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Migrations::Sidebar < Components::Base
6
+ include Phlex::Rails::Helpers::LinkTo
7
+ include Phlex::Rails::Helpers::FormWith
8
+ include Phlex::Rails::Helpers::ButtonTo
9
+ include Phlex::Rails::Helpers::TurboFrameTag
10
+
11
+ def initialize
12
+ end
13
+
14
+ def view_template
15
+ div(class: "flex flex-col gap-2", data: { controller: "search" }) do
16
+ render Components::Databasium::Forms::Search.new(
17
+ url: databasium.sidebar_migrations_path,
18
+ turbo_frame: "results",
19
+ placeholder: "Search for a migration"
20
+ )
21
+ turbo_frame_tag("results", src: databasium.sidebar_migrations_path) do
22
+ p(class: "mt-2 animate-pulse") { "Loading migrations..." }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,49 @@
1
+ module Components
2
+ module Databasium
3
+ class Models::Attributes < Components::Base
4
+ def initialize(attributes: nil, models: nil)
5
+ @attributes = attributes
6
+ @models = models
7
+ end
8
+
9
+ def view_template
10
+ @attributes
11
+ .fetch(:unknown, [])
12
+ .each do |line|
13
+ input(type: "hidden", name: "model[unknown][]", value: line.fetch(:line, line["line"]))
14
+ end
15
+ render_collapsable(
16
+ name: "Pre-filled attributes",
17
+ form: nil,
18
+ class_name: "bg-panel rounded-xl py-2"
19
+ ) do
20
+ div(
21
+ class: "rounded-b-xl border border-border overflow-hidden divide-y divide-border mt-2"
22
+ ) do
23
+ @attributes
24
+ &.fetch(:columns_hash)
25
+ &.each do |name, attribute|
26
+ render Models::Templates::Attribute.new(name: name, params: attribute)
27
+ end
28
+ end
29
+ end
30
+ render_collapsable(
31
+ name: "Pre-filled relations",
32
+ form: nil,
33
+ data_targets: {
34
+ controller: "relation"
35
+ },
36
+ class_name: "bg-panel mt-2 rounded-xl py-2"
37
+ ) do
38
+ div(class: "flex flex-col gap-2 py-2 px-3") do
39
+ @attributes
40
+ &.fetch(:relations)
41
+ &.each do |relation|
42
+ render Models::Templates::Relation.new(relation: relation, models: @models)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Models::Form < Components::Base
6
+ include Phlex::Rails::Helpers::FormWith
7
+
8
+ def initialize(attributes: nil, model: nil, models: nil)
9
+ @attributes = attributes
10
+ @model = model
11
+ @models = models
12
+ end
13
+
14
+ def view_template
15
+ form_with(
16
+ url: databasium.models_path,
17
+ method: :post,
18
+ scope: :model,
19
+ html: {
20
+ id: "model_form"
21
+ }
22
+ ) do |form|
23
+ render_model_name(form)
24
+ render_form
25
+ render_attributes_container
26
+ render_relations_container
27
+ render_unknown
28
+ form.submit "Create preview for model",
29
+ class: "bg-accent shadow-accent rounded-xl p-1 p-2 mt-2 w-full text-center"
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def render_unknown
36
+ end
37
+
38
+ def render_attributes_container
39
+ render_collapsable(
40
+ name: @model.present? ? "New Attributes" : "Attributes",
41
+ form: nil,
42
+ class_name: "bg-panel rounded-xl py-2 mt-2"
43
+ ) do
44
+ div(
45
+ class: "rounded-b-xl border border-border divide-y divide-border mt-2",
46
+ data: {
47
+ model_target: "attributesContainer"
48
+ }
49
+ ) { }
50
+ end
51
+ end
52
+
53
+ def render_relations_container
54
+ render_collapsable(
55
+ name: @model.present? ? "New Relations" : "Relations",
56
+ form: nil,
57
+ class_name: "bg-panel rounded-xl py-2 mt-2",
58
+ data_targets: {
59
+ controller: "relation"
60
+ }
61
+ ) do
62
+ div(
63
+ class: "flex flex-col gap-2 py-2 px-3",
64
+ data: {
65
+ model_target: "relationsContainer"
66
+ }
67
+ ) { }
68
+ end
69
+ end
70
+
71
+ def render_model_name(form)
72
+ div(class: "flex items-center mb-3 text-xl") do
73
+ form.label :model_name, "Name:", class: "font-semibold pe-2"
74
+ form.text_field :model_name,
75
+ value: @model,
76
+ class:
77
+ "border-1 w-full rounded-xl p-1 border-border bg-panel w-fit focus:outline-none"
78
+ end
79
+ end
80
+
81
+ def render_form
82
+ if @attributes.present?
83
+ render Components::Databasium::Models::Attributes.new(
84
+ attributes: @attributes,
85
+ models: @models
86
+ )
87
+ end
88
+ template(data: { model_target: "attribute" }) do
89
+ render Components::Databasium::Models::Templates::Attribute.new
90
+ end
91
+ template(data: { model_target: "relation" }) do
92
+ render Components::Databasium::Models::Templates::Relation.new(models: @models)
93
+ end
94
+ template(data: { model_target: "validation" }) do
95
+ render Components::Databasium::Models::Templates::Validation.new
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Components
4
+ module Databasium
5
+ class Models::HeaderActions < Components::Base
6
+ include Phlex::Rails::Helpers::LinkTo
7
+ include Phlex::Rails::Helpers::Routes
8
+ include Phlex::Rails::Helpers::TurboFrameTag
9
+ include Phlex::Rails::Helpers::ButtonTo
10
+ def initialize(model: nil)
11
+ @model = model
12
+ end
13
+
14
+ def view_template
15
+ div(id: "header_actions", class: "max-w-full overflow-x-auto") do
16
+ render Components::Databasium::Navigation::IconPanel.new(
17
+ icons_with_text: [
18
+ {
19
+ icon: "plus-circle",
20
+ text: "Add attribute",
21
+ method: :frontend,
22
+ data_params: {
23
+ action: "click->model#add",
24
+ model_target_param: "attribute",
25
+ model_container_param: "attributesContainer"
26
+ }
27
+ },
28
+ {
29
+ icon: "plus-circle",
30
+ text: "Add relation",
31
+ method: :frontend,
32
+ data_params: {
33
+ action: "click->model#add",
34
+ model_target_param: "relation",
35
+ model_container_param: "relationsContainer"
36
+ }
37
+ },
38
+ {
39
+ icon: "document-plus",
40
+ text: "New model",
41
+ method: :get,
42
+ path: databasium.new_model_path,
43
+ turbo_frame: "main"
44
+ }
45
+ ]
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end