Merge branch 'main' into delete-account-improvements

This commit is contained in:
Alexander Zaytsev
2026-03-19 15:26:31 +01:00
331 changed files with 8082 additions and 2471 deletions
@@ -0,0 +1,6 @@
json.(@export, :id, :status)
json.created_at @export.created_at.utc
if @export.completed? && @export.file.attached?
json.download_url rails_blob_url(@export.file, disposition: "attachment")
end
@@ -0,0 +1,3 @@
json.(@join_code, :code, :usage_count, :usage_limit)
json.url join_url(code: @join_code.code, script_name: Current.account.slug)
json.active !!@join_code.active?
-2
View File
@@ -21,5 +21,3 @@
<%= render "account/settings/cancellation" %>
</div>
</div>
<%= render "account/settings/subscription_panel" if Fizzy.saas? %>
@@ -0,0 +1,3 @@
json.(@account, :id, :name, :cards_count)
json.created_at @account.created_at.utc
json.auto_postpone_period_in_days @account.entropy.auto_postpone_period_in_days
+1
View File
@@ -1,6 +1,7 @@
json.cache! board do
json.(board, :id, :name, :all_access)
json.created_at board.created_at.utc
json.auto_postpone_period_in_days board.auto_postpone_period_in_days
json.url board_url(board)
json.creator board.creator, partial: "users/user", as: :user
@@ -0,0 +1 @@
json.array! @page.records, partial: "cards/card", as: :card
@@ -0,0 +1 @@
json.array! @page.records, partial: "cards/card", as: :card
@@ -0,0 +1 @@
json.array! @page.records, partial: "cards/card", as: :card
+1 -1
View File
@@ -12,7 +12,7 @@
</div>
<div class="cards__list hide-scrollbar" data-drag-drop-item-container>
<% if page.used? %>
<%= with_automatic_pagination "maybe", @page do %>
<%= with_automatic_pagination "maybe", page do %>
<%= render "cards/display/previews", cards: page.records, draggable: true %>
<% end %>
<% end %>
+5 -1
View File
@@ -1,7 +1,11 @@
<%= messages_tag(card) do %>
<% if card.published? %>
<%= render partial: "cards/comments/comment", collection: card.comments.preloaded.chronologically, cached: true %>
<%= render "cards/comments/new", card: card %>
<% if Fizzy.saas? %>
<%= render "cards/comments/saas/new", card: card %>
<% else %>
<%= render "cards/comments/new", card: card %>
<% end %>
<%= render "cards/comments/watchers", card: card %>
<% end %>
@@ -3,7 +3,7 @@
<% if @source_column %>
<%= turbo_stream.replace(dom_id(@source_column), partial: "boards/show/column", method: :morph, locals: { column: @source_column }) %>
<% elsif @was_in_stream %>
<%= turbo_stream.replace("the-stream", partial: "boards/show/stream", method: :morph, locals: { board: @card.board, page: @page }) %>
<%= turbo_stream.replace("maybe", partial: "boards/show/stream", method: :morph, locals: { board: @card.board, page: @page }) %>
<% end %>
<%= turbo_stream.replace([ @card, :card_container ], partial: "cards/container", method: :morph, locals: { card: @card.reload }) %>
@@ -3,7 +3,7 @@
<% if @card.column %>
<%= turbo_stream.replace(dom_id(@card.column), partial: "boards/show/column", method: :morph, locals: { column: @card.column }) %>
<% elsif @card.awaiting_triage? %>
<%= turbo_stream.replace("the-stream", partial: "boards/show/stream", method: :morph, locals: { board: @card.board, page: @page }) %>
<%= turbo_stream.replace("maybe", partial: "boards/show/stream", method: :morph, locals: { board: @card.board, page: @page }) %>
<% end %>
<%= turbo_stream.replace([ @card, :card_container ], partial: "cards/container", method: :morph, locals: { card: @card.reload }) %>
@@ -1,13 +1,27 @@
<div class="flex gap-half">
<%= link_to edit_card_path(card), class: "btn btn--circle-mobile borderless",
data: { controller: "hotkey", action: "keydown.e@document->hotkey#click", bridge__overflow_menu_target: "item", bridge_title: "Edit", turbo_frame: dom_id(card, :edit) } do %>
data: {
controller: "hotkey",
action: "keydown.e@document->hotkey#click",
bridge__overflow_menu_target: "item",
bridge_title: "Edit",
turbo_frame: dom_id(card, :edit)
} do %>
<%= icon_tag "pencil", class: "icon--mobile-only" %>
<span>Edit</span>
<kbd class="txt-x-small hide-on-touch">e</kbd>
<% end %>
<%= button_to card_closure_path(card), class: "btn btn--circle-mobile borderless",
data: { controller: "hotkey", form_target: "submit", bridge__buttons_target: "button", bridge_title: "Mark done", bridge_display_as_primary_action: true, bridge_display_title: true, bridge_icon_url: bridge_icon("check"), action: "keydown.d@document->hotkey#click" },
<%= button_to card_closure_path(card), class: "btn btn--circle-mobile borderless hide-on-native",
data: {
controller: "hotkey",
form_target: "submit",
bridge__buttons_target: ("button" unless card.postponed?),
bridge_title: "Mark done",
bridge_display_as_primary_action: true,
bridge_display_title: true, bridge_icon_url: bridge_icon("check"),
action: "keydown.d@document->hotkey#click"
},
form: { data: { controller: "form" } } do %>
<%= icon_tag "check", class: "icon--mobile-only" %>
<span class="overflow-ellipsis">Mark as Done</span>
@@ -14,6 +14,6 @@
<% end %>
</div>
<%= render "cards/container/footer/saas/near_notice" if Fizzy.saas? %>
<%= render "cards/container/footer/saas/storage_limit_notice" if Fizzy.saas? %>
</div>
@@ -12,8 +12,8 @@
</span>
</button>
<% if Current.user && !local_assigns[:preview] %>
<%= button_to "Assign to me", card_assignments_path(card, params: { assignee_id: Current.user.id }), method: :post, data: {controller: "hotkey", action: "keydown.m@document->hotkey#click" }, hidden: true %>
<% unless local_assigns[:preview] %>
<%= button_to "Assign to me", card_self_assignment_path(card), method: :post, data: {controller: "hotkey", action: "keydown.m@document->hotkey#click" }, hidden: true %>
<% end %>
<dialog class="popup panel flex-column align-start gap-half fill-white shadow" data-dialog-target="dialog" data-action="turbo:before-morph-attribute->dialog#preventCloseOnMorphing turbo:submit-end->dialog#close">
+14 -4
View File
@@ -1,15 +1,25 @@
<% if card.postponed? %>
<div class="card__closed <%= "card__closed--system" if card.postponed_by&.system? %>">
<%= tag.div class: token_list("card__closed", "card__closed--system": card.postponed_by&.system?), data: {
controller: "bridge--stamp",
bridge__stamp_scope_selector_value: ".card-perma",
bridge_title: "Not Now",
bridge_description: card.postponed_at.strftime("%b %d, %Y")
} do %>
<span class="card__closed-title" data-text="Not Now">Not Now</span>
<strong class="card__closed-date"><%= card.postponed_at.strftime("%b %d, %Y") %></strong>
<span class="card__closed-by-line">by <span class="card__closed-by"><%= card.postponed_by&.familiar_name %></span></span>
</div>
<% end %>
<% end %>
<% if card.closed? %>
<div class="card__closed">
<%= tag.div class: "card__closed", data: {
controller: "bridge--stamp",
bridge__stamp_scope_selector_value: ".card-perma",
bridge_title: "Done",
bridge_description: card.closed_at.strftime("%b %d, %Y")
} do %>
<span class="card__closed-title" data-text="Done">Done</span>
<strong class="card__closed-date"><%= card.closed_at.strftime("%b %d, %Y") %></strong>
<span class="card__closed-by-line">by <span class="card__closed-by"><%= card.closed_by.familiar_name %></span></span>
</div>
<% end %>
<% end %>
@@ -3,7 +3,7 @@
<% if @source_column %>
<%= turbo_stream.replace(dom_id(@source_column), partial: "boards/show/column", method: :morph, locals: { column: @source_column }) %>
<% elsif @was_in_stream %>
<%= turbo_stream.replace("the-stream", partial: "boards/show/stream", method: :morph, locals: { board: @card.board, page: @page }) %>
<%= turbo_stream.replace("maybe", partial: "boards/show/stream", method: :morph, locals: { board: @card.board, page: @page }) %>
<% end %>
<%= turbo_stream.replace([ @card, :card_container ], partial: "cards/container", method: :morph, locals: { card: @card.reload }) %>
+1 -1
View File
@@ -7,7 +7,7 @@
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= link_back_to_board(@card.board) %>
<%= link_back_to_board @card.board, prefer_referrer: [ root_path, cards_path, board_path(@card.board) ] %>
</div>
<% end %>
@@ -0,0 +1 @@
json.array! @card.steps, partial: "cards/steps/step", as: :step
+3 -2
View File
@@ -6,8 +6,9 @@
<%= form_with model: model, url: url, data: { controller: "form" } do |form| %>
<%= render "entropy/knob",
form: form,
name: :auto_postpone_period,
knob_options: entropy_auto_close_options,
name: :auto_postpone_period_in_days,
current_value: model.auto_postpone_period_in_days,
knob_options: Entropy::AUTO_POSTPONE_PERIODS_IN_DAYS,
label: "Days until auto-close",
disabled: disabled %>
<% end %>
+2 -3
View File
@@ -1,6 +1,5 @@
<%
period = form.object.send(name)
current_index = knob_options.index(period.to_i / 1.day)
current_index = knob_options.index(current_value) || knob_options.index(Entropy::DEFAULT_AUTO_POSTPONE_PERIOD_IN_DAYS)
%>
<fieldset class="knob" data-controller="knob" data-knob-target="field" style="--knob-options: <%= knob_options.length %>; --knob-index: <%= current_index %>">
@@ -8,7 +7,7 @@
<% knob_options.each_with_index do |value, index| %>
<label class="knob__option" style="--i: <%= index %>">
<%= form.radio_button name,
value.days,
value,
data: {
action: "change->knob#optionChanged change->form#submit",
index: index,
+1 -1
View File
@@ -2,8 +2,8 @@
id: dom_id(event, "timelined"),
class: "event event--#{ event.action } #{ "golden-effect" if event.card.golden? } center-block flex flex-column full-width align-start justify-start position-relative",
style: "--card-color: #{ card.closed? ? "var(--color-card-complete)" : card.color };",
target: "_top",
data: {
turbo_frame: "_top",
related_element_target: "related",
related_element_group_value: card.id,
action: "mouseover->related-element#highlight mouseout->related-element#unhighlight" } do %>
@@ -1,7 +1,7 @@
<div class="header__actions header__actions--end">
<div>
<%= link_to new_board_path, class: "btn btn--link btn--circle-mobile",
data: { controller: "hotkey", action: "keydown.b@document->hotkey#click", bridge__buttons_target: "button", bridge_title: "Add board", bridge_display_title: true, bridge_icon_url: bridge_icon("board") } do %>
data: { controller: "hotkey", action: "keydown.b@document->hotkey#click", bridge__buttons_target: "button", bridge_title: "Add board", bridge_display_title: true, bridge_icon_url: bridge_icon("board"), bridge_slot: "left" } do %>
<%= icon_tag "board" %>
<span class="overflow-ellipsis">Add a board</span>
<kbd class="hide-on-touch">B</kbd>
+5 -1
View File
@@ -5,7 +5,11 @@
<body class="<%= @body_class %>"
data-controller="local-time timezone-cookie turbo-navigation theme bridge--title bridge--text-size bridge--insets"
data-action="turbo:morph@window->local-time#refreshAll turbo:before-visit@document->turbo-navigation#rememberLocation"
data-platform="<%= platform.type %>" data-bridge--title-title-value="<%= @page_title %>">
data-turbo-navigation-label-value="<%= @page_title %>"
data-platform="<%= platform.type %>"
data-bridge-platform="<%= platform.bridge_name %>"
data-bridge-components="<%= platform.bridge_components %>"
data-bridge--title-title-value="<%= @page_title %>">
<div id="global-container" data-controller="bridge--buttons bridge--overflow-menu">
<header class="header header--mobile-actions-stack <%= @header_class %>" id="header">
<a href="#main" class="header__skip-navigation btn" data-turbo="false">Skip to main content</a>
-1
View File
@@ -17,7 +17,6 @@
<%= render "layouts/theme_preference" %>
<%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "fizzy/saas", "data-turbo-track": "reload" if Fizzy.saas? %>
<%= javascript_importmap_tags %>
<%= tenanted_action_cable_meta_tag %>
@@ -11,6 +11,10 @@
<p>This code will work for <%= distance_of_time_in_words(MagicLink::EXPIRATION_TIME) %>.</p>
<% if account = @magic_link.identity.accounts.last %>
<p>P.S. You can make your account more secure and sign-in faster with a <%= link_to "Passkey", my_passkeys_url(script_name: account.slug) %></p>
<% end %>
<p class="footer">Need help? <%= mail_to "support@fizzy.do", "Send us an email"%>.
</p>
@@ -7,3 +7,7 @@ Please enter this 6-character verification code to finish creating your account:
<%= @magic_link.code %>
This code will work for <%= distance_of_time_in_words(MagicLink::EXPIRATION_TIME) %>.
<% if account = @magic_link.identity.accounts.last %>
P.S. If you want to sign-in faster, and make your account more secure, add a passkey: <%= my_passkeys_url(script_name: account.slug) %>
<% end %>
@@ -0,0 +1,2 @@
json.(access_token, :id, :description, :permission)
json.created_at access_token.created_at.utc
+1 -1
View File
@@ -11,7 +11,7 @@
<section class="panel panel--wide shadow center webhooks">
<% if @access_tokens.any? %>
<p class="margin-none-block-start">Tokens you have generated that can be used to access the Fizzy API.</p>
<table class="access_tokens_table margin-block-end-double max-width txt-small">
<table class="access-tokens margin-block-end-double max-width txt-small">
<thead>
<tr>
<th>Description</th>
@@ -0,0 +1 @@
json.array! @access_tokens, partial: "my/access_tokens/access_token", as: :access_token
+1 -1
View File
@@ -6,7 +6,7 @@
<%= tag.li class: "popup__item", data: { filter_target: "item", navigable_list_target: "item" } do %>
<%= icon_tag "logout", class: "popup__icon" %>
<%= button_to session_path(script_name: nil), method: :delete, class: "popup__btn btn", data: { turbo: false } do %>
<%= button_to session_path(script_name: nil), method: :delete, class: "popup__btn btn", form: { data: { turbo: false, controller: "clear-offline-cache", action: "submit->clear-offline-cache#clearCache" } } do %>
<span>Sign out</span>
<% end %>
<% end %>
+13
View File
@@ -0,0 +1,13 @@
<li class="credential" style="view-transition-name: <%= dom_id(passkey) %>">
<%= link_to edit_my_passkey_path(passkey), class: "credential__link" do %>
<% if icon = passkey.authenticator&.icon %>
<%= image_tag icon[:light], size: 24, class: "flex-item-no-shrink hide-on-dark-mode", aria: { hidden: true } %>
<%= image_tag icon[:dark], size: 24, class: "flex-item-no-shrink hide-on-light-mode", aria: { hidden: true } %>
<% else %>
<%= image_tag "passkeys/generic_light.svg", size: 24, class: "flex-item-no-shrink hide-on-dark-mode", aria: { hidden: true } %>
<%= image_tag "passkeys/generic_dark.svg", size: 24, class: "flex-item-no-shrink hide-on-light-mode", aria: { hidden: true } %>
<% end %>
<strong class="overflow-ellipsis min-width"><%= passkey.name.presence || "Passkey" %></strong>
<strong class="credential__arrow">→</strong>
<% end %>
</li>
+33
View File
@@ -0,0 +1,33 @@
<% @page_title = "Edit Passkey" %>
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= back_link_to "Passkeys", my_passkeys_path, "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
</div>
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
<% end %>
<section class="panel panel--wide shadow center txt-align-start flex flex-column gap">
<% if params[:created] %>
<p class="margin-none">Your passkey has been registered. Give it a name so you can identify it later.</p>
<% end %>
<%= form_with model: @passkey, scope: :passkey, url: my_passkey_path(@passkey), html: { class: "flex flex-column gap" } do |form| %>
<div class="flex flex-column gap-half">
<strong><%= form.label "Name your passkey" %></strong>
<%= form.text_field :name, autofocus: true, class: "input", placeholder: "e.g. MacBook Pro, iPhone", data: { "1p-ignore": "" }, autocomplete: "off" %>
</div>
<%= form.submit "Save", class: "btn btn--link center" %>
<% end %>
<div class="txt-align-center">
<%= button_to my_passkey_path(@passkey), method: :delete,
class: "btn txt-negative borderless txt-small",
data: { turbo_confirm: "Are you sure you want to remove this passkey?" } do %>
<%= icon_tag "trash" %>
<span>Remove this passkey</span>
<% end %>
</div>
</section>
+43
View File
@@ -0,0 +1,43 @@
<% @page_title = "Passkeys" %>
<% content_for :head do %>
<%= passkey_creation_options_meta_tag(@creation_options) %>
<% end %>
<% content_for :header do %>
<div class="header__actions header__actions--start">
<%= back_link_to "My profile", user_path(Current.user), "keydown.left@document->hotkey#click keydown.esc@document->hotkey#click" %>
</div>
<h1 class="header__title" data-bridge--title-target="header"><%= @page_title %></h1>
<% end %>
<section class="panel panel--wide shadow center" data-passkey-errors>
<p class="font-weight-medium margin-none-block-start">Passkeys let you sign in securely without a password or email code.</p>
<% if @passkeys.any? %>
<ul class="margin-none-block-start margin-block-end-double unpad">
<%= render partial: "my/passkeys/passkey", collection: @passkeys %>
</ul>
<% end %>
<footer>
<%= passkey_creation_button my_passkeys_path, class: "btn btn--link center txt-medium" do %>
<%= icon_tag "add" %>
<span>Register a passkey</span>
<% end %>
<p class="txt-small txt-subtle txt-balance margin-none-block-end">
Your browser will prompt you to create a passkey using your device's biometrics, PIN, or security key
</p>
<p data-passkey-error="error" class="txt-negative">
Something went wrong while registering your passkey.
</p>
<p data-passkey-error="cancelled" class="txt-subtle">
Passkey registration was cancelled.
Try again when you are ready.
</p>
</footer>
</section>
@@ -0,0 +1 @@
json.bundle_email_frequency Current.user.settings.bundle_email_frequency
-41
View File
@@ -1,41 +0,0 @@
self.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET') return
if (event.request.destination === 'document') {
event.respondWith(
fetch(event.request, { cache: 'no-cache' })
.catch(() => caches.match(event.request))
)
}
})
self.addEventListener("push", (event) => {
const data = event.data.json()
event.waitUntil(Promise.all([ showNotification(data), updateBadgeCount(data.options) ]))
})
async function showNotification({ title, options }) {
return self.registration.showNotification(title, options)
}
async function updateBadgeCount({ data: { badge } }) {
return self.navigator.setAppBadge?.(badge || 0)
}
self.addEventListener("notificationclick", (event) => {
event.notification.close()
const url = new URL(event.notification.data.url, self.location.origin).href
event.waitUntil(openURL(url))
})
async function openURL(url) {
const clients = await self.clients.matchAll({ type: "window" })
const focused = clients.find((client) => client.focused)
if (focused) {
await focused.navigate(url)
} else {
await self.clients.openWindow(url)
}
}
+97
View File
@@ -0,0 +1,97 @@
importScripts("<%= javascript_url("turbo-offline-umd.min") %>")
// Documents - top-level navigation requests only
// We need `cache: "no-cache"` here to work around
// an annoying Safari PWA bug that predates offline mode
TurboOffline.addRule({
match: (request) => request.destination === "document",
except: /\/(edit|pin|watch|new)$/,
handler: TurboOffline.handlers.networkFirst({
cacheName: "main",
maxAge: 60 * 60 * 24 * 3,
networkTimeout: 3,
fetchOptions: { cache: "no-cache" },
maxEntrySize: 1024 * 1024
})
})
TurboOffline.addRule({
match: /\/assets\/.+[-\.][0-9a-f]+\.(js|css|svg|png|jpg|webp|woff2?|ico)$/,
handler: TurboOffline.handlers.cacheFirst({
cacheName: "assets",
maxAge: 60 * 60 * 24 * 7,
maxEntrySize: 1024 * 1024
})
})
TurboOffline.addRule({
match: /\/(boards|cards|users)\//,
except: /\/(edit|pin|watch|new)$/,
handler: TurboOffline.handlers.networkFirst({
cacheName: "main",
maxAge: 60 * 60 * 24 * 3,
networkTimeout: 2,
maxEntrySize: 1024 * 1024
})
})
TurboOffline.addRule({
match: /\/rails\/active_storage\//,
handler: TurboOffline.handlers.networkFirst({
cacheName: "storage",
maxAge: 60 * 60 * 24 * 7,
networkTimeout: 2,
maxEntrySize: 2 * 1024 * 1024, // 2MB covers about 95% of all Fizzy blobs
maxEntries: 500,
fetchOptions: { mode: "cors" }
})
})
// Everything else
TurboOffline.addRule({
except: /\/(service-worker\.js|edit|pin|watch|new)$/,
handler: TurboOffline.handlers.networkFirst({
cacheName: "main",
maxAge: 60 * 60 * 24,
networkTimeout: 3,
maxEntrySize: 1024 * 1024
})
})
self.addEventListener("activate", (event) => {
event.waitUntil(self.clients.claim())
})
TurboOffline.start()
self.addEventListener("push", (event) => {
const data = event.data.json()
event.waitUntil(Promise.all([ showNotification(data), updateBadgeCount(data.options) ]))
})
async function showNotification({ title, options }) {
return self.registration.showNotification(title, options)
}
async function updateBadgeCount({ data: { badge } }) {
return self.navigator.setAppBadge?.(badge || 0)
}
self.addEventListener("notificationclick", (event) => {
event.notification.close()
const url = new URL(event.notification.data.url, self.location.origin).href
event.waitUntil(openURL(url))
})
async function openURL(url) {
const clients = await self.clients.matchAll({ type: "window" })
const focused = clients.find((client) => client.focused)
if (focused) {
await focused.navigate(url)
} else {
await self.clients.openWindow(url)
}
}
+1
View File
@@ -0,0 +1 @@
json.partial! "reactions/reaction", reaction: @reaction
+1
View File
@@ -0,0 +1 @@
json.array! @page.records, partial: "cards/card", as: :card
+1 -1
View File
@@ -8,7 +8,7 @@
</p>
</header>
<%= form_with url: session_magic_link_path, method: :post, html: { data: { controller: "magic-link" } } do |form| %>
<%= form_with url: session_magic_link_path, method: :post, html: { data: { controller: "magic-link clear-offline-cache", action: "submit->clear-offline-cache#clearCache" } } do |form| %>
<%= form.text_field :code, required: true, class: "input center txt-align-enter txt-large txt-uppercase",
autofocus: true, autocorrect: "off", autocapitalize: "off", spellcheck: "false", "data-1p-ignore": true,
autocomplete: "one-time-code", maxlength: "6", placeholder: "••••••", value: params[:code],
+11 -4
View File
@@ -1,26 +1,33 @@
<% @page_title = "Enter your email" %>
<% content_for :head do %>
<%= passkey_request_options_meta_tag(@request_options) %>
<% end %>
<div class="panel panel--centered flex flex-column gap-half">
<h1 class="txt-x-large font-weight-black margin-block-end">Get into Fizzy</h1>
<%= form_with url: session_path, class: "flex flex-column gap-half txt-medium" do |form| %>
<div class="flex align-center gap">
<label class="flex align-center gap input input--actor">
<%= form.email_field :email_address, required: true, class: "input txt-large full-width", autofocus: true, autocomplete: "username", placeholder: "Enter your email address…" %>
<%= form.email_field :email_address, required: true, class: "input txt-large full-width", autofocus: true, autocomplete: "username webauthn", placeholder: "Enter your email address…" %>
</label>
</div>
<% if Account.accepting_signups? %>
<p><strong>New here?</strong> <%= link_to "Sign up", new_signup_path %> to create an account. <strong>Already have an account?</strong> Enter your email and well get you signed in.</p>
<p><strong>New here?</strong> <%= link_to "Sign up", new_signup_path %> to create an account. <strong>Already have an account?</strong> Enter your email and we'll get you signed in.</p>
<% else %>
<p>Enter your email and well get you signed in.</p>
<p>Enter your email and we'll get you signed in.</p>
<% end %>
<button type="submit" id="log_in" class="btn btn--link center txt-medium">
<span>Lets go</span>
<span>Let's go</span>
<%= icon_tag "arrow-right" %>
</button>
<% end %>
<%= passkey_sign_in_button "Sign in with a passkey", session_passkey_path, mediation: "conditional", class: "btn btn--link center txt-medium", hidden: true %>
</div>
<% content_for :footer do %>
+6 -1
View File
@@ -41,7 +41,12 @@
<% if Current.user == @user %>
<hr class="separator--horizontal full-width flex-item-grow margin-block-start-double" style="--border-color: var(--color-ink-light);" aria-hidden="true">
<%= button_to session_path(script_name: nil), method: :delete, class: "btn txt-x-small center", data: { turbo: false } do %>
<%= link_to my_passkeys_path, class: "btn txt-x-small center" do %>
<%= icon_tag "authentication" %>
<span>Manage passkeys</span>
<% end %>
<%= button_to session_path(script_name: nil), method: :delete, class: "btn txt-x-small center", form: { data: { turbo: false, controller: "clear-offline-cache", action: "submit->clear-offline-cache#clearCache" } } do %>
<span>Sign out of Fizzy on this device</span>
<% end %>
<% end %>
@@ -0,0 +1,8 @@
json.cache! [ webhook, webhook.board ] do
json.(webhook, :id, :name, :active, :signing_secret, :subscribed_actions)
json.payload_url webhook.url
json.created_at webhook.created_at.utc
json.url board_webhook_url(webhook.board, webhook)
json.board webhook.board, partial: "boards/board", as: :board
end
+1
View File
@@ -0,0 +1 @@
json.array! @page.records, partial: "webhooks/webhook", as: :webhook
+1
View File
@@ -0,0 +1 @@
json.partial! "webhooks/webhook", webhook: @webhook