Fix clipboard copy and org settings navigation
- Replace phx:copy event with CopyToClipboard LiveView hook for more reliable clipboard functionality - Add visual feedback (checkmark) when text is copied to clipboard - Apply hook to both agent token and docker compose copy buttons - Add readonly attribute to docker compose textarea - Fix org settings page to stay on settings after save instead of redirecting to dashboard The clipboard copy now works correctly by using a dedicated LiveView hook that directly handles the click event and clipboard API, rather than relying on custom event dispatching which was causing issues.
This commit is contained in:
parent
eff7d0b55a
commit
f9a18fa423
3 changed files with 70 additions and 18 deletions
|
|
@ -284,7 +284,7 @@ if (!csrfToken) {
|
|||
const liveSocket = new LiveSocket("/live", Socket, {
|
||||
longPollFallbackMs: 2500,
|
||||
params: { _csrf_token: csrfToken },
|
||||
hooks: { ...colocatedHooks, SensorChart, WebAuthnRegister, WebAuthnLogin },
|
||||
hooks: { ...colocatedHooks, SensorChart, WebAuthnRegister, WebAuthnLogin, CopyToClipboard },
|
||||
})
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
|
|
@ -293,7 +293,9 @@ window.addEventListener("phx:page-loading-start", _info => topbar.show(300))
|
|||
window.addEventListener("phx:page-loading-stop", _info => topbar.hide())
|
||||
|
||||
// Handle clipboard copy events
|
||||
window.addEventListener("phx:copy", (event) => {
|
||||
window.addEventListener("phx:copy", (event: any) => {
|
||||
// When using JS.dispatch with 'to:', the event is dispatched to that element
|
||||
// and event.target will be that element
|
||||
const el = event.target as HTMLElement
|
||||
|
||||
let text = ""
|
||||
|
|
@ -304,14 +306,64 @@ window.addEventListener("phx:copy", (event) => {
|
|||
text = el.innerText || el.textContent || ""
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
// Optional: Show a success message or visual feedback
|
||||
console.log('Copied to clipboard')
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy text: ', err)
|
||||
})
|
||||
if (text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
console.log('Copied to clipboard:', text.substring(0, 50) + (text.length > 50 ? '...' : ''))
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy text:', err)
|
||||
})
|
||||
} else {
|
||||
console.warn('No text found to copy from element:', el)
|
||||
}
|
||||
})
|
||||
|
||||
// LiveView hook for copying to clipboard
|
||||
const CopyToClipboard = {
|
||||
mounted() {
|
||||
this.el.addEventListener("click", (e) => {
|
||||
e.preventDefault()
|
||||
const targetId = this.el.dataset.target
|
||||
if (!targetId) {
|
||||
console.error("No data-target attribute found on copy button")
|
||||
return
|
||||
}
|
||||
|
||||
const targetEl = document.querySelector(targetId) as HTMLInputElement | HTMLTextAreaElement | HTMLElement
|
||||
if (!targetEl) {
|
||||
console.error(`Target element not found: ${targetId}`)
|
||||
return
|
||||
}
|
||||
|
||||
let text = ""
|
||||
if (targetEl.tagName === "INPUT" || targetEl.tagName === "TEXTAREA") {
|
||||
text = (targetEl as HTMLInputElement | HTMLTextAreaElement).value
|
||||
} else {
|
||||
text = targetEl.innerText || targetEl.textContent || ""
|
||||
}
|
||||
|
||||
if (text) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
console.log('Copied to clipboard successfully')
|
||||
// Optional: Show visual feedback
|
||||
const originalText = this.el.innerHTML
|
||||
this.el.innerHTML = '<svg class="h-3 w-3 inline" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" /></svg> Copied!'
|
||||
setTimeout(() => {
|
||||
this.el.innerHTML = originalText
|
||||
}, 2000)
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy:', err)
|
||||
})
|
||||
} else {
|
||||
console.error('No text found to copy')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const Hooks = {
|
||||
CopyToClipboard
|
||||
}
|
||||
|
||||
// connect if there are any LiveViews on the page
|
||||
liveSocket.connect()
|
||||
|
||||
|
|
|
|||
|
|
@ -215,10 +215,9 @@
|
|||
/>
|
||||
<button
|
||||
type="button"
|
||||
phx-click={
|
||||
JS.dispatch("phx:copy", to: "#agent-token-input")
|
||||
|> JS.push("token_copied")
|
||||
}
|
||||
phx-hook="CopyToClipboard"
|
||||
data-target="#agent-token-input"
|
||||
id={"copy-token-#{@new_token.id}"}
|
||||
class="absolute right-2 top-1/2 -translate-y-1/2 px-3 py-1 text-xs font-medium text-zinc-700 dark:text-zinc-300 bg-zinc-100 dark:bg-zinc-800 border border-zinc-300 dark:border-zinc-700 rounded hover:bg-zinc-200 dark:hover:bg-zinc-700"
|
||||
>
|
||||
<.icon name="hero-clipboard" class="h-4 w-4" />
|
||||
|
|
@ -263,12 +262,14 @@
|
|||
- /var/run/docker.sock:/var/run/docker.sock</code></pre>
|
||||
<button
|
||||
type="button"
|
||||
phx-click={JS.dispatch("phx:copy", to: "#docker-compose-example")}
|
||||
class="mt-2 text-xs text-blue-600 dark:text-blue-400 hover:underline"
|
||||
phx-hook="CopyToClipboard"
|
||||
data-target="#docker-compose-example"
|
||||
id={"copy-docker-compose-#{@new_token.id}"}
|
||||
class="mt-2 text-xs text-blue-600 dark:text-blue-400 hover:underline flex items-center gap-1"
|
||||
>
|
||||
Copy to clipboard
|
||||
<.icon name="hero-clipboard" class="h-3 w-3" /> Copy to clipboard
|
||||
</button>
|
||||
<textarea id="docker-compose-example" class="sr-only">services:
|
||||
<textarea id="docker-compose-example" class="sr-only" readonly>services:
|
||||
towerops-agent:
|
||||
image: {@agent_image}
|
||||
container_name: towerops-agent
|
||||
|
|
|
|||
|
|
@ -40,8 +40,7 @@ defmodule ToweropsWeb.Org.SettingsLive do
|
|||
{:noreply,
|
||||
socket
|
||||
|> assign(:organization, organization)
|
||||
|> put_flash(:info, "Organization settings updated successfully")
|
||||
|> push_navigate(to: ~p"/orgs/#{organization.slug}")}
|
||||
|> put_flash(:info, "Organization settings updated successfully")}
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{:noreply, assign(socket, :form, to_form(changeset))}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue