<div class="loopui-assign-component"> <div class="loopui-assign-component__left"> <turbo-frame id="tmp__assign__left"> <div class="loopui-assign-section"> <div class="loopui-assign-section__top-wrapper"> <div class="lui-title_description lui-title_description--small"> <span class="lui-title_description__title"> Users </span> </div> </div> <div class="loopui-assign-section__wrapper" data-controller="assign-search"> <div class="loopui-assign-section__elements-section" data-assign-search-target="result"> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/praesentiumipsumeaque.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Marlys Rempel</div> <span id="lui-token_5897773874" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">0 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> marlys.rempel@loopos.com </div> </div> </div> </div> </div> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/animiutdebitis.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Rep. Titus Medhurst</div> <span id="lui-token_3633890962" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">2 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> medhurst.rep.titus@loopos.com </div> </div> </div> </div> </div> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/blanditiisundeomnis.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Pinkie McLaughlin</div> <span id="lui-token_5432969054" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">8 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> pinkie.mclaughlin@loopos.com </div> </div> </div> </div> </div> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/undedebitisfacere.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Edison Rau</div> <span id="lui-token_9158206488" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">4 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> edison.rau@loopos.com </div> </div> </div> </div> </div> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/cumexercitationemducimus.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Lamar Bechtelar</div> <span id="lui-token_222646500" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">0 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> lamar.bechtelar@loopos.com </div> </div> </div> </div> </div> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/namutquia.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Arie O'Connell</div> <span id="lui-token_9610425221" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">7 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> arie_oconnell@loopos.com </div> </div> </div> </div> </div> <div class="relative"> <div class="loopui-assign-card"> <div class="loopui-assign-card__image-wrapper"> <img class="loopui-assign-card__image" src="https://robohash.org/quoperspiciatisfugit.png?size=300x300&set=set1" /> </div> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Fr. Luanne Wolff</div> <span id="lui-token_1006410545" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">7 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> <i class="fa-regular fa-envelope"></i> wolff.fr.luanne@loopos.com </div> </div> </div> </div> </div> </div> </div> </div> </turbo-frame> </div> <div class="loopui-assign-component__right"> <turbo-frame id="tmp__assign__right"> <div class="loopui-assign-section"> <div class="loopui-assign-section__top-wrapper"> <div class="lui-title_description lui-title_description--small"> <span class="lui-title_description__title"> Professions </span> </div> </div> <div class="loopui-assign-section__wrapper" data-controller="assign-search"> <div class="loopui-assign-section__elements-section" data-assign-search-target="result"> <div class="relative"> <div class= "loopui-assign-card"> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Painter</div> <span id="lui-token_8083101637" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">6 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> Horizontal uniform matrices </div> </div> </div> </div> </div> <div class="relative"> <div class= "loopui-assign-card"> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Pharmacist</div> <span id="lui-token_5949402131" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">2 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> Distributed even-keeled encoding </div> <hr class="loopui-assign-card__dot"> <span id="lui-token_9553542728" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">Inherited</span> <div class="lui-token__actions"> </div> </span> </div> </div> </div> </div> <div class="relative"> <div class= "loopui-assign-card"> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Politician</div> <span id="lui-token_3917432518" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">0 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> Re-engineered bifurcated application </div> <hr class="loopui-assign-card__dot"> <span id="lui-token_1479021817" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">Inherited</span> <div class="lui-token__actions"> </div> </span> </div> </div> </div> </div> <div class="relative"> <div class= "loopui-assign-card"> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Interpreter</div> <span id="lui-token_5745583536" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">6 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> Re-engineered real-time implementation </div> </div> </div> </div> </div> <div class="relative"> <div class= "loopui-assign-card"> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Soldier</div> <span id="lui-token_5309901276" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">6 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> Reactive uniform groupware </div> <hr class="loopui-assign-card__dot"> <span id="lui-token_7351318025" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">Inherited</span> <div class="lui-token__actions"> </div> </span> </div> </div> </div> </div> <div class="relative"> <div class= "loopui-assign-card"> <div class="loopui-assign-card__info-wrapper"> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__title">Human Resources</div> <span id="lui-token_8928872532" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">9 user groups</span> <div class="lui-token__actions"> </div> </span> </div> <div class="loopui-assign-card__row"> <div class="loopui-assign-card__description"> Phased client-server pricing structure </div> <hr class="loopui-assign-card__dot"> <span id="lui-token_2194263485" class="lui-token lui-entity-token lui-entity-token-general lui-tag-token" style="color: #b53c00; border-color: #e9c5b3;"> <i class="lui-token__icon fa-regular fa-regular fa-tag"></i> <span class="lui-token__text">Inherited</span> <div class="lui-token__actions"> </div> </span> </div> </div> </div> </div> </div> </div> </div> </turbo-frame> </div></div>Assign Component
The Assign component allows users to connect two types of resources (e.g., users, scopes, or other entities) to a main resource in either a two-way or three-way relationship. This component is useful in cases where users need to manage relationships, dependencies, or roles associated with a primary entity, such as assigning permissions or linking users to partners.
Related components
| Used Components | Components where is Used |
|---|---|
| None | None |
Usage rules
✅ Do
- Use in interfaces where multi-resource relationships need to be managed
❌ Don't
- Don't use if a simple one-to-one relationship suffices
- Don't overload with too many resource types at once
<% user_group = OpenStruct.new() users = (1..(10 + (5 * rand()).floor)).map do |index| name = Faker::Name.name OpenStruct.new( id: index, name: name, email: Faker::Internet.email(name: name, domain: "loopos.com"), assigned: [true, false].sample, user_groups_count: (rand() * 10).floor, ) end assigned_users = users.filter { |u| u.assigned } unassigned_users = users.filter { |u| !u.assigned } professions = (1..(10 + (5 * rand()).floor)).map do |index| OpenStruct.new( id: index, name: Faker::Company.profession.humanize.titleize, description: Faker::Company.catch_phrase, assigned: [true, false].sample, user_groups_count: (rand() * 10).floor, inherited?: [true, false].sample ) end assigned_professions = professions.filter { |u| u.assigned } unassigned_professions = professions.filter { |u| !u.assigned } options = params.to_h.symbolize_keys url_helpers = Rails.application.routes.url_helpers current_data = { user_group: user_group.marshal_dump, users: users.map(&:marshal_dump), professions: professions.map(&:marshal_dump) } turbo_frame_prefix = "tmp"%><%= render LooposUi::Assign::AssignComponent.new do |component| %> <% component.with_left_section do %> <%# Turbo frames will be necessary to be able to replace sections %> <%= turbo_frame_tag "#{turbo_frame_prefix}__assign__left" do %> <%= render LooposUi::Assign::AssignSection.new( main_resource: user_group, card: "assign_preview/user_card", title: "Users", assigned: assigned_users, unassigned: unassigned_users, edit_mode: options[:left_side_editable], assign_mode: options[:left_side_assignable], single_side: false, edit_path: ->(args = {}) { url_helpers.loopos_ui_assign_edit_user_professions_path(**args.merge(data: current_data.to_json)) }, assign_path: ->(args = {}) { url_helpers.loopos_ui_assign_assign_user_to_user_group_path(**args.merge(data: current_data.to_json)) }, unassign_path: ->(args = {}) { url_helpers.loopos_ui_assign_unassign_user_to_user_group_path(**args.merge(data: current_data.to_json)) }, search_path: options[:left_side_searchable] ? ->(_) {} : nil, turbo_frame_prefix: turbo_frame_prefix, ) %> <% end %> <% end %> <% component.with_right_section do %> <%# Turbo frames will be necessary to be able to replace sections %> <%= turbo_frame_tag "#{turbo_frame_prefix}__assign__right" do %> <%= render LooposUi::Assign::AssignSection.new( main_resource: user_group, card: "assign_preview/profession_card", title: "Professions", assigned: assigned_professions, unassigned: unassigned_professions, edit_mode: options[:right_side_editable], assign_mode: options[:right_side_assignable], edit_path: ->(args = {}) { url_helpers.loopos_ui_assign_edit_profession_users_path(**args.merge(data: current_data.to_json)) }, assign_path: ->(args = {}) { url_helpers.loopos_ui_assign_assign_profession_to_user_group_path(**args.merge(data: current_data.to_json)) }, unassign_path: ->(args = {}) { url_helpers.loopos_ui_assign_unassign_profession_to_user_group_path(**args.merge(data: current_data.to_json)) }, search_path: options[:right_side_searchable] ? ->(_) {} : nil, single_side: false, turbo_frame_prefix: turbo_frame_prefix, ) %> <% end %> <% end %><% end %>No notes provided.
| Param | Description | Input |
|---|---|---|
|
Be able to assign or unassign left side resources. |
|
|
|
Allow searching the unassigned left side resources. Requires assignable. |
|
|
|
Allow editing (which will show a new right side) left side resources. |
|
|
|
Be able to assign or unassign right side resources. |
|
|
|
Allow searching the unassigned right side resources. Requires assignable. |
|
Assign Component
The Assign component allows users to connect two types of resources (e.g., users, scopes, or other entities) to a main resource in either a two-way or three-way relationship. This component is useful in cases where users need to manage relationships, dependencies, or roles associated with a primary entity, such as assigning permissions or linking users to partners.
Arguments
| Property | Default | Description |
|---|---|---|
| None |
Slots
left_section - Should be a LooposUi::Assign::AssignSection.
right_section - Should be a LooposUi::Assign::AssignSection.
AssignSection
| Property | Default | Description |
|---|---|---|
main_resource |
`` | The resource that this relation affects. |
card |
`` | The path of the partial to use on the section. |
title |
`` | Title of the section. |
assigned |
`` | Section elements that are assigned. |
unassigned |
`` | Section elements that are not assigned. |
edit_mode |
`` | Flag to allow editing the element (changes the other section). |
assign_mode |
`` | Flag to allow assigning/unassigning elements. |
single_side |
`` | Single side means that assigned elements are on the left side and unassigned on the right side. Multiple side means that different sides have different resources. |
edit_path |
`` | URL for editing the selected element. |
assign_path |
`` | URL for assigning the selected element. |
unassign_path |
`` | URL for unassigning the selected element. |
search_path |
`` | URL for searching unassigned elements. |
turbo_frame_prefix |
`` | Prefix for turbo frame. |
Usage process
Single side
Single side is used for when the relation is 2-way. This means that there is a Main resource and a Secondary resource, and we can assign/unassign the secondary resources to the Main resource.
Existing example for single side is the Management tab on Core's Protocol show page.
Multiple side (not single side)
Multiple side is used for when the relation is 3-way. This means that there is a Main resource and two secondary resources (A and B). We can assign/unassign As and Bs. to the Main resource. We can also edit As, which corresponds to assigning/unassigning Bs to the Main resource in relation to the selected A, and vice-versa (editing Bs, assigning/unassigning As to Main).
Existing example for multiple side is the User Scopes tab on Manager's User show page.
Implementation process
We have implemented the Assign Component for the Usage preview, so you can base your work on this, but we have limitations on Lookbook that do not allow us to do it fully:
- The search is not possible
- Turbo is not working properly, so all our endpoints are GET, but should also be PUT and DELETE
This means that you should try to use the examples for each type so that you have better examples of how to implement.