glib-web 4.41.0 → 4.42.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3c5782b7132ed122204cc3314927ada0e3804ea47ee795c34d6311a959580b6
4
- data.tar.gz: ca30770101141a9388bb11ef182a6c554ada7cc1ba88b068f7e3e0ef130425f6
3
+ metadata.gz: 10cf86096f7289ec5330a4945455aa966fffa5ec6d25e9eed14a9d1cc05324e5
4
+ data.tar.gz: 3e5c534eb6a24b7643c2ba053a92dbc85854242af338489244d8f229ee8ea86f
5
5
  SHA512:
6
- metadata.gz: eef32c78bfbd00ae4082fbd0069b7a92045d253411dc70cd2a82a6218f6b59984362652e63c9ad64bbad0d26bef567a23f7e9d3edfca125d97eb8cd2e6379529
7
- data.tar.gz: 84039000106e73c327d049234261c182d099e5f66412138ea7e5cd3abe4921898c5ba4c72640acc2cbf7036955d0a6c6b9994c66b7ca77b3512693ca9f3418b1
6
+ metadata.gz: 6f4b740caa11bfc6f41686c03c84fcc507ef0de4ae17d8b317b98fcd301c311bfc72eb9eaeb5ac04fe847828e00826234bc3087f72571143fb653b5e0cd92465
7
+ data.tar.gz: c99131d247a25df28948a755718a63d0d1548eb1d5b149917156340798115a67ab587ceb9c282bbccaeb1e4ddd31e552a9f034c3fa54ca246654c3cbb3f660bc
@@ -131,9 +131,10 @@ module Glib::Json::Libs
131
131
  redirect_to sign_in_url, **redirect_options
132
132
  end
133
133
  format.json do
134
- render \
134
+ render(
135
135
  json: json_ui_401_response(sign_in_url, **redirect_options),
136
136
  status: Rails.env.test? ? :unauthorized : :ok
137
+ )
137
138
  end
138
139
  format.csv do
139
140
  if Rails.env.test?
@@ -230,7 +231,9 @@ module Glib::Json::Libs
230
231
  view: 'label',
231
232
  text: message
232
233
  },
233
- ].concat(bottom_views),
234
+ ].concat(
235
+ bottom_views
236
+ ),
234
237
  }
235
238
  ]
236
239
  # Set this in the project's custom CSS.
@@ -288,6 +291,17 @@ module Glib::Json::Libs
288
291
 
289
292
  module ClassMethods
290
293
 
294
+ # This usually resides in a base controller class, e.g. ApplicationController.
295
+ def __json_libs_init_options
296
+ if instance_variable_defined?(:@__json_libs_init_options)
297
+ @__json_libs_init_options
298
+ elsif superclass.respond_to?(:__json_libs_init_options)
299
+ superclass.__json_libs_init_options
300
+ else
301
+ nil
302
+ end
303
+ end
304
+
291
305
  def json_libs_init(options)
292
306
  include Glib::Json::Transformation
293
307
  include Glib::Json::Validation
@@ -296,6 +310,10 @@ module Glib::Json::Libs
296
310
  include Glib::Json::Traversal
297
311
  include Glib::Json::NewDynamicText
298
312
 
313
+ @__json_libs_init_options = options
314
+
315
+ puts "*********** __json_libs_init_options: #{@__json_libs_init_options}"
316
+
299
317
  before_action do
300
318
  __json_ui_start(options)
301
319
  end
@@ -350,30 +368,37 @@ module Glib::Json::Libs
350
368
  redirect_to instance_exec(&sign_in_url_proc)
351
369
  end
352
370
  format.json do
353
- render json: {
354
- glib_json_dialog_mode? ? :onLoad : :onResponse => __glib_error_dialog(
355
- 'Your session has expired', 'Please refresh the page and retry.', ['dialog-csrf-error'], [
356
- {
357
- view: 'button',
358
- text: 'Refresh',
359
- onClick: {
360
- action: 'auth/saveCsrfToken',
361
- token: form_authenticity_token,
362
- onSave: {
363
- action: 'dialogs/close',
364
- onClose: {
365
- action: 'windows/reload',
366
- onReload: {
367
- action: 'snackbars/alert',
368
- message: 'Refreshed. You can try again now.'
371
+ raise exception if Rails.env.test? || !json_ui_should_render?(self.class.__json_libs_init_options)
372
+
373
+ render(
374
+ json: {
375
+ glib_json_dialog_mode? ? :onLoad : :onResponse => __glib_error_dialog(
376
+ 'Your session has expired',
377
+ 'Please refresh the page and retry.',
378
+ ['dialog-csrf-error'],
379
+ [
380
+ {
381
+ view: 'button',
382
+ text: 'Refresh',
383
+ onClick: {
384
+ action: 'auth/saveCsrfToken',
385
+ token: form_authenticity_token,
386
+ onSave: {
387
+ action: 'dialogs/close',
388
+ onClose: {
389
+ action: 'windows/reload',
390
+ onReload: {
391
+ action: 'snackbars/alert',
392
+ message: 'Refreshed. You can try again now.'
393
+ }
369
394
  }
370
395
  }
371
396
  }
372
- }
373
- },
374
- ]
375
- )
376
- }
397
+ },
398
+ ]
399
+ )
400
+ }
401
+ )
377
402
  end
378
403
  end
379
404
  end
@@ -36,6 +36,16 @@ module Glib::Json::Ui
36
36
  @__json_ui_rendering != nil
37
37
  end
38
38
 
39
+ def json_ui_should_render?(options)
40
+ when_option = options[:when]
41
+ if when_option.is_a?(Proc)
42
+ # should_render = when_option.call(self)
43
+ instance_exec(&when_option)
44
+ else
45
+ when_option == :always
46
+ end
47
+ end
48
+
39
49
  def json_ui_page_lifecycle_prop(name)
40
50
  if (hash = json_transformation_start).is_a?(Hash) && hash['body'] # A valid page
41
51
  original_action = hash[name]
@@ -70,14 +80,7 @@ module Glib::Json::Ui
70
80
  @__json_ui_activated = false
71
81
  @__json_ui_rendering = nil
72
82
 
73
- when_option = options[:when]
74
- if when_option.is_a?(Proc)
75
- # should_render = when_option.call(self)
76
- should_render = instance_exec(&when_option)
77
- else
78
- should_render = when_option == :always
79
- end
80
-
83
+ should_render = json_ui_should_render?(options)
81
84
  if should_render || params[:_render] == 'v1'
82
85
  @__json_ui_activated = true
83
86
  request.variant = :ui
@@ -114,12 +117,14 @@ module Glib::Json::Ui
114
117
 
115
118
  renderer_path = params[:_skip_custom_render] == 'true' ? 'layouts/json_ui/no_custom' : options[:renderer_path]
116
119
  @__json_ui_orig_page = response.body
117
- response.body = render_to_string(
118
- template: renderer_path,
119
- layout: 'json_ui/renderer',
120
- content_type: 'text/html',
121
- formats: [:html],
122
- locals: { page: hash, options: options }
120
+ response.body(
121
+ render_to_string(
122
+ template: renderer_path,
123
+ layout: 'json_ui/renderer',
124
+ content_type: 'text/html',
125
+ formats: [:html],
126
+ locals: { page: hash, options: options }
127
+ )
123
128
  )
124
129
  end
125
130
  end
@@ -59,7 +59,7 @@ module Glib
59
59
  # menu :leftOuterButtons
60
60
  int :imageSize
61
61
 
62
- panels_builder :content, :header, :footer, :right
62
+ panels_builder :content, :header, :footer, :right, :left
63
63
  end
64
64
 
65
65
  class Editable < Standard
@@ -269,6 +269,14 @@ class Glib::JsonUi::ViewBuilder
269
269
  #
270
270
  # @see app/views/json_ui/garage/forms/text_validation.json.jbuilder Garage example
271
271
  class Email < Text
272
+ def created
273
+ super
274
+ # Assume format validation handled by frontend
275
+ if @validation.present? && @validation[:format].present?
276
+ @validation.delete(:format)
277
+ json.validation @validation
278
+ end
279
+ end
272
280
  end
273
281
 
274
282
  # URL input field with URL format validation.
@@ -578,6 +586,7 @@ class Glib::JsonUi::ViewBuilder
578
586
 
579
587
  # To override the default behaviour
580
588
  bool :useChips
589
+ int :maxChipLines
581
590
 
582
591
  def options(value)
583
592
  @_options = value
@@ -274,10 +274,17 @@ module Glib
274
274
  @size = value
275
275
  end
276
276
 
277
+ def family(value)
278
+ @family = value.to_s
279
+ end
280
+
277
281
  # Override
278
282
  def created
279
283
  if @name
280
- json.material do
284
+ icon_type = @family == 'fontawesome' ? 'fa' :
285
+ @family == 'custom' ? 'custom' : 'material'
286
+
287
+ json.set!(icon_type) do
281
288
  json.name @name
282
289
  json.size @size if @size
283
290
  end
@@ -4,7 +4,7 @@ module Glib
4
4
  include ActiveModel::Validations
5
5
  include ActiveModel::AttributeMethods
6
6
 
7
- attr_accessor :name, :age, :position, :words, :accept, :pet_you_have
7
+ attr_accessor :email, :name, :age, :position, :words, :accept, :pet_you_have
8
8
 
9
9
  validates :name, :position, presence: true
10
10
  validates :accept, acceptance: {}, allow_nil: false
@@ -13,5 +13,6 @@ module Glib
13
13
  validates :name, format: { with: /Doe\z/, message: 'This job is for person end with Doe' }
14
14
  validates :position, inclusion: { in: ['programmer', 'devops', 'designer'] }, allow_blank: true
15
15
  validates :pet_you_have, exclusion: { in: ['crocodile', 'shark' ], message: 'We dont accept that kind of animal!' }, allow_blank: true
16
+ validates :email, format: { with: /\.doe\z/, message: "This message is never shown because it's handled by frontend" }
16
17
  end
17
18
  end
@@ -1,14 +1,24 @@
1
- sleep 3
1
+ sleep 0.5
2
2
  page = json_ui_page json
3
3
 
4
- page.body childViews: ->(body) do
5
- body.panels_responsive padding: { all: 16 }, childViews: ->(res) do
6
- res.fields_richText value: "<p>#{DateTime.current.to_fs(:rfc822)}</p><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>"
7
- res.spacer height: 8
8
- res.button text: 'close', onClick: ->(action) do
9
- action.sheets_close onClose: ->(saction) do
10
- saction.snackbars_alert message: 'sheet closed'
4
+ page.body(
5
+ childViews: ->(body) do
6
+ body.panels_responsive(
7
+ padding: { all: 16 },
8
+ childViews: ->(res) do
9
+ res.fields_richText value: "<p>#{DateTime.current.to_fs(:rfc822)}</p><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>"
10
+ res.spacer height: 8
11
+ res.button(
12
+ text: 'close',
13
+ onClick: ->(action) do
14
+ action.sheets_close(
15
+ onClose: ->(saction) do
16
+ saction.snackbars_alert message: 'sheet closed'
17
+ end
18
+ )
19
+ end
20
+ )
11
21
  end
12
- end
22
+ )
13
23
  end
14
- end
24
+ )
@@ -5,6 +5,10 @@ json.title 'Lists'
5
5
  render "#{@path_prefix}/nav_menu", json: json, page: page
6
6
 
7
7
  tab_index = params[:tab].to_i
8
+ batch_count = 20
9
+ editable_ids = (0...batch_count).map do |index|
10
+ "PK_#{(batch_count * tab_index) + index}"
11
+ end
8
12
 
9
13
  page.header(
10
14
  childViews: ->(header) do
@@ -42,7 +46,25 @@ page.form(
42
46
  childViews: ->(header) do
43
47
  header.panels_horizontal(
44
48
  childViews: ->(horizontal) do
45
- horizontal.fields_check name: 'user[check_all]', label: 'All', checkValue: true
49
+ horizontal.fields_check(
50
+ name: 'user[check_all]',
51
+ label: 'All',
52
+ checkValue: true,
53
+ onChange: ->(action) do
54
+ action.runMultiple(
55
+ childActions: ->(multiple) do
56
+ editable_ids.each do |editable_id|
57
+ multiple.logics_set(
58
+ targetId: editable_id,
59
+ conditionalData: {
60
+ value: { "if": [{ "var": 'user[check_all]' }, true, false] }
61
+ }
62
+ )
63
+ end
64
+ end
65
+ )
66
+ end
67
+ )
46
68
  horizontal.spacer width: 20
47
69
  # header.fields_text width: 'matchParent', styleClass: 'outlined', name: 'user[new_name]', label: 'Item name'
48
70
  statuses = [:pending, :active]
@@ -61,23 +83,14 @@ page.form(
61
83
  )
62
84
  section.rows(
63
85
  builder: ->(row) do
64
- batch_count = 20
65
86
  batch_count.times do |index|
66
87
  id = (batch_count * tab_index) + index
67
- row.editable title: "Item #{id}", recordId: "PK_#{id}"
88
+ row.editable id: "PK_#{id}", title: "Item #{id}", recordId: "PK_#{id}"
68
89
  end
69
90
  end
70
91
  )
71
92
  end
72
- ],
73
- fieldCheckValueIf: {
74
- "==": [
75
- {
76
- "var": 'user[check_all]'
77
- },
78
- true
79
93
  ]
80
- }
81
94
  )
82
95
  end
83
96
  )
@@ -1,4 +1,4 @@
1
- navs = ['form', 'dialog', 'form_dynamic', 'auto_validate', 'multiupload', 'dirty_state', 'window', 'selectable', 'lifecycle', 'file_upload_new']
1
+ navs = ['form', 'dialog', 'form_dynamic', 'auto_validate', 'multiupload', 'dirty_state', 'window', 'selectable', 'list_editable', 'lifecycle', 'file_upload_new', 'logics_set', 'http', 'sheets', 'snackbars']
2
2
 
3
3
  view.panels_flow(
4
4
  innerPadding: { bottom: 0 },
@@ -2,76 +2,75 @@ json.title 'Test Page (Form)'
2
2
 
3
3
  page = json_ui_page json
4
4
 
5
- page.body childViews: ->(body) do
6
- render 'json_ui/garage/test_page/header', view: body
7
-
8
- model = Glib::DummyJobApplication.new(
9
- name: 'John Deo',
10
- accept: '0',
11
- words: '',
12
- age: 17,
13
- position: 'ceo',
14
- pet_you_have: 'crocodile'
15
- )
16
-
17
- body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
18
- res.h2 text: 'Job Application'
19
- res.spacer height: 8
20
- res.panels_form \
21
- url: json_ui_garage_url(path: 'forms/generic_post'),
22
- method: 'post',
23
- model: model,
24
- autoValidate: true,
25
- childViews: ->(form) do
26
- form.fields_text prop: :name, label: 'name', name: 'user[name]', width: 'matchParent'
27
- form.spacer height: 4
28
-
29
- form.fields_text label: 'say hello', name: 'user[say]', width: 'matchParent', validation: { format: { regex: 'hello', message: 'Say hello!' } }, value: 'sup'
30
- form.spacer height: 4
31
-
32
- form.fields_number prop: :age, label: 'age', name: 'user[age]', width: 'matchParent'
33
- form.spacer height: 4
34
-
35
- form.fields_number \
36
- name: 'user[height]',
37
- label: 'height',
38
- width: 'matchParent',
39
- validation: { numericality: { greater_than_or_equal_to: 155, less_than_or_equal_to: 190, message: { greater_than_or_equal_to: 'Too short', less_than_or_equal_to: 'Too long' } } },
40
- value: 150
41
- form.spacer height: 4
42
-
43
- form.fields_textarea prop: :words, label: 'words', name: 'user[words]', width: 'matchParent'
44
- form.spacer height: 4
45
-
46
- form.fields_textarea label: 'Tell me story', name: 'user[story]', width: 'matchParent', validation: { length: { minimum: 1, message: { too_short: { one: 'Min %{count} word', other: 'Min %{count} words' } } } }
47
- form.spacer height: 4
48
-
49
- options = ['programmer', 'devops', 'designer', 'ceo', 'office_boy'].map { |v| { text: v.humanize, value: v } }
50
- form.fields_select prop: :position, label: 'postion', name: 'user[position]', width: 'matchParent', options: options
51
- form.spacer height: 4
52
-
53
- options2 = ['crocodile', 'cat', 'dog', 'bird', 'shark'].map { |v| { text: v.humanize, value: v } }
54
- form.fields_select prop: :pet_you_have, label: 'Pet you have', name: 'user[pet_you_have]', width: 'matchParent', options: options2
55
- form.spacer height: 4
56
-
57
- form.label text: 'Decision'
58
- options3 = ['choose me', 'dont choose me', 'whatever']
59
- form.fields_radioGroup value: 'dont choose me', validation: { exclusion: { in: ['dont choose me'], message: 'dont choose me' } }, childViews: ->(radio) do
60
- options3.each do |option|
61
- radio.fields_radio label: option, value: option
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
8
+ model = Glib::DummyJobApplication.new(
9
+ name: 'John Deo',
10
+ accept: '0',
11
+ words: '',
12
+ age: 17,
13
+ position: 'ceo',
14
+ pet_you_have: 'crocodile'
15
+ )
16
+ body.panels_responsive(
17
+ padding: glib_json_padding_body,
18
+ childViews: ->(res) do
19
+ res.h2 text: 'Job Application'
20
+ res.spacer height: 8
21
+ res.panels_form(
22
+ url: json_ui_garage_url(path: 'forms/generic_post'),
23
+ method: 'post',
24
+ model: model,
25
+ autoValidate: true,
26
+ childViews: ->(form) do
27
+ form.fields_text prop: :name, label: 'name', name: 'user[name]', width: 'matchParent'
28
+ form.spacer height: 4
29
+ form.fields_email prop: :email, label: 'email', name: 'user[email]', width: 'matchParent'
30
+ form.fields_text label: 'say hello', name: 'user[say]', width: 'matchParent', validation: { format: { regex: 'hello', message: 'Say hello!' } }, value: 'sup'
31
+ form.spacer height: 4
32
+ form.fields_number prop: :age, label: 'age', name: 'user[age]', width: 'matchParent'
33
+ form.spacer height: 4
34
+ form.fields_number(
35
+ name: 'user[height]',
36
+ label: 'height',
37
+ width: 'matchParent',
38
+ validation: { numericality: { greater_than_or_equal_to: 155, less_than_or_equal_to: 190, message: { greater_than_or_equal_to: 'Too short', less_than_or_equal_to: 'Too long' } } },
39
+ value: 150
40
+ )
41
+ form.spacer height: 4
42
+ form.fields_textarea prop: :words, label: 'words', name: 'user[words]', width: 'matchParent'
43
+ form.spacer height: 4
44
+ form.fields_textarea label: 'Tell me story', name: 'user[story]', width: 'matchParent', validation: { length: { minimum: 1, message: { too_short: { one: 'Min %{count} word', other: 'Min %{count} words' } } } }
45
+ form.spacer height: 4
46
+ options = ['programmer', 'devops', 'designer', 'ceo', 'office_boy'].map { |v| { text: v.humanize, value: v } }
47
+ form.fields_select prop: :position, label: 'postion', name: 'user[position]', width: 'matchParent', options: options
48
+ form.spacer height: 4
49
+ options2 = ['crocodile', 'cat', 'dog', 'bird', 'shark'].map { |v| { text: v.humanize, value: v } }
50
+ form.fields_select prop: :pet_you_have, label: 'Pet you have', name: 'user[pet_you_have]', width: 'matchParent', options: options2
51
+ form.spacer height: 4
52
+ form.label text: 'Decision'
53
+ options3 = ['choose me', 'dont choose me', 'whatever']
54
+ form.fields_radioGroup(
55
+ value: 'dont choose me',
56
+ validation: { exclusion: { in: ['dont choose me'], message: 'dont choose me' } },
57
+ childViews: ->(radio) do
58
+ options3.each do |option|
59
+ radio.fields_radio label: option, value: option
60
+ end
61
+ end
62
+ )
63
+ form.spacer height: 4
64
+ form.fields_check prop: :accept, label: 'accept', name: 'user[accept]', width: 'matchParent', checkValue: '1'
65
+ form.spacer height: 4
66
+ form.spacer height: 4
67
+ form.fields_submit text: 'submit'
68
+ form.spacer height: 2
69
+ form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
62
70
  end
63
- end
64
- form.spacer height: 4
65
-
66
- form.fields_check prop: :accept, label: 'accept', name: 'user[accept]', width: 'matchParent', checkValue: '1'
67
- form.spacer height: 4
68
-
69
- form.spacer height: 4
70
- form.fields_submit text: 'submit'
71
- form.spacer height: 2
72
- form.fields_submit text: 'submit (if form valid)', disableIfFormInvalid: true
71
+ )
72
+ res.spacer height: 16
73
73
  end
74
-
75
- res.spacer height: 16
74
+ )
76
75
  end
77
- end
76
+ )
@@ -0,0 +1,70 @@
1
+ json.title 'Test Page (HTTP)'
2
+
3
+ page = json_ui_page json
4
+
5
+ page.body(
6
+ childViews: ->(body) do
7
+ render 'json_ui/garage/test_page/header', view: body
8
+ body.panels_responsive(
9
+ padding: glib_json_padding_body,
10
+ childViews: ->(res) do
11
+ res.h2 text: 'HTTP'
12
+ res.spacer height: 8
13
+ res.button text: 'http/post', onClick: ->(action) do
14
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
15
+ subaction.http_post(
16
+ url: json_ui_garage_url(path: 'forms/basic_post'),
17
+ formData: { user: { name: { first: 'New', last: 'Joe' } } }
18
+ )
19
+ end
20
+ end
21
+ res.spacer height: 4
22
+ res.button text: 'http/post (error page)', onClick: ->(action) do
23
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
24
+ subaction.http_post(
25
+ retryLimit: 5,
26
+ url: json_ui_garage_url(path: SecureRandom.hex(3)),
27
+ formData: { user: { name: { first: 'New', last: 'Joe' } } }
28
+ )
29
+ end
30
+ end
31
+ res.spacer height: 4
32
+ res.button text: 'http/patch', onClick: ->(action) do
33
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
34
+ subaction.http_patch(
35
+ url: json_ui_garage_url(path: 'forms/basic_post'),
36
+ formData: { 'user[name]' => 'Edit Joe' }
37
+ )
38
+ end
39
+ end
40
+ res.spacer height: 4
41
+ res.button text: 'http/patch (error page)', onClick: ->(action) do
42
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
43
+ subaction.http_patch(
44
+ url: json_ui_garage_url(path: SecureRandom.hex(3)),
45
+ formData: { 'user[name]' => 'Edit Joe' }
46
+ )
47
+ end
48
+ end
49
+ res.spacer height: 4
50
+ res.button text: 'http/delete', onClick: ->(action) do
51
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
52
+ subaction.http_delete(
53
+ url: json_ui_garage_url(path: 'forms/basic_post'),
54
+ formData: { 'user[name]' => 'Delete Joe' }
55
+ )
56
+ end
57
+ end
58
+ res.spacer height: 4
59
+ res.button text: 'http/delete (error page)', onClick: ->(action) do
60
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
61
+ subaction.http_delete(
62
+ url: json_ui_garage_url(path: SecureRandom.hex(3)),
63
+ formData: { 'user[name]' => 'Delete Joe' }
64
+ )
65
+ end
66
+ end
67
+ end
68
+ )
69
+ end
70
+ )