@@ -46,10 +46,7 @@ defmodule Sanbase.Menus do
46
46
47
47
@ type update_menu_item_params :: % {
48
48
optional ( :parent_id ) => menu_id ,
49
- optional ( :position ) => integer ( ) | nil ,
50
- optional ( :query_id ) => Sanbase.Queries.Query . query_id ( ) ,
51
- optional ( :dashboard_id ) => Sanbase.Queries.Dashboard . dashboard_id ( ) ,
52
- optional ( :menu_id ) => menu_id
49
+ optional ( :position ) => integer ( ) | nil
53
50
}
54
51
55
52
@ doc ~s"""
@@ -77,6 +74,10 @@ defmodule Sanbase.Menus do
77
74
"""
78
75
def menu_to_simple_map ( % Menu { } = menu ) do
79
76
% {
77
+ # If this menu is a sub-menu, then the caller from get_menu_items/1 will
78
+ # additionally set the menu_item_id. If this is the top-level menu, then
79
+ # this is not a sub-menu and it does not have a menu_item_id
80
+ menu_item_id: nil ,
80
81
type: :menu ,
81
82
id: menu . id ,
82
83
name: menu . name ,
@@ -95,7 +96,9 @@ defmodule Sanbase.Menus do
95
96
"""
96
97
@ spec create_menu ( create_menu_params , user_id ) :: { :ok , Menu . t ( ) } | { :error , String . t ( ) }
97
98
def create_menu ( params , user_id ) do
98
- params = params |> Map . merge ( % { user_id: user_id } )
99
+ params =
100
+ params
101
+ |> Map . merge ( % { user_id: user_id } )
99
102
100
103
Ecto.Multi . new ( )
101
104
|> Ecto.Multi . run ( :create_menu , fn _repo , _changes ->
@@ -121,19 +124,13 @@ defmodule Sanbase.Menus do
121
124
)
122
125
end
123
126
end )
124
- # |> Ecto.Multi.run(
125
- # :get_menu_with_preloads,
126
- # fn _repo, %{create_menu: menu, maybe_create_menu_item: menu_or_nil} ->
127
- # # If the menu was created as a sub-menu, then the `maybe_create_menu_item` step
128
- # # has already returned the menu with preloads. Otherwise, we need to preload it here.
129
- # case menu_or_nil do
130
- # %Menu{} = m -> {:ok, m}
131
- # nil -> get_menu(menu.id, user_id)
132
- # end
133
- # end
134
- # )
127
+ |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { create_menu: menu } ->
128
+ # There would be no menu items, but this will help to set the menu items to []
129
+ # instead of getting an error when trying to iterate them because they're set to <not preloaded>
130
+ get_menu ( menu . id , user_id )
131
+ end )
135
132
|> Repo . transaction ( )
136
- |> process_transaction_result ( :create_menu )
133
+ |> process_transaction_result ( :get_menu_with_preloads )
137
134
end
138
135
139
136
@ doc ~s"""
@@ -152,7 +149,7 @@ defmodule Sanbase.Menus do
152
149
query = Menu . update ( menu , params )
153
150
Repo . update ( query )
154
151
end )
155
- |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { create_menu : menu } ->
152
+ |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { update_menu : menu } ->
156
153
get_menu ( menu . id , user_id )
157
154
end )
158
155
|> Repo . transaction ( )
@@ -168,11 +165,19 @@ defmodule Sanbase.Menus do
168
165
|> Ecto.Multi . run ( :get_menu_for_update , fn _repo , _changes ->
169
166
get_menu_for_update ( menu_id , user_id )
170
167
end )
168
+ |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , _changes ->
169
+ # Call this so we can return the menu with its menu items after it is
170
+ # successfully deleted
171
+ get_menu ( menu_id , user_id )
172
+ end )
171
173
|> Ecto.Multi . run ( :delete_menu , fn _repo , % { get_menu_for_update: menu } ->
172
174
Repo . delete ( menu )
173
175
end )
174
176
|> Repo . transaction ( )
175
- |> process_transaction_result ( :delete_menu )
177
+ # Purposefully do not return the result of the last Ecto.Multi.run call,
178
+ # but from the get_menu_with_preloads call, so we can return the menu with
179
+ # its items.
180
+ |> process_transaction_result ( :get_menu_with_preloads )
176
181
end
177
182
178
183
@ doc ~s"""
@@ -234,45 +239,63 @@ defmodule Sanbase.Menus do
234
239
235
240
@ doc ~s"""
236
241
Update an existing menu item.
242
+
243
+ A menu item can have the follwing fields updated:
244
+ - position - change the position of the item in the menu
245
+ - parent_id - change the parent menu of the item. On the frontend this is done
246
+ by dragging and dropping the item in the menu tree (this can also update the position)
247
+
248
+ The entity (query, dashboard, etc.) cannot be changed. Delete a menu item and insert a new
249
+ one instead.
237
250
"""
238
- @ spec update_menu_item ( menu_id , menu_item_id , update_menu_item_params , user_id ) ::
251
+ @ spec update_menu_item ( menu_item_id , update_menu_item_params , user_id ) ::
239
252
{ :ok , Menu . t ( ) } | { :error , String . t ( ) }
240
- def update_menu_item ( menu_id , menu_item_id , params , user_id ) do
253
+ def update_menu_item ( menu_item_id , params , user_id ) do
241
254
Ecto.Multi . new ( )
242
- |> Ecto.Multi . run ( :get_menu_for_update , fn _repo , _changes ->
243
- # Just check that the current user can update the parent menu
244
- get_menu_for_update ( menu_id , user_id )
245
- end )
246
255
|> Ecto.Multi . run ( :get_menu_item_for_update , fn _repo , _changes ->
247
- get_menu_item_for_update ( menu_item_id )
256
+ get_menu_item_for_update ( menu_item_id , user_id )
248
257
end )
258
+ |> Ecto.Multi . run (
259
+ :maybe_update_items_positions ,
260
+ fn _repo , % { get_menu_item_for_update: menu_item } ->
261
+ case Map . get ( params , :position ) do
262
+ nil ->
263
+ { :ok , nil }
264
+
265
+ position when is_integer ( position ) ->
266
+ # If `position` is specified, bump all the positions bigger than it by 1 in
267
+ # order to avoid having multiple items with the same position.
268
+ { :ok , { _ , nil } } = inc_all_positions_after ( menu_item . parent_id , position )
269
+ { :ok , position }
270
+ end
271
+ end
272
+ )
249
273
|> Ecto.Multi . run ( :update_menu_item , fn _repo , % { get_menu_item_for_update: menu_item } ->
250
- # Handle change of entity
251
- params = MenuItem . process_update_params ( params )
252
274
query = MenuItem . update ( menu_item , params )
253
275
Repo . update ( query )
254
276
end )
255
- |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { get_menu_for_update: menu } ->
256
- get_menu ( menu . id , user_id )
277
+ |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { update_menu_item: menu_item } ->
278
+ get_menu ( menu_item . parent_id , user_id )
257
279
end )
258
280
|> Repo . transaction ( )
259
281
|> process_transaction_result ( :get_menu_with_preloads )
260
282
end
261
283
262
- def delete_menu_item ( menu_id , menu_item_id , user_id ) do
284
+ @ doc ~s"""
285
+ Delete a menu item.
286
+ """
287
+ @ spec delete_menu_item ( menu_item_id , user_id ) ::
288
+ { :ok , Menu . t ( ) } | { :error , String . t ( ) }
289
+ def delete_menu_item ( menu_item_id , user_id ) do
263
290
Ecto.Multi . new ( )
264
- |> Ecto.Multi . run ( :check_user_has_write_access , fn _repo , _changes ->
265
- # Just check that the current user can update the parent menu
266
- get_menu_for_update ( menu_id , user_id )
267
- end )
268
291
|> Ecto.Multi . run ( :get_menu_item , fn _repo , _changes ->
269
- get_menu_item_for_update ( menu_item_id )
292
+ get_menu_item_for_update ( menu_item_id , user_id )
270
293
end )
271
294
|> Ecto.Multi . run ( :delete_menu_item , fn _repo , % { get_menu_item: menu_item } ->
272
295
Repo . delete ( menu_item )
273
296
end )
274
- |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { create_menu: menu } ->
275
- get_menu ( menu . id , user_id )
297
+ |> Ecto.Multi . run ( :get_menu_with_preloads , fn _repo , % { delete_menu_item: menu_item } ->
298
+ get_menu ( menu_item . parent_id , user_id )
276
299
end )
277
300
|> Repo . transaction ( )
278
301
|> process_transaction_result ( :get_menu_with_preloads )
@@ -289,11 +312,8 @@ defmodule Sanbase.Menus do
289
312
end
290
313
end
291
314
292
- defp get_menu_item_for_update ( menu_item_id ) do
293
- # This should be called only after checking that the user
294
- # has write access to the menu. This is done by calling
295
- # get_menu_for_update/2 first.
296
- query = MenuItem . get_for_update ( menu_item_id )
315
+ defp get_menu_item_for_update ( menu_item_id , user_id ) do
316
+ query = MenuItem . get_for_update ( menu_item_id , user_id )
297
317
298
318
case Repo . one ( query ) do
299
319
nil -> { :error , "Menu item does not exist" }
@@ -339,14 +359,17 @@ defmodule Sanbase.Menus do
339
359
defp get_menu_items ( % Menu { menu_items: list } ) when is_list ( list ) do
340
360
list
341
361
|> Enum . map ( fn
342
- % { query: % { id: _ } = map , position: p } ->
343
- Map . take ( map , [ :id , :name , :description ] ) |> Map . merge ( % { type: :query , position: p } )
362
+ % { id: menu_item_id , query: % { id: _ } = map , position: position } ->
363
+ Map . take ( map , [ :id , :name , :description ] )
364
+ |> Map . merge ( % { type: :query , position: position , menu_item_id: menu_item_id } )
344
365
345
- % { dashboard: % { id: _ } = map , position: p } ->
346
- Map . take ( map , [ :id , :name , :description ] ) |> Map . merge ( % { type: :dashboard , position: p } )
366
+ % { id: menu_item_id , dashboard: % { id: _ } = map , position: position } ->
367
+ Map . take ( map , [ :id , :name , :description ] )
368
+ |> Map . merge ( % { type: :dashboard , position: position , menu_item_id: menu_item_id } )
347
369
348
- % { menu: % { id: _ } = map , position: p } ->
349
- menu_to_simple_map ( map ) |> Map . put ( :position , p )
370
+ % { id: menu_item_id , menu: % { id: _ } = map , position: position } ->
371
+ menu_to_simple_map ( map )
372
+ |> Map . merge ( % { type: :menu , position: position , menu_item_id: menu_item_id } )
350
373
end )
351
374
end
352
375
end
0 commit comments