diff --git a/app/javascript/controllers/tooltip_controller.js b/app/javascript/controllers/tooltip_controller.js index cad419a1..7013607f 100644 --- a/app/javascript/controllers/tooltip_controller.js +++ b/app/javascript/controllers/tooltip_controller.js @@ -4,11 +4,11 @@ import { flip, shift, offset, - arrow + autoUpdate } from '@floating-ui/dom'; export default class extends Controller { - static targets = ["arrow", "tooltip"]; + static targets = ["tooltip"]; static values = { placement: { type: String, default: "top" }, offset: { type: Number, default: 10 }, @@ -17,58 +17,67 @@ export default class extends Controller { }; connect() { - this.element.addEventListener("mouseenter", this.showTooltip); - this.element.addEventListener("mouseleave", this.hideTooltip); - this.element.addEventListener("focus", this.showTooltip); - this.element.addEventListener("blur", this.hideTooltip); - }; - - showTooltip = () => { - this.tooltipTarget.style.display = 'block'; - this.#update(); - }; - - hideTooltip = () => { - this.tooltipTarget.style.display = ''; - }; + this._cleanup = null; + this.boundUpdate = this.update.bind(this); + this.startAutoUpdate(); + this.addEventListeners(); + } disconnect() { - this.element.removeEventListener("mouseenter", this.showTooltip); - this.element.removeEventListener("mouseleave", this.hideTooltip); - this.element.removeEventListener("focus", this.showTooltip); - this.element.removeEventListener("blur", this.hideTooltip); - }; + this.removeEventListeners(); + this.stopAutoUpdate(); + } - #update() { + addEventListeners() { + this.element.addEventListener("mouseenter", this.show); + this.element.addEventListener("mouseleave", this.hide); + } + + removeEventListeners() { + this.element.removeEventListener("mouseenter", this.show); + this.element.removeEventListener("mouseleave", this.hide); + } + + show = () => { + this.tooltipTarget.style.display = 'block'; + this.update(); // Ensure immediate update when shown + } + + hide = () => { + this.tooltipTarget.style.display = 'none'; + } + + startAutoUpdate() { + if (!this._cleanup) { + this._cleanup = autoUpdate( + this.element, + this.tooltipTarget, + this.boundUpdate + ); + } + } + + stopAutoUpdate() { + if (this._cleanup) { + this._cleanup(); + this._cleanup = null; + } + } + + update() { + // Update position even if not visible, to ensure correct positioning when shown computePosition(this.element, this.tooltipTarget, { placement: this.placementValue, middleware: [ offset({ mainAxis: this.offsetValue, crossAxis: this.crossAxisValue, alignmentAxis: this.alignmentAxisValue }), flip(), - shift({ padding: 5 }), - arrow({ element: this.arrowTarget }), + shift({ padding: 5 }) ], }).then(({ x, y, placement, middlewareData }) => { Object.assign(this.tooltipTarget.style, { left: `${x}px`, top: `${y}px`, }); - - const { x: arrowX, y: arrowY } = middlewareData.arrow; - const staticSide = { - top: 'bottom', - right: 'left', - bottom: 'top', - left: 'right', - }[placement.split('-')[0]]; - - Object.assign(this.arrowTarget.style, { - left: arrowX != null ? `${arrowX}px` : '', - top: arrowY != null ? `${arrowY}px` : '', - right: '', - bottom: '', - [staticSide]: '-4px', - }); }); - }; -} + } +} \ No newline at end of file diff --git a/app/models/account.rb b/app/models/account.rb index 9490f0f3..9f17f756 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -75,9 +75,11 @@ class Account < ApplicationRecord end end - def alert - latest_sync = syncs.latest - [ latest_sync&.error, *latest_sync&.warnings ].compact.first + def owns_ticker?(ticker) + security_id = Security.find_by(ticker: ticker)&.id + entries.account_trades + .joins("JOIN account_trades ON account_entries.entryable_id = account_trades.id") + .where(account_trades: { security_id: security_id }).any? end def favorable_direction diff --git a/app/models/account/sync.rb b/app/models/account/sync.rb index adc26843..28ae251a 100644 --- a/app/models/account/sync.rb +++ b/app/models/account/sync.rb @@ -25,6 +25,8 @@ class Account::Sync < ApplicationRecord rescue StandardError => error account.observe_unknown_issue(error) fail! error + + raise error if Rails.env.development? end private diff --git a/app/models/issue/prices_missing.rb b/app/models/issue/prices_missing.rb index 823c1d19..6cb014af 100644 --- a/app/models/issue/prices_missing.rb +++ b/app/models/issue/prices_missing.rb @@ -1,6 +1,8 @@ class Issue::PricesMissing < Issue store_accessor :data, :missing_prices + after_initialize :initialize_missing_prices + validates :missing_prices, presence: true def append_missing_price(ticker, date) @@ -10,7 +12,10 @@ class Issue::PricesMissing < Issue def stale? stale = true + missing_prices.each do |ticker, dates| + next unless issuable.owns_ticker?(ticker) + oldest_date = dates.min expected_price_count = (oldest_date..Date.current).count prices = Security::Price.find_prices(ticker: ticker, start_date: oldest_date) @@ -19,4 +24,10 @@ class Issue::PricesMissing < Issue stale end + + private + + def initialize_missing_prices + self.missing_prices ||= {} + end end diff --git a/app/views/account/holdings/_holding.html.erb b/app/views/account/holdings/_holding.html.erb index fc4db7f5..83f84254 100644 --- a/app/views/account/holdings/_holding.html.erb +++ b/app/views/account/holdings/_holding.html.erb @@ -4,9 +4,13 @@
The Synth data provider could not find the requested data.
+ +We are actively developing Synth to be a low cost and easy to use data provider. You can help us improve Synth by + requesting the data you need.
+ +Please post in our <%= link_to "Discord server", "https://link.maybe.co/discord", target: "_blank" %> with the + following information:
+ +The Synth data provider could not find the requested data.
- -We are actively developing Synth to be a low cost and easy to use data provider. You can help us improve Synth by - requesting the data you need.
- -Please post in our <%= link_to "Discord server", "https://link.maybe.co/discord", target: "_blank" %> with the - following information:
- -Some stock prices are missing for this account.
+ +<%= JSON.pretty_generate(@issue.data) %>
+<% end %>
+
+<%= content_for :action do %>
+ <%= render "issue/request_synth_data_action" %>
+<% end %>
diff --git a/app/views/issues/_issue.html.erb b/app/views/issues/_issue.html.erb
index cc80fbb6..cabb360c 100644
--- a/app/views/issues/_issue.html.erb
+++ b/app/views/issues/_issue.html.erb
@@ -1,14 +1,15 @@
<%# locals: (issue:) %>
<% priority_class = issue.critical? || issue.error? ? "bg-error/5" : "bg-warning/5" %>
+<% text_class = issue.critical? || issue.error? ? "text-error" : "text-warning" %>
<%= tag.div class: "flex gap-6 items-center rounded-xl px-4 py-3 #{priority_class}" do %>
- <%= issue.title %>