mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 20:14:08 +00:00
Add protection indicator to entries and unlock functionality (#765)
* feat: add protection indicator to entries and unlock functionality - Introduced protection indicator component rendering on hover and in detail views. - Added support to unlock entries, clearing protection flags (`user_modified`, `import_locked`, and locked attributes). - Updated routes, controllers, and models to enable unlock functionality for trades and transactions. - Refactored views and localized content to support the new feature. - Added relevant tests for unlocking functionality and attribute handling. * feat: improve sync protection and turbo stream updates for entries - Added tests for turbo stream updates reflecting protection indicators. - Ensured user-modified entries lock specific attributes to prevent overwrites. - Updated controllers to mark entries as user-modified and reload for accurate rendering. - Enhanced protection indicator rendering using turbo frames. - Applied consistent lock state handling across trades and transactions. * Address PR review comments for protection indicator --------- Co-authored-by: luckyPipewrench <luckypipewrench@proton.me>
This commit is contained in:
42
app/views/entries/_protection_indicator.html.erb
Normal file
42
app/views/entries/_protection_indicator.html.erb
Normal file
@@ -0,0 +1,42 @@
|
||||
<%# locals: (entry:, unlock_path:) %>
|
||||
|
||||
<%# Protection indicator - shows when entry is protected from sync overwrites %>
|
||||
<%= turbo_frame_tag dom_id(entry, :protection) do %>
|
||||
<% if entry.protected_from_sync? && !entry.excluded? %>
|
||||
<details class="mx-4 my-3 border border-primary rounded-lg overflow-hidden">
|
||||
<summary class="flex items-center gap-2 cursor-pointer p-3 bg-container hover:bg-surface-hover list-none [&::-webkit-details-marker]:hidden">
|
||||
<%= icon "lock", size: "sm", class: "text-secondary" %>
|
||||
<span class="text-sm font-medium text-primary flex-1"><%= t("entries.protection.title") %></span>
|
||||
<%= icon "chevron-down", size: "sm", class: "text-secondary transition-transform [[open]>&]:rotate-180" %>
|
||||
</summary>
|
||||
<div class="p-4 border-t border-primary bg-surface-inset space-y-4">
|
||||
<p class="text-sm text-secondary">
|
||||
<%= t("entries.protection.description") %>
|
||||
</p>
|
||||
|
||||
<% if entry.locked_field_names.any? %>
|
||||
<div class="space-y-2">
|
||||
<p class="text-xs font-medium text-secondary"><%= t("entries.protection.locked_fields_label") %></p>
|
||||
<% entry.locked_fields_with_timestamps.each do |field, timestamp| %>
|
||||
<div class="flex items-center justify-between text-sm">
|
||||
<span class="text-primary"><%= field.humanize %></span>
|
||||
<span class="text-secondary"><%= timestamp.respond_to?(:strftime) ? l(timestamp.to_date, format: :long) : timestamp %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= link_to unlock_path,
|
||||
class: "w-full flex items-center justify-center gap-2 px-3 py-2 text-sm font-medium rounded-lg border border-secondary text-primary hover:bg-surface-hover transition-colors",
|
||||
data: {
|
||||
turbo_method: :post,
|
||||
turbo_confirm: t("entries.protection.unlock_confirm"),
|
||||
turbo_frame: "_top"
|
||||
} do %>
|
||||
<%= icon "unlock", size: "sm" %>
|
||||
<span><%= t("entries.protection.unlock_button") %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<%= turbo_frame_tag dom_id(entry) do %>
|
||||
<%= turbo_frame_tag dom_id(trade) do %>
|
||||
<div class="grid grid-cols-12 items-center <%= entry.excluded ? "text-gray-400 bg-gray-25" : "text-primary" %> text-sm font-medium p-4">
|
||||
<div class="group grid grid-cols-12 items-center <%= entry.excluded ? "text-secondary bg-surface-inset" : "text-primary" %> text-sm font-medium p-4">
|
||||
<div class="col-span-8 flex items-center gap-4">
|
||||
<%= check_box_tag dom_id(entry, "selection"),
|
||||
class: "checkbox checkbox--light hidden lg:block",
|
||||
@@ -44,7 +44,16 @@
|
||||
<%= render "investment_activity/quick_edit_badge", entry: entry, entryable: trade %>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 col-span-4 lg:col-span-2 ml-auto text-right">
|
||||
<div class="shrink-0 col-span-4 lg:col-span-2 ml-auto flex items-center justify-end gap-2">
|
||||
<%# Protection indicator - shows on hover when entry is protected from sync %>
|
||||
<% if entry.protected_from_sync? && !entry.excluded? %>
|
||||
<%= link_to entry_path(entry),
|
||||
data: { turbo_frame: "drawer", turbo_prefetch: false },
|
||||
class: "invisible group-hover:visible transition-opacity",
|
||||
title: t("entries.protection.tooltip") do %>
|
||||
<%= icon "lock", size: "sm", class: "text-secondary" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= content_tag :p,
|
||||
format_money(-entry.amount_money),
|
||||
class: ["text-green-600": entry.amount.negative?] %>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<% trade = @entry.trade %>
|
||||
|
||||
<% dialog.with_body do %>
|
||||
<%= render "entries/protection_indicator", entry: @entry, unlock_path: unlock_trade_path(trade) %>
|
||||
|
||||
<% dialog.with_section(title: t(".details"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: @entry,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<%= turbo_frame_tag dom_id(entry) do %>
|
||||
<%= turbo_frame_tag dom_id(transaction) do %>
|
||||
<div class="flex lg:grid lg:grid-cols-12 items-center text-primary text-sm font-medium p-3 lg:p-4 <%= entry.excluded ? "opacity-50 text-gray-400" : "" %>">
|
||||
<div class="group flex lg:grid lg:grid-cols-12 items-center text-primary text-sm font-medium p-3 lg:p-4 <%= entry.excluded ? "opacity-50 text-secondary" : "" %>">
|
||||
|
||||
<div class="pr-4 lg:pr-10 flex items-center gap-3 lg:gap-4 col-span-8 min-w-0">
|
||||
<%= check_box_tag dom_id(entry, "selection"),
|
||||
@@ -145,7 +145,16 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0 col-span-4 lg:col-span-2 ml-auto text-right">
|
||||
<div class="shrink-0 col-span-4 lg:col-span-2 ml-auto flex items-center justify-end gap-2">
|
||||
<%# Protection indicator - shows on hover when entry is protected from sync %>
|
||||
<% if entry.protected_from_sync? && !entry.excluded? %>
|
||||
<%= link_to entry_path(entry),
|
||||
data: { turbo_frame: "drawer", turbo_prefetch: false },
|
||||
class: "invisible group-hover:visible transition-opacity",
|
||||
title: t("entries.protection.tooltip") do %>
|
||||
<%= icon "lock", size: "sm", class: "text-secondary" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= content_tag :p,
|
||||
transaction.transfer? && view_ctx == "global" ? "+/- #{format_money(entry.amount_money.abs)}" : format_money(-entry.amount_money),
|
||||
class: ["text-green-600": entry.amount.negative?] %>
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render "entries/protection_indicator", entry: @entry, unlock_path: unlock_transaction_path(@entry.transaction) %>
|
||||
|
||||
<% dialog.with_section(title: t(".overview"), open: true) do %>
|
||||
<div class="pb-4">
|
||||
<%= styled_form_with model: @entry,
|
||||
|
||||
Reference in New Issue
Block a user