Я пишу приложение на ruby on rails. И мне не очень...
创建于:2025年12月7日
使用 GPT-5.1 Thinking 回答 Chat01
创建于:2025年12月7日
使用 GPT-5.1 Thinking 回答 Chat01
Я пишу приложение на ruby on rails. И мне не очень нравятся вьюхи. они выглядят хорошо но кривовато. Я хочу, чтобы во первых email помещался в карточку а не вылазил. также хотелось бы чтобы соседние карточки были одного размера снизу, а не разных. также я не хочу вообще использовать style="" во вьюхах, а хочу перенести все в стили в .scss файлы. Но также, я хочу, чтобы все стили, которые можно написать с помщью бутстрап, а не с помощью .scss. Также я хочу, чтобы ты убрал все строгие размеры и сделал размеры адаптивными.
_address_card.html.erb:
<div class="col-md-6"> <div class="border rounded-3 p-3 h-100"> <h6 class="mb-2"> <%= address.country %>, <%= address.city %> </h6></div> </div>text<p class="mb-1"> <%= "#{address.street} #{address.house}" %> <% if address.apartment.present? %> , <%= t('account.addresses.apartment') %> <%= address.apartment %> <% end %> </p> <p class="mb-1 text-muted"> <%= t('account.addresses.zip') %>: <%= address.zip_code %> </p> <% if address.entrance.present? || address.floor.present? %> <p class="mb-0 text-muted"> <%= [ (address.entrance.present? && "#{t('account.addresses.entrance')} #{address.entrance}"), (address.floor.present? && "#{t('account.addresses.floor')} #{address.floor}") ].compact.join(', ') %> </p> <% end %>
_tabs.html+desktop.erb:
<div class="account-sidebar-card"> <div class="account-user-header"> <% if current_user.profile&.avatar&.attached? %> <%= image_tag current_user.profile.avatar.variant(:thumb), class: "account-user-avatar" %> <% else %> <div class="account-user-avatar d-flex align-items-center justify-content-center bg-light"> <span class="fw-bold"> <%= (current_user.profile&.nickname || current_user.email).first.upcase %> </span> </div> <% end %></div> <div class="list-group account-tabs"> <%= link_to t('account.tabs.my_profile'), account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :profile}" %>text<h5 class="mb-0"> <%= current_user.profile&.nickname || current_user.email %> </h5> <small class="text-muted"> <%= t('account.sidebar.subtitle') %> </small>
</div> </div>text<%= link_to t('account.tabs.edit_user'), edit_user_account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :edit_user}" %> <%= link_to t('account.tabs.edit_profile'), edit_profile_account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :edit_profile}" %> <%= link_to t('account.tabs.addresses'), addresses_account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :addresses}" %>
_tabs.html+mobile.erb:
<div class="mb-3 text-center"> <% if current_user.profile&.avatar&.attached? %> <%= image_tag current_user.profile.avatar.variant(:thumb), class: "account-user-avatar mb-2" %> <% else %> <div class="account-user-avatar d-inline-flex align-items-center justify-content-center bg-light mb-2"> <span class="fw-bold"> <%= (current_user.profile&.nickname || current_user.email).first.upcase %> </span> </div> <% end %> <div class="fw-semibold"> <%= current_user.profile&.nickname || current_user.email %> </div> <small class="text-muted"> <%= t('account.sidebar.subtitle') %> </small> </div> <div class="nav nav-pills justify-content-between flex-wrap gap-2 mb-3"> <%= link_to t('account.tabs.my_profile'), account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :profile}" %><%= link_to t('account.tabs.edit_user'),
edit_user_account_path,
class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :edit_user}" %>
<%= link_to t('account.tabs.edit_profile'),
edit_profile_account_path,
class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :edit_profile}" %>
<%= link_to t('account.tabs.addresses'),
addresses_account_path,
class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :addresses}" %>
addresses.html.erb:
<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout" style="max-width: 1100px; width: 100%;"> <div class="account-sidebar"> <%= render 'account/tabs', active: :addresses %> </div></div> </div>text<div class="account-main"> <div class="account-main-card"> <h3 class="mb-4"><%= t('account.addresses.title') %></h3> <% if @addresses.present? %> <div class="row g-3 mb-4"> <%= render partial: 'account/address_card', collection: @addresses, as: :address %> </div> <% else %> <p class="text-muted mb-4"> <%= t('account.addresses.empty') %> </p> <% end %> <hr class="my-3"> <h5 class="mb-3"> <%= t('account.addresses.new_title') %> </h5> <%= render 'devise/shared/error_messages', resource: @address %> <%= form_for(@address, url: create_address_account_path, html: { method: :post, novalidate: true, data: { turbo: false } }) do |f| %> <%= render 'devise/registrations/address_fields', f: f, context: :edit %> <div class="d-grid"> <%= f.submit t('account.addresses.save_new'), class: 'btn btn-success' %> </div> <% end %> </div> </div>
edit_profile.html.erb:
<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout" style="max-width: 1100px; width: 100%;"> <div class="account-sidebar"> <%= render 'account/tabs', active: :edit_profile %> </div></div> </div>text<div class="account-main"> <div class="account-main-card"> <h3 class="mb-4"><%= t('account.edit_profile.title') %></h3> <%= render 'devise/shared/error_messages', resource: @profile %> <%= form_for(current_user, url: update_profile_account_path, html: { method: :patch, novalidate: true, multipart: true, data: { turbo: false } }) do |f| %> <%= f.fields_for :profile, @profile do |pf| %> <%= render 'devise/registrations/profile_fields', f: pf, context: :edit %> <% end %> <div class='d-grid mb-2'> <%= f.submit t('account.edit_profile.submit'), class: 'btn btn-success' %> </div> <% end %> </div> </div>
edit_user.html.erb:
<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout" style="max-width: 1100px; width: 100%;"> <div class="account-sidebar"> <%= render 'account/tabs', active: :edit_user %> </div></div> </div>text<div class="account-main"> <div class="account-main-card"> <h3 class="mb-4"><%= t('account.edit_user.title') %></h3> <%= render 'devise/shared/error_messages', resource: current_user %> <%= form_for(current_user, url: update_user_account_path, html: { method: :patch, novalidate: true, data: { turbo: false } }) do |f| %> <div class="mb-3"> <%= f.email_field :email, autocomplete: 'email', placeholder: t('devise.registrations.edit.email_placeholder'), class: 'form-control' %> </div> <hr class="my-3"> <h5 class="mb-3"><%= t('account.edit_user.security_title') %></h5> <div class='mb-3'> <%= f.password_field :password, autocomplete: 'new-password', placeholder: t('devise.registrations.edit.new_password_placeholder'), class: 'form-control' %> </div> <div class='mb-3'> <%= f.password_field :password_confirmation, autocomplete: 'new-password', placeholder: t('devise.registrations.edit.confirm_password_placeholder'), class: 'form-control' %> </div> <div class='mb-3'> <%= f.password_field :current_password, autocomplete: 'current-password', placeholder: t('devise.registrations.edit.current_password_placeholder'), class: 'form-control' %> </div> <div class='d-grid mb-2'> <%= f.submit t('devise.registrations.edit.submit'), class: 'btn btn-success' %> </div> <% end %> </div> </div>
show.html.erb:
<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout" style="max-width: 1100px; width: 100%;"> <div class="account-sidebar"> <%= render 'account/tabs', active: :profile %> </div></div> </div>text<div class="account-main"> <div class="account-main-card"> <h3 class="mb-4"><%= t('account.my_profile.title') %></h3> <div class="row gy-4"> <div class="col-md-4 text-center"> <% if @profile&.avatar&.attached? %> <%= image_tag @profile.avatar.variant(:preview), class: "img-fluid rounded-3 mb-3" %> <% else %> <div class="bg-light rounded-3 d-flex align-items-center justify-content-center" style="height: 220px;"> <span class="text-muted"> <%= t('account.my_profile.no_avatar') %> </span> </div> <% end %> <p class="mt-2 mb-0 fw-semibold"> <%= @profile&.nickname || current_user.email %> </p> <small class="text-muted"><%= current_user.email %></small> </div> <div class="col-md-8"> <h5 class="mb-3"><%= t('account.my_profile.basic_info') %></h5> <dl class="row mb-4"> <dt class="col-sm-4"><%= t('account.labels.full_name') %></dt> <dd class="col-sm-8"> <%= [@profile&.first_name, @profile&.last_name].compact.join(' ').presence || '—' %> </dd> <dt class="col-sm-4">Email</dt> <dd class="col-sm-8"><%= current_user.email %></dd> <dt class="col-sm-4"><%= t('account.labels.birthday') %></dt> <dd class="col-sm-8"><%= @profile&.birthday&.strftime('%d.%m.%Y') || '—' %></dd> <dt class="col-sm-4"><%= t('account.labels.metrics') %></dt> <dd class="col-sm-8"> <% if @profile&.weight || @profile&.height %> <%= [@profile&.weight && "#{@profile.weight} kg", @profile&.height && "#{@profile.height} cm"].compact.join(' / ') %> <% else %> — <% end %> </dd> </dl> <h5 class="mb-3"><%= t('account.my_profile.about') %></h5> <p class="mb-0"> <%= @profile&.description.presence || t('account.my_profile.no_description') %> </p> </div> </div> </div> </div>
account.scss:
.account-layout {
display: flex;
gap: 1.5rem;
align-items: flex-start;
}
.account-sidebar {
width: 260px;
}
.account-main {
flex: 1;
}
.account-sidebar-card,
.account-main-card {
background: #ffffff;
border-radius: 0.75rem;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12);
padding: 1.5rem;
}
.account-user-header {
text-align: center;
margin-bottom: 1.5rem;
}
.account-user-avatar {
width: 95px;
height: 95px;
border-radius: 50%;
object-fit: cover;
box-shadow: 0 0 0 4px #ffffff, 0 4px 10px rgba(0, 0, 0, 0.15);
margin: 0 auto 1rem;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
}
.account-tabs .list-group-item {
border: none;
border-radius: 0.5rem;
margin-bottom: 0.25rem;
font-weight: 500;
}
.account-tabs .list-group-item.active {
background-color: #198754;
color: #ffffff;
}
.account-tabs .list-group-item:hover {
background-color: #e9f5ef;
}
@media (max-width: 768px) {
.account-layout {
flex-direction: column;
}
.account-sidebar,
.account-main {
width: 100%;
}
.account-sidebar-card {
margin-bottom: 1rem;
}
.account-tabs {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.account-tabs .list-group-item {
margin-bottom: 0;
flex: 1 1 calc(50% - 0.5rem);
text-align: center;
}
}
devise/form_card.scss:
.auth-card {
width: 400px;
z-index: 1;
overflow: visible;
}
.auth-card::before {
content: "";
position: absolute;
top: -50px;
left: 50%;
transform: translateX(-50%);
width: 100px;
height: 60px;
background: #f7edb6;
border-radius: 50% 50% 0 0;
box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.1);
}
.auth-card::after {
content: "";
position: absolute;
top: -70px;
left: 50%;
transform: translateX(-50%);
width: 75px;
height: 75px;
border-radius: 50%;
background-size: contain;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
background-color: #abf5f5;
background-image: url("/assets/shared/logo.png");
}
@media (max-width: 600px) {
.auth-card {
width: 330px;
margin: 0 auto;
}
}
.auth-card--wide {
width: 800px;
max-width: 100%;
}
@media (max-width: 768px) {
.auth-card--wide {
width: 100%;
max-width: 100%;
margin: 0 auto;
padding-inline: 1rem;
}
}
сюда кидай полностью измененные файлы без комментов, чтобы я их копировал и вставлял. также не меняй структуру и названия переменных
_address_card.html.erb
erb<div class="col-md-6"> <div class="border rounded-3 p-3 h-100"> <h6 class="mb-2"> <%= address.country %>, <%= address.city %> </h6> <p class="mb-1"> <%= "#{address.street} #{address.house}" %> <% if address.apartment.present? %> , <%= t('account.addresses.apartment') %> <%= address.apartment %> <% end %> </p> <p class="mb-1 text-muted"> <%= t('account.addresses.zip') %>: <%= address.zip_code %> </p> <% if address.entrance.present? || address.floor.present? %> <p class="mb-0 text-muted"> <%= [ (address.entrance.present? && "#{t('account.addresses.entrance')} #{address.entrance}"), (address.floor.present? && "#{t('account.addresses.floor')} #{address.floor}") ].compact.join(', ') %> </p> <% end %> </div> </div>
_tabs.html+desktop.erb
erb<div class="account-sidebar-card h-100 d-flex flex-column"> <div class="account-user-header"> <% if current_user.profile&.avatar&.attached? %> <%= image_tag current_user.profile.avatar.variant(:thumb), class: "account-user-avatar" %> <% else %> <div class="account-user-avatar d-flex align-items-center justify-content-center bg-light"> <span class="fw-bold"> <%= (current_user.profile&.nickname || current_user.email).first.upcase %> </span> </div> <% end %> <h5 class="mb-0 text-break"> <%= current_user.profile&.nickname || current_user.email %> </h5> <small class="text-muted"> <%= t('account.sidebar.subtitle') %> </small> </div> <div class="list-group account-tabs"> <%= link_to t('account.tabs.my_profile'), account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :profile}" %> <%= link_to t('account.tabs.edit_user'), edit_user_account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :edit_user}" %> <%= link_to t('account.tabs.edit_profile'), edit_profile_account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :edit_profile}" %> <%= link_to t('account.tabs.addresses'), addresses_account_path, class: "list-group-item list-group-item-action #{'active' if local_assigns[:active] == :addresses}" %> </div> </div>
_tabs.html+mobile.erb
erb<div class="mb-3 text-center"> <% if current_user.profile&.avatar&.attached? %> <%= image_tag current_user.profile.avatar.variant(:thumb), class: "account-user-avatar mb-2" %> <% else %> <div class="account-user-avatar d-inline-flex align-items-center justify-content-center bg-light mb-2"> <span class="fw-bold"> <%= (current_user.profile&.nickname || current_user.email).first.upcase %> </span> </div> <% end %> <div class="fw-semibold text-break"> <%= current_user.profile&.nickname || current_user.email %> </div> <small class="text-muted"> <%= t('account.sidebar.subtitle') %> </small> </div> <div class="nav nav-pills justify-content-between flex-wrap gap-2 mb-3"> <%= link_to t('account.tabs.my_profile'), account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :profile}" %> <%= link_to t('account.tabs.edit_user'), edit_user_account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :edit_user}" %> <%= link_to t('account.tabs.edit_profile'), edit_profile_account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :edit_profile}" %> <%= link_to t('account.tabs.addresses'), addresses_account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :addresses}" %> </div>
addresses.html.erb
erb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :addresses %> </div> <div class="account-main flex-fill"> <div class="account-main-card h-100"> <h3 class="mb-4"><%= t('account.addresses.title') %></h3> <% if @addresses.present? %> <div class="row g-3 mb-4"> <%= render partial: 'account/address_card', collection: @addresses, as: :address %> </div> <% else %> <p class="text-muted mb-4"> <%= t('account.addresses.empty') %> </p> <% end %> <hr class="my-3"> <h5 class="mb-3"> <%= t('account.addresses.new_title') %> </h5> <%= render 'devise/shared/error_messages', resource: @address %> <%= form_for(@address, url: create_address_account_path, html: { method: :post, novalidate: true, data: { turbo: false } }) do |f| %> <%= render 'devise/registrations/address_fields', f: f, context: :edit %> <div class="d-grid"> <%= f.submit t('account.addresses.save_new'), class: 'btn btn-success' %> </div> <% end %> </div> </div> </div> </div>
edit_profile.html.erb
erb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :edit_profile %> </div> <div class="account-main flex-fill"> <div class="account-main-card h-100"> <h3 class="mb-4"><%= t('account.edit_profile.title') %></h3> <%= render 'devise/shared/error_messages', resource: @profile %> <%= form_for(current_user, url: update_profile_account_path, html: { method: :patch, novalidate: true, multipart: true, data: { turbo: false } }) do |f| %> <%= f.fields_for :profile, @profile do |pf| %> <%= render 'devise/registrations/profile_fields', f: pf, context: :edit %> <% end %> <div class='d-grid mb-2'> <%= f.submit t('account.edit_profile.submit'), class: 'btn btn-success' %> </div> <% end %> </div> </div> </div> </div>
edit_user.html.erb
erb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :edit_user %> </div> <div class="account-main flex-fill"> <div class="account-main-card h-100"> <h3 class="mb-4"><%= t('account.edit_user.title') %></h3> <%= render 'devise/shared/error_messages', resource: current_user %> <%= form_for(current_user, url: update_user_account_path, html: { method: :patch, novalidate: true, data: { turbo: false } }) do |f| %> <div class="mb-3"> <%= f.email_field :email, autocomplete: 'email', placeholder: t('devise.registrations.edit.email_placeholder'), class: 'form-control' %> </div> <hr class="my-3"> <h5 class="mb-3"><%= t('account.edit_user.security_title') %></h5> <div class='mb-3'> <%= f.password_field :password, autocomplete: 'new-password', placeholder: t('devise.registrations.edit.new_password_placeholder'), class: 'form-control' %> </div> <div class='mb-3'> <%= f.password_field :password_confirmation, autocomplete: 'new-password', placeholder: t('devise.registrations.edit.confirm_password_placeholder'), class: 'form-control' %> </div> <div class='mb-3'> <%= f.password_field :current_password, autocomplete: 'current-password', placeholder: t('devise.registrations.edit.current_password_placeholder'), class: 'form-control' %> </div> <div class='d-grid mb-2'> <%= f.submit t('devise.registrations.edit.submit'), class: 'btn btn-success' %> </div> <% end %> </div> </div> </div> </div>
show.html.erb
erb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :profile %> </div> <div class="account-main flex-fill"> <div class="account-main-card h-100"> <h3 class="mb-4"><%= t('account.my_profile.title') %></h3> <div class="row gy-4"> <div class="col-md-4 text-center"> <% if @profile&.avatar&.attached? %> <%= image_tag @profile.avatar.variant(:preview), class: "img-fluid rounded-3 mb-3" %> <% else %> <div class="bg-light rounded-3 d-flex align-items-center justify-content-center account-avatar-placeholder"> <span class="text-muted"> <%= t('account.my_profile.no_avatar') %> </span> </div> <% end %> <p class="mt-2 mb-0 fw-semibold text-break"> <%= @profile&.nickname || current_user.email %> </p> <small class="text-muted text-break"><%= current_user.email %></small> </div> <div class="col-md-8"> <h5 class="mb-3"><%= t('account.my_profile.basic_info') %></h5> <dl class="row mb-4"> <dt class="col-sm-4"><%= t('account.labels.full_name') %></dt> <dd class="col-sm-8"> <%= [@profile&.first_name, @profile&.last_name].compact.join(' ').presence || '—' %> </dd> <dt class="col-sm-4">Email</dt> <dd class="col-sm-8 text-break"><%= current_user.email %></dd> <dt class="col-sm-4"><%= t('account.labels.birthday') %></dt> <dd class="col-sm-8"><%= @profile&.birthday&.strftime('%d.%m.%Y') || '—' %></dd> <dt class="col-sm-4"><%= t('account.labels.metrics') %></dt> <dd class="col-sm-8"> <% if @profile&.weight || @profile&.height %> <%= [@profile&.weight && "#{@profile.weight} kg", @profile&.height && "#{@profile.height} cm"].compact.join(' / ') %> <% else %> — <% end %> </dd> </dl> <h5 class="mb-3"><%= t('account.my_profile.about') %></h5> <p class="mb-0"> <%= @profile&.description.presence || t('account.my_profile.no_description') %> </p> </div> </div> </div> </div> </div> </div>
account.scss
scss.account-layout { width: 100%; } .account-sidebar-card, .account-main-card { background: #ffffff; border-radius: 0.75rem; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); padding: 1.5rem; } .account-user-header { text-align: center; margin-bottom: 1.5rem; } .account-user-avatar { width: 6rem; height: 6rem; border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 4px #ffffff, 0 4px 10px rgba(0, 0, 0, 0.15); margin: 0 auto 1rem; display: flex; align-items: center; justify-content: center; font-size: 2rem; } .account-tabs .list-group-item { border: none; border-radius: 0.5rem; margin-bottom: 0.25rem; font-weight: 500; } .account-tabs .list-group-item.active { background-color: #198754; color: #ffffff; } .account-tabs .list-group-item:hover { background-color: #e9f5ef; } .account-avatar-placeholder { min-height: 12rem; } @media (max-width: 768px) { .account-sidebar-card { margin-bottom: 1rem; } .account-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; } .account-tabs .list-group-item { margin-bottom: 0; flex: 1 1 calc(50% - 0.5rem); text-align: center; } }
devise/form_card.scss
scss.auth-card { width: 100%; max-width: 25rem; z-index: 1; overflow: visible; margin: 0 auto; } .auth-card::before { content: ""; position: absolute; top: -3.125rem; left: 50%; transform: translateX(-50%); width: 6.25rem; height: 3.75rem; background: #f7edb6; border-radius: 50% 50% 0 0; box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.1); } .auth-card::after { content: ""; position: absolute; top: -4.375rem; left: 50%; transform: translateX(-50%); width: 4.6875rem; height: 4.6875rem; border-radius: 50%; background-size: contain; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); background-color: #abf5f5; background-image: url("/assets/shared/logo.png"); } @media (max-width: 600px) { .auth-card { max-width: 20.625rem; } } .auth-card--wide { width: 100%; max-width: 50rem; } @media (max-width: 768px) { .auth-card--wide { width: 100%; max-width: 100%; margin: 0 auto; padding-inline: 1rem; } }
Так. смотри. Когда у меня отображается email, то он уже помещается. но если они слишком короткие, то карточка слева получается слишком маленькой как на картинке, хотелось бы, чтобы минимальный размер был чуть побольше. также ты не все стили перенес на бутстрап, которые можно было. Сначала точно также полность скинь код для решения этих проблем. А дальше есть еще одна вещь. Дело в том, что когда пользователь регистриурется первым у него появляется экран с паролем и почтой. Дальше, если он правильно их ввел, тогда он переносится на создаия профиля, но если при создании профиля он введет что-то не так при создании профиля, тогда он переносится на обратно на страницу с паролями, а они сбрасываются и ему приходится их сначала вводить. как можно это решить?
Вот обновлённые файлы с учётом двух пожеланий:
app/views/account/_tabs.html+desktop.erberb<div class="account-sidebar-card bg-white rounded-3 shadow-sm p-4 h-100 d-flex flex-column"> <div class="account-user-header text-center mb-4"> <% if current_user.profile&.avatar&.attached? %> <%= image_tag current_user.profile.avatar.variant(:thumb), class: "account-user-avatar" %> <% else %> <div class="account-user-avatar d-flex align-items-center justify-content-center bg-light"> <span class="fw-bold"> <%= (current_user.profile&.nickname || current_user.email).first.upcase %> </span> </div> <% end %> <h5 class="mb-0 text-break"> <%= current_user.profile&.nickname || current_user.email %> </h5> <small class="text-muted"> <%= t('account.sidebar.subtitle') %> </small> </div> <div class="list-group account-tabs"> <%= link_to t('account.tabs.my_profile'), account_path, class: "list-group-item list-group-item-action border-0 rounded-3 mb-1 fw-semibold #{'active' if local_assigns[:active] == :profile}" %> <%= link_to t('account.tabs.edit_user'), edit_user_account_path, class: "list-group-item list-group-item-action border-0 rounded-3 mb-1 fw-semibold #{'active' if local_assigns[:active] == :edit_user}" %> <%= link_to t('account.tabs.edit_profile'), edit_profile_account_path, class: "list-group-item list-group-item-action border-0 rounded-3 mb-1 fw-semibold #{'active' if local_assigns[:active] == :edit_profile}" %> <%= link_to t('account.tabs.addresses'), addresses_account_path, class: "list-group-item list-group-item-action border-0 rounded-3 mb-1 fw-semibold #{'active' if local_assigns[:active] == :addresses}" %> </div> </div>
app/views/account/_tabs.html+mobile.erberb<div class="mb-3 text-center"> <% if current_user.profile&.avatar&.attached? %> <%= image_tag current_user.profile.avatar.variant(:thumb), class: "account-user-avatar rounded-circle mb-2" %> <% else %> <div class="account-user-avatar d-inline-flex align-items-center justify-content-center bg-light rounded-circle mb-2"> <span class="fw-bold"> <%= (current_user.profile&.nickname || current_user.email).first.upcase %> </span> </div> <% end %> <div class="fw-semibold text-break"> <%= current_user.profile&.nickname || current_user.email %> </div> <small class="text-muted"> <%= t('account.sidebar.subtitle') %> </small> </div> <div class="nav nav-pills justify-content-between flex-wrap gap-2 mb-3"> <%= link_to t('account.tabs.my_profile'), account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :profile}" %> <%= link_to t('account.tabs.edit_user'), edit_user_account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :edit_user}" %> <%= link_to t('account.tabs.edit_profile'), edit_profile_account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :edit_profile}" %> <%= link_to t('account.tabs.addresses'), addresses_account_path, class: "btn btn-outline-secondary flex-fill #{'active' if local_assigns[:active] == :addresses}" %> </div>
app/views/account/addresses.html.erberb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :addresses %> </div> <div class="account-main flex-fill"> <div class="account-main-card bg-white rounded-3 shadow-sm p-4 h-100"> <h3 class="mb-4"><%= t('account.addresses.title') %></h3> <% if @addresses.present? %> <div class="row g-3 mb-4"> <%= render partial: 'account/address_card', collection: @addresses, as: :address %> </div> <% else %> <p class="text-muted mb-4"> <%= t('account.addresses.empty') %> </p> <% end %> <hr class="my-3"> <h5 class="mb-3"> <%= t('account.addresses.new_title') %> </h5> <%= render 'devise/shared/error_messages', resource: @address %> <%= form_for(@address, url: create_address_account_path, html: { method: :post, novalidate: true, data: { turbo: false } }) do |f| %> <%= render 'devise/registrations/address_fields', f: f, context: :edit %> <div class="d-grid"> <%= f.submit t('account.addresses.save_new'), class: 'btn btn-success' %> </div> <% end %> </div> </div> </div> </div>
app/views/account/edit_profile.html.erberb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :edit_profile %> </div> <div class="account-main flex-fill"> <div class="account-main-card bg-white rounded-3 shadow-sm p-4 h-100"> <h3 class="mb-4"><%= t('account.edit_profile.title') %></h3> <%= render 'devise/shared/error_messages', resource: @profile %> <%= form_for(current_user, url: update_profile_account_path, html: { method: :patch, novalidate: true, multipart: true, data: { turbo: false } }) do |f| %> <%= f.fields_for :profile, @profile do |pf| %> <%= render 'devise/registrations/profile_fields', f: pf, context: :edit %> <% end %> <div class='d-grid mb-2'> <%= f.submit t('account.edit_profile.submit'), class: 'btn btn-success' %> </div> <% end %> </div> </div> </div> </div>
app/views/account/edit_user.html.erberb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :edit_user %> </div> <div class="account-main flex-fill"> <div class="account-main-card bg-white rounded-3 shadow-sm p-4 h-100"> <h3 class="mb-4"><%= t('account.edit_user.title') %></h3> <%= render 'devise/shared/error_messages', resource: current_user %> <%= form_for(current_user, url: update_user_account_path, html: { method: :patch, novalidate: true, data: { turbo: false } }) do |f| %> <div class="mb-3"> <%= f.email_field :email, autocomplete: 'email', placeholder: t('devise.registrations.edit.email_placeholder'), class: 'form-control' %> </div> <hr class="my-3"> <h5 class="mb-3"><%= t('account.edit_user.security_title') %></h5> <div class='mb-3'> <%= f.password_field :password, autocomplete: 'new-password', placeholder: t('devise.registrations.edit.new_password_placeholder'), class: 'form-control' %> </div> <div class='mb-3'> <%= f.password_field :password_confirmation, autocomplete: 'new-password', placeholder: t('devise.registrations.edit.confirm_password_placeholder'), class: 'form-control' %> </div> <div class='mb-3'> <%= f.password_field :current_password, autocomplete: 'current-password', placeholder: t('devise.registrations.edit.current_password_placeholder'), class: 'form-control' %> </div> <div class='d-grid mb-2'> <%= f.submit t('devise.registrations.edit.submit'), class: 'btn btn-success' %> </div> <% end %> </div> </div> </div> </div>
app/views/account/show.html.erberb<div class="d-flex flex-grow-1 justify-content-center align-items-start mt-4"> <div class="account-layout container d-flex flex-column flex-md-row align-items-stretch gap-4"> <div class="account-sidebar mb-4 mb-md-0"> <%= render 'account/tabs', active: :profile %> </div> <div class="account-main flex-fill"> <div class="account-main-card bg-white rounded-3 shadow-sm p-4 h-100"> <h3 class="mb-4"><%= t('account.my_profile.title') %></h3> <div class="row gy-4"> <div class="col-md-4 text-center"> <% if @profile&.avatar&.attached? %> <%= image_tag @profile.avatar.variant(:preview), class: "img-fluid rounded-3 mb-3" %> <% else %> <div class="bg-light rounded-3 d-flex align-items-center justify-content-center account-avatar-placeholder"> <span class="text-muted"> <%= t('account.my_profile.no_avatar') %> </span> </div> <% end %> <p class="mt-2 mb-0 fw-semibold text-break"> <%= @profile&.nickname || current_user.email %> </p> <small class="text-muted text-break"><%= current_user.email %></small> </div> <div class="col-md-8"> <h5 class="mb-3"><%= t('account.my_profile.basic_info') %></h5> <dl class="row mb-4"> <dt class="col-sm-4"><%= t('account.labels.full_name') %></dt> <dd class="col-sm-8"> <%= [@profile&.first_name, @profile&.last_name].compact.join(' ').presence || '—' %> </dd> <dt class="col-sm-4">Email</dt> <dd class="col-sm-8 text-break"><%= current_user.email %></dd> <dt class="col-sm-4"><%= t('account.labels.birthday') %></dt> <dd class="col-sm-8"><%= @profile&.birthday&.strftime('%d.%m.%Y') || '—' %></dd> <dt class="col-sm-4"><%= t('account.labels.metrics') %></dt> <dd class="col-sm-8"> <% if @profile&.weight || @profile&.height %> <%= [@profile&.weight && "#{@profile.weight} kg", @profile&.height && "#{@profile.height} cm"].compact.join(' / ') %> <% else %> — <% end %> </dd> </dl> <h5 class="mb-3"><%= t('account.my_profile.about') %></h5> <p class="mb-0"> <%= @profile&.description.presence || t('account.my_profile.no_description') %> </p> </div> </div> </div> </div> </div> </div>
app/assets/stylesheets/account.scssscss.account-layout { width: 100%; } .account-user-avatar { width: 6rem; height: 6rem; border-radius: 50%; object-fit: cover; box-shadow: 0 0 0 4px #ffffff, 0 4px 10px rgba(0, 0, 0, 0.15); margin: 0 auto 1rem; display: flex; align-items: center; justify-content: center; font-size: 2rem; } .account-tabs .list-group-item.active { background-color: #198754; color: #ffffff; } .account-tabs .list-group-item:hover { background-color: #e9f5ef; } .account-avatar-placeholder { min-height: 12rem; } @media (min-width: 768px) { .account-sidebar-card, .account-main-card { min-height: 20rem; } } @media (max-width: 768px) { .account-sidebar-card { margin-bottom: 1rem; } .account-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; } .account-tabs .list-group-item { margin-bottom: 0; flex: 1 1 calc(50% - 0.5rem); text-align: center; } }
app/assets/stylesheets/devise/form_card.scssscss.auth-card { width: 100%; max-width: 25rem; z-index: 1; overflow: visible; margin: 0 auto; } .auth-card::before { content: ""; position: absolute; top: -3.125rem; left: 50%; transform: translateX(-50%); width: 6.25rem; height: 3.75rem; background: #f7edb6; border-radius: 50% 50% 0 0; box-shadow: 0 -4px 10px rgba(0, 0, 0, 0.1); } .auth-card::after { content: ""; position: absolute; top: -4.375rem; left: 50%; transform: translateX(-50%); width: 4.6875rem; height: 4.6875rem; border-radius: 50%; background-size: contain; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); background-color: #abf5f5; background-image: url("/assets/shared/logo.png"); } @media (max-width: 600px) { .auth-card { max-width: 20.625rem; } } .auth-card--wide { width: 100%; max-width: 50rem; } @media (max-width: 768px) { .auth-card--wide { width: 100%; max-width: 100%; margin: 0 auto; padding-inline: 1rem; } }
Сценарий:
По сути, есть две нормальные стратегии.
Это самый простой и «девизовский» путь.
sign_up переопределяешь редирект:ruby# app/controllers/users/registrations_controller.rb class Users::RegistrationsController < Devise::RegistrationsController protected def after_sign_up_path_for(resource) edit_profile_account_path end end
AccountsController#update_profile при ошибках делай не redirect_to, а именно render той же вьюхи:rubydef update_profile @profile = current_user.profile || current_user.build_profile if @profile.update(profile_params) redirect_to account_path else render :edit_profile, status: :unprocessable_entity end end
Тогда:
Profile;Это обычно решает проблему полностью и логически красиво выглядит.
Если очень хочется, чтобы формально пользователь создавался только после обоих шагов:
rubysession[:registration_user] = { email: params[:user][:email], password: params[:user][:password], password_confirmation: params[:user][:password_confirmation] } redirect_to new_profile_step_path
rubydef create user_attrs = session[:registration_user] || {} @user = User.new(user_attrs) @user.build_profile(profile_params) if @user.save session.delete(:registration_user) sign_in(@user) redirect_to account_path else render :new_profile, status: :unprocessable_entity end end
Тут при ошибках профиля форма new_profile просто рендерится снова, а email/пароль берутся из @user, который собран из данных в сессии — вводить их заново не нужно.
Если хочешь, можешь скинуть свой RegistrationsController/AccountsController, я прямо на нём покажу, какой именно render/redirect_to поменять и где лучше сделать редирект после sign_up.