glib-web 0.5.16 → 0.5.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. data/app/controllers/concerns/glib/analytics/funnel.rb +60 -0
  3. data/app/controllers/concerns/glib/json/libs.rb +4 -4
  4. data/app/controllers/concerns/glib/json/traversal.rb +1 -0
  5. data/app/controllers/concerns/glib/json/ui.rb +16 -8
  6. data/app/controllers/glib/home_controller.rb +0 -0
  7. data/app/helpers/glib/json_ui/action_builder.rb +12 -0
  8. data/app/helpers/glib/json_ui/menu_builder.rb +48 -6
  9. data/app/helpers/glib/json_ui/page_helper.rb +21 -0
  10. data/app/helpers/glib/json_ui/response_helper.rb +0 -0
  11. data/app/helpers/glib/json_ui/view_builder.rb +36 -19
  12. data/app/helpers/glib/json_ui/view_builder/fields.rb +2 -0
  13. data/app/helpers/glib/json_ui/view_builder/panels.rb +1 -1
  14. data/app/views/json_ui/garage/_nav_menu.json.jbuilder +0 -1
  15. data/app/views/json_ui/garage/actions/_http.json.jbuilder +9 -3
  16. data/app/views/json_ui/garage/actions/index.json.jbuilder +0 -0
  17. data/app/views/json_ui/garage/forms/basic.json.jbuilder +2 -14
  18. data/app/views/json_ui/garage/forms/dynamic_group.json.jbuilder +31 -16
  19. data/app/views/json_ui/garage/forms/pickers.json.jbuilder +0 -0
  20. data/app/views/json_ui/garage/home/index.json.jbuilder +0 -0
  21. data/app/views/json_ui/garage/lists/chat_ui.json.jbuilder +112 -0
  22. data/app/views/json_ui/garage/lists/index.json.jbuilder +2 -2
  23. data/app/views/json_ui/garage/notifications/android_post.json.jbuilder +48 -0
  24. data/app/views/json_ui/garage/notifications/index.json.jbuilder +14 -0
  25. data/app/views/json_ui/garage/pages/nav_buttons.json.jbuilder +4 -16
  26. data/app/views/json_ui/garage/tables/_autoload_section.json.jbuilder +6 -2
  27. data/app/views/json_ui/garage/tables/autoload_all.json.jbuilder +2 -0
  28. data/app/views/json_ui/garage/tables/autoload_as_needed.json.jbuilder +13 -2
  29. data/app/views/json_ui/garage/tables/index.json.jbuilder +19 -20
  30. data/app/views/json_ui/garage/tables/layout.json.jbuilder +26 -28
  31. data/app/views/json_ui/garage/views/banners.json.jbuilder +18 -6
  32. data/app/views/json_ui/garage/views/icons.json.jbuilder +1439 -11
  33. data/app/views/json_ui/garage/views/index.json.jbuilder +6 -3
  34. data/app/views/layouts/json_ui/renderer.html.erb +7 -4
  35. metadata +5 -3
  36. data/app/views/json_ui/garage/lists/chat.json.jbuilder +0 -112
@@ -7,14 +7,6 @@ json_ui_page json do |page|
7
7
  form.fields_text name: 'user[name]', width: 'matchParent', label: 'Name'
8
8
  form.fields_password name: 'user[password]', width: 'matchParent', label: 'Password'
9
9
 
10
- # form.panels_split width: 'matchParent', leftViews: ->(split) do
11
- # if params[:mode] == 'dialog'
12
- # split.button styleClass: 'link', text: 'cancel', onClick: ->(action) { action.dialogs_close }
13
- # end
14
- # end, rightViews: ->(split) do
15
- # split.button text: 'Submit', onClick: ->(action) { action.forms_submit }
16
- # end
17
-
18
10
  form.panels_split width: 'matchParent', content: ->(split) do
19
11
  split.left childViews: ->(left) do
20
12
  if params[:mode] == 'dialog'
@@ -22,13 +14,9 @@ json_ui_page json do |page|
22
14
  end
23
15
  end
24
16
  split.right childViews: ->(right) do
25
- right.button text: 'Submit', onClick: ->(action) { action.forms_submit }
17
+ # right.button text: 'Submit', onClick: ->(action) { action.forms_submit }
18
+ right.fields_submit text: 'Submit'
26
19
  end
27
20
  end
28
-
29
21
  end
30
- # , paramNameForFormData: 'formData', onSubmit: ->(action) do
31
- # action.http_post url: json_ui_garage_url(path: 'forms/generic_post')
32
- # end
33
-
34
22
  end
@@ -7,23 +7,38 @@ json_ui_page json do |page|
7
7
  form.h2 text: 'Dynamic Group'
8
8
  form.spacer height: 6
9
9
 
10
- value = [
11
- {
12
- 'question': 'Punctuality',
13
- 'type': 'rating',
14
- 'enabled': '1'
15
- },
16
- {
17
- 'question': 'Quality of work',
18
- 'type': 'rating'
19
- },
20
- {
21
- 'question': 'Satisfied?',
22
- 'type': 'yes_no'
23
- }
24
- ]
10
+ # value = [
11
+ # {
12
+ # 'question': 'Punctuality',
13
+ # 'type': 'rating'
14
+ # },
15
+ # {
16
+ # 'question': 'Quality of work',
17
+ # 'type': 'rating',
18
+ # 'enabled': '1'
19
+ # },
20
+ # {
21
+ # 'question': 'Satisfied?',
22
+ # 'type': 'yes_no'
23
+ # }
24
+ # ]
25
25
 
26
- form.fields_dynamicGroup width: 'matchParent', name: 'user[evaluation]', value: value, titlePrefix: 'Entry', content: ->(group) do
26
+ properties = [
27
+ [
28
+ { name: 'question', value: 'Punctuality' },
29
+ { name: 'type', value: 'rating' },
30
+ ],
31
+ [
32
+ { name: 'question', value: 'Quality of work' },
33
+ { name: 'type', value: 'rating' },
34
+ { name: 'enabled', value: '1', styleClasses: ['success'] },
35
+ ],
36
+ [
37
+ { name: 'question', value: 'Satisfied?' },
38
+ { name: 'type', value: 'yes_no' },
39
+ ]
40
+ ]
41
+ form.fields_dynamicGroup width: 'matchParent', name: 'user[evaluation]', groupFieldProperties: properties, titlePrefix: 'Entry', content: ->(group) do
27
42
  group.template padding: { left: 32 }, childViews: ->(template) do
28
43
  template.spacer height: 10
29
44
  template.fields_text width: 'matchParent', name: 'question', label: 'Question', placeholder: 'Question'
@@ -0,0 +1,112 @@
1
+ json.title 'Lists'
2
+
3
+ liked = params[:liked] == 'true'
4
+
5
+ page = json_ui_page json
6
+ render "#{@path_prefix}/nav_menu", json: json, page: page
7
+
8
+ json.ws({
9
+ "socket" => {
10
+ "endpoint" => "/socket/websocket",
11
+ "params" => {
12
+ vsn: '2.0.0',
13
+ token: 'TOKEN'
14
+ }
15
+ },
16
+ "topic" => "rooms",
17
+ "events" => [],
18
+ # "events" => ["new_link_added"],
19
+ # "header" => {
20
+ # "user_id" => 2,
21
+ # "prev_item_id" => nil,
22
+ # "last_item_id" => nil
23
+ # }
24
+ })
25
+
26
+ list_ws = { topic: 'rooms', events: ['comments_updated'] }
27
+ page.list ws: list_ws, firstSection: ->(section) do
28
+ section.header padding: { top: 12, left: 16, right: 16, bottom: 12 }, childViews: ->(header) do
29
+ header.h3 text: 'Chat with John Doe'
30
+ end
31
+
32
+ section.rows builder: ->(template) do
33
+ # template.thumbnail title: "windows/reload (timestamp: #{DateTime.current.to_i}) -- #{liked}", onClick: ->(action) do
34
+ # action.windows_reload
35
+ # end, onLongPress: ->(action) do
36
+ # action.sheets_select message: "Context Menu (#{DateTime.current.to_i})", buttons: ->(menu) do
37
+ # if liked
38
+ # menu.button text: 'Cancel 👍', onClick: ->(subaction) do
39
+ # subaction.windows_reload url: json_ui_garage_url(path: 'lists/chat', liked: false)
40
+ # end
41
+ # else
42
+ # menu.button text: 'Give 👍', onClick: ->(subaction) do
43
+ # subaction.windows_reload url: json_ui_garage_url(path: 'lists/chat', liked: true)
44
+ # end
45
+ # end
46
+ # end
47
+ # end
48
+
49
+ template.commentOutgoing subtitle: 'Hey!', subsubtitle: l(10.minutes.ago, format: :short), imageUrl: glib_json_image_standard_url, chips: ->(menu) do
50
+ menu.button text: '😊 2', styleClass: 'info'
51
+ end
52
+
53
+ template.commentOutgoing subtitle: 'How are you?', subsubtitle: l(DateTime.current, format: :short)
54
+
55
+ template.commentIncoming title: 'John Doe', subtitle: 'Very well', subsubtitle: l(7.minutes.ago, format: :short), imageUrl: glib_json_image_standard_url, chips: ->(menu) do
56
+ menu.button text: "👍 #{liked ? 2 : 1}"
57
+ end, onLongPress: ->(action) do
58
+ action.sheets_select message: 'Context Menu', buttons: ->(menu) do
59
+ if liked
60
+ menu.button text: 'Cancel 👍', onClick: ->(subaction) do
61
+ subaction.windows_reload url: json_ui_garage_url(path: 'lists/chat_ui', liked: false)
62
+ end
63
+ else
64
+ menu.button text: 'Give 👍', onClick: ->(subaction) do
65
+ subaction.windows_reload url: json_ui_garage_url(path: 'lists/chat_ui', liked: true)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ page.footer padding: { top: 12, left: 16, right: 16, bottom: 12 }, childViews: ->(footer) do
74
+ # json.ws({
75
+ # "socket" => {
76
+ # "endpoint" => "/socket/websocket",
77
+ # "params" => {
78
+ # vsn: '2.0.0',
79
+ # token: 'TOKEN'
80
+ # }
81
+ # },
82
+ # # "topic" => "room:30",
83
+ # # "event" => "comments_updated",
84
+ # "topic" => "links",
85
+ # "events" => ["new_link_added"],
86
+ # "header" => {
87
+ # "user_id" => 2,
88
+ # "prev_item_id" => nil,
89
+ # "last_item_id" => nil
90
+ # }
91
+ # })
92
+
93
+ footer.panels_form width: 'matchParent', url: json_ui_garage_url(path: 'forms/basic_post'), method: 'post', padding: glib_json_padding_body, paramNameForFormData: 'formData', onSubmit: ->(action) do
94
+ json.action "ws/push"
95
+ json.topic "rooms"
96
+ json.event "create_comment"
97
+ json.payload({
98
+ "room_id": "30",
99
+ "user_id": "2"
100
+ })
101
+
102
+ end, childViews: ->(form) do
103
+ form.fields_text name: 'user[message]', width: 'matchParent', label: 'Message'
104
+
105
+ form.panels_split width: 'matchParent', content: ->(split) do
106
+ split.right childViews: ->(right) do
107
+ right.button text: 'Submit', onClick: ->(action) { action.forms_submit }
108
+ end
109
+ end
110
+ end
111
+
112
+ end
@@ -14,8 +14,8 @@ json_ui_page json do |page|
14
14
  template.thumbnail title: 'FAB (Floating Action Button)', onClick: ->(action) do
15
15
  action.windows_open url: json_ui_garage_url(path: 'lists/fab')
16
16
  end
17
- template.thumbnail title: 'Chat Prototype', onClick: ->(action) do
18
- action.windows_open url: json_ui_garage_url(path: 'lists/chat')
17
+ template.thumbnail title: 'Chat UI', onClick: ->(action) do
18
+ action.windows_open url: json_ui_garage_url(path: 'lists/chat_ui')
19
19
  end
20
20
  template.thumbnail title: 'Autoload as Needed', onClick: ->(action) do
21
21
  action.windows_open url: json_ui_garage_url(path: 'lists/autoload_as_needed')
@@ -0,0 +1,48 @@
1
+
2
+ if (server_key = ENV['FCM_SERVER_KEY'])
3
+ if defined? FCM
4
+ # Introduce delay so that we have time to place the app to background
5
+ # before receiving push notification.
6
+ sleep 2
7
+
8
+ fcm = FCM.new(server_key)
9
+ registration_ids = [params[:token]] # An array of one or more client registration tokens
10
+
11
+ payload = json_ui_action_payload do |action|
12
+ action.dialogs_alert message: 'Opened'
13
+ end
14
+
15
+ # See https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages for all available options.
16
+ options = {
17
+ notification: {
18
+ title: params[:title],
19
+ body: 'Message from server'
20
+ # title: 'Offer from Ajisen Ramen',
21
+ # body: 'Free side order of Gyoza'
22
+ },
23
+ data: {
24
+ channelId: 'default',
25
+ notificationId: 1,
26
+ openUrl: json_ui_garage_url(path: 'home/blank'),
27
+ onOpen: payload
28
+ # openUrl: 'http://10.0.2.2:3000/offers.json?_render=v1',
29
+ }
30
+ }
31
+ response = fcm.send(registration_ids, options)
32
+
33
+ body = JSON.parse(response[:body])
34
+ if (errors = body['results'].map { |i| i['error'] }.compact).any?
35
+ message = errors.join(', ')
36
+ else
37
+ message = 'Sent'
38
+ end
39
+ else
40
+ message = 'FCM gem needed'
41
+ end
42
+ else
43
+ message = 'Env var needed: GCM_SERVER_KEY'
44
+ end
45
+
46
+ json_ui_response json do |action|
47
+ action.snackbars_alert message: message
48
+ end
@@ -11,6 +11,20 @@ page.list firstSection: ->(section) do
11
11
  end
12
12
  end
13
13
 
14
+ template.thumbnail title: 'Send Android Notification', onClick: ->(action) do
15
+ action.auth_saveCsrfToken token: form_authenticity_token, onSave: ->(subaction) do
16
+ # subaction.devices_getPushToken paramNameForToken: 'formData[token]', onGet: ->(subsubaction) do
17
+ # formData = {
18
+ # title: 'Title from formData'
19
+ # }
20
+ # subsubaction.http_post url: json_ui_garage_url(path: 'notifications/android_post'), formData: formData
21
+ # end
22
+
23
+ post_url = json_ui_garage_url(path: 'notifications/android_post', title: 'Title from formData')
24
+ subaction.devices_getPushToken postUrl: post_url, paramNameForToken: 'token'
25
+ end
26
+ end
27
+
14
28
  template.thumbnail title: 'WebSocket Real-time Update', onClick: ->(action) do
15
29
  action.windows_open url: json_ui_garage_url(path: 'notifications/web_socket')
16
30
  end
@@ -4,30 +4,18 @@ page = json_ui_page json
4
4
 
5
5
  render "#{@path_prefix}/nav_menu", json: json, page: page
6
6
 
7
- page.navBar backgroundColor: '#a8c4e3', rightButtons: ->(menu) do
8
- menu.button icon: 'search', onClick: ->(action) do
7
+ page.navBar backgroundColor: '#a8c4e3', color: '#ffffff', rightButtons: ->(menu) do
8
+ menu.button icon: 'search', onClick: ->(action) do
9
9
  action.dialogs_alert message: 'Perform some action'
10
10
  end
11
- menu.button icon: { name: 'star', badge: { text: '1', backgroundColor: '#ff0000' } }, onClick: ->(action) do
11
+ menu.button icon: { name: 'star', badge: { text: '1', backgroundColor: '#ff0000' } }, onClick: ->(action) do
12
12
  action.dialogs_alert message: 'Perform some action'
13
13
  end
14
- menu.button icon: { name: 'map', badge: '2' }, onClick: ->(action) do
14
+ menu.button icon: { name: 'map', badge: '2' }, onClick: ->(action) do
15
15
  action.dialogs_alert message: 'Perform some action'
16
16
  end
17
17
  end
18
18
 
19
- # page.rightNavButtons do |menu|
20
- # menu.button icon: 'search', onClick: ->(action) do
21
- # action.dialogs_alert message: 'Perform some action'
22
- # end
23
- # menu.button icon: { name: 'star', badge: { text: '1', backgroundColor: '#ff0000' } }, onClick: ->(action) do
24
- # action.dialogs_alert message: 'Perform some action'
25
- # end
26
- # menu.button icon: { name: 'map', badge: '2' }, onClick: ->(action) do
27
- # action.dialogs_alert message: 'Perform some action'
28
- # end
29
- # end
30
-
31
19
  page.scroll padding: glib_json_padding_body, childViews: ->(scroll) do
32
20
  scroll.label text: 'See the right menu buttons on the nav bar'
33
21
  end
@@ -2,10 +2,14 @@ batch_count = 30
2
2
  items = (1..batch_count)
3
3
  section = page.table_section_builder
4
4
  section.rows objects: items, builder: ->(row, item, index) do
5
- row.default cellViews: ->(cell) do
5
+ row.default colStyles: [{ width: 36 }, {}, {}, {}, { width: 36 }], cellViews: ->(cell) do
6
+ cell.label text: ''
7
+
6
8
  column_indexes.each do |i|
7
- cell.label text: "Data #{page_index * batch_count + item}"
9
+ cell.label text: "Data #{page_index * batch_count + item}-#{i}"
8
10
  end
11
+
12
+ cell.label text: ''
9
13
  end, onClick: ->(action) do
10
14
  action.windows_open url: json_ui_garage_url(path: 'home/blank')
11
15
  end
@@ -10,6 +10,8 @@ column_indexes = (1..3)
10
10
  # - for SEO: one URL for a standalone page and one URL for pagination only
11
11
  # - for generic approach, e.g. excluding nav_menu when there is no change
12
12
  if params[:section_only].present?
13
+ sleep 1
14
+
13
15
  json.nextPage next_page if page_index < 3
14
16
  json_ui_page json do |page|
15
17
  json.sections do
@@ -1,12 +1,15 @@
1
1
 
2
+ col_index = params[:col].to_i
2
3
  page_index = params[:page].to_i
3
4
  next_page = {
4
- url: json_ui_garage_url(path: 'tables/autoload_as_needed', page: page_index + 1, section_only: 'v1'),
5
+ url: json_ui_garage_url(path: 'tables/autoload_as_needed', page: page_index + 1, col: col_index, section_only: 'v1'),
5
6
  autoload: 'asNeeded'
6
7
  }
7
8
 
8
9
  page = json_ui_page json
9
- column_indexes = (1..3)
10
+ column_begin = (col_index * 3) + 1
11
+ column_end = column_begin + 2
12
+ column_indexes = (column_begin..column_end)
10
13
 
11
14
  if params[:section_only].present?
12
15
  sleep 1
@@ -24,9 +27,17 @@ else
24
27
 
25
28
  page.table nextPage: next_page, firstSection: ->(section) do
26
29
  section.header cellViews: ->(header) do
30
+ header.button icon: "chevron_left", styleClass: 'icon', onClick: ->(action) do
31
+ action.windows_reload url: json_ui_garage_url(path: 'tables/autoload_as_needed', page: 0, col: col_index - 1)
32
+ end
33
+
27
34
  column_indexes.each do |i|
28
35
  header.label text: "Heading#{i}"
29
36
  end
37
+
38
+ header.button icon: "chevron_right", styleClass: 'icon', onClick: ->(action) do
39
+ action.windows_reload url: json_ui_garage_url(path: 'tables/autoload_as_needed', page: 0, col: col_index + 1)
40
+ end
30
41
  end
31
42
 
32
43
  render "#{@path_prefix}/tables/autoload_section", page: page, page_index: page_index, column_indexes: column_indexes
@@ -1,26 +1,25 @@
1
1
  json.title 'Tables'
2
2
 
3
- json_ui_page json do |page|
4
- render "#{@path_prefix}/nav_menu", json: json, page: page, top_nav: true
3
+ page = json_ui_page json
4
+ render "#{@path_prefix}/nav_menu", json: json, page: page, top_nav: true
5
5
 
6
- page.list firstSection: ->(section) do
7
- section.rows builder: ->(template) do
8
- template.thumbnail title: 'Layout', onClick: ->(action) do
9
- action.windows_open url: json_ui_garage_url(path: 'tables/layout')
10
- end
11
- template.thumbnail title: 'Horizontal Scroll', onClick: ->(action) do
12
- action.windows_open url: json_ui_garage_url(path: 'tables/horizontal_scroll')
13
- end
14
- template.thumbnail title: 'Export/Import', onClick: ->(action) do
15
- action.windows_open url: json_ui_garage_url(path: 'tables/export_import')
16
- end
17
- template.thumbnail title: 'Autoload All', onClick: ->(action) do
18
- action.windows_open url: json_ui_garage_url(path: 'tables/autoload_all')
19
- end
20
- template.thumbnail title: 'Autoload as Needed', onClick: ->(action) do
21
- action.windows_open url: json_ui_garage_url(path: 'tables/autoload_as_needed')
22
- end
6
+ page.list firstSection: ->(section) do
7
+ section.rows builder: ->(template) do
8
+ template.thumbnail title: 'Layout', onClick: ->(action) do
9
+ action.windows_open url: json_ui_garage_url(path: 'tables/layout')
10
+ end
11
+ template.thumbnail title: 'Horizontal Scroll', onClick: ->(action) do
12
+ action.windows_open url: json_ui_garage_url(path: 'tables/horizontal_scroll')
13
+ end
14
+ template.thumbnail title: 'Export/Import', onClick: ->(action) do
15
+ action.windows_open url: json_ui_garage_url(path: 'tables/export_import')
16
+ end
17
+ template.thumbnail title: 'Autoload as Needed', onClick: ->(action) do
18
+ action.windows_open url: json_ui_garage_url(path: 'tables/autoload_as_needed')
19
+ end
20
+ template.thumbnail title: 'Autoload All', onClick: ->(action) do
21
+ action.windows_open url: json_ui_garage_url(path: 'tables/autoload_all')
23
22
  end
24
-
25
23
  end
24
+
26
25
  end
@@ -1,38 +1,36 @@
1
1
  json.title 'Tables'
2
2
 
3
- json_ui_page json do |page|
4
- render "#{@path_prefix}/nav_menu", json: json, page: page
3
+ page = json_ui_page json
4
+ render "#{@path_prefix}/nav_menu", json: json, page: page
5
5
 
6
- column_indexes = (1..5)
7
- page.table sections: [
8
- ->(section) do
9
- section.rows builder: ->(row) do
10
- row.default colSpans: [3, 2], cellViews: ->(cell) do
11
- cell.label text: 'Spans 3 columns'
12
- cell.panels_horizontal width: 'matchParent', backgroundColor: '#dddddd', padding: { top: 10, right: 10, bottom: 10, left: 10 }, childViews: ->(horizontal) do
13
- horizontal.label text: 'Spans 2 columns'
14
- end
6
+ column_indexes = (1..5)
7
+ page.table sections: [
8
+ ->(section) do
9
+ section.rows builder: ->(row) do
10
+ row.default colSpans: [3, 2], cellViews: ->(cell) do
11
+ cell.label text: 'Spans 3 columns'
12
+ cell.panels_horizontal width: 'matchParent', backgroundColor: '#dddddd', padding: { top: 10, right: 10, bottom: 10, left: 10 }, childViews: ->(horizontal) do
13
+ horizontal.label text: 'Spans 2 columns'
15
14
  end
16
15
  end
17
- end,
18
- ->(section) do
19
- section.header cellViews: ->(header) do
20
- column_indexes.each do |i|
21
- header.label text: "Heading#{i}"
22
- end
16
+ end
17
+ end,
18
+ ->(section) do
19
+ section.header cellViews: ->(header) do
20
+ column_indexes.each do |i|
21
+ header.label text: "Heading#{i}"
23
22
  end
23
+ end
24
24
 
25
- items = [1, 2, 3]
26
- section.rows objects: items, builder: ->(row, item, index) do
27
- row.default colStyles: [{ width: 200, backgroundColor: '#eeeeee' }], cellViews: ->(cell) do
28
- column_indexes.each do |i|
29
- cell.label text: "Data #{item}"
30
- end
31
- end, onClick: ->(action) do
32
- action.windows_open url: json_ui_garage_url(path: 'home/blank')
25
+ items = [1, 2, 3]
26
+ section.rows objects: items, builder: ->(row, item, index) do
27
+ row.default colStyles: [{ width: 200, backgroundColor: '#eeeeee' }], cellViews: ->(cell) do
28
+ column_indexes.each do |i|
29
+ cell.label text: "Data #{item}"
33
30
  end
31
+ end, onClick: ->(action) do
32
+ action.windows_open url: json_ui_garage_url(path: 'home/blank')
34
33
  end
35
34
  end
36
- ]
37
-
38
- end
35
+ end
36
+ ]