Add reanalyze button

Add a reanalyze button for individual recordings in the analysis dropdown

As part of this, split out ApiRequestButton so that state transitions
(clickable -> loading/disabled -> done) can be shared across start/stop
recording and this new button. Other buttons might benefit from this as
well.

Also fix a broken checkbox while we're here.
This commit is contained in:
Markus Unterwaditzer
2025-08-02 20:15:01 +02:00
committed by Cooper Quintin
parent fe6afac817
commit 5c03f6ea03
12 changed files with 249 additions and 106 deletions
@@ -0,0 +1,91 @@
<script lang="ts">
import { req } from '$lib/utils.svelte';
let {
url,
method = 'POST',
label,
loadingLabel,
disabled = false,
variant = 'blue',
icon,
onclick,
ariaLabel,
}: {
url: string;
method?: string;
label: string;
loadingLabel?: string;
disabled?: boolean;
variant?: 'blue' | 'red' | 'green';
icon?: any; // Svelte snippet
onclick?: () => void | Promise<void>;
ariaLabel?: string;
} = $props();
let is_requesting = $state(false);
let is_disabled = $derived(disabled || is_requesting);
const variantClasses = {
blue: {
enabled: 'bg-blue-500 hover:bg-blue-700',
disabled: 'bg-blue-500 opacity-50 cursor-not-allowed',
},
red: {
enabled: 'bg-red-500 hover:bg-red-700',
disabled: 'bg-red-500 opacity-50 cursor-not-allowed',
},
green: {
enabled: 'bg-green-500 hover:bg-green-700',
disabled: 'bg-green-500 opacity-50 cursor-not-allowed',
},
};
async function handleClick() {
if (is_disabled) return;
is_requesting = true;
try {
await req(method, url);
if (onclick) {
await onclick();
}
} catch (err) {
console.error(`Failed to ${method} ${url}:`, err);
alert(`Request failed. Please try again.`);
} finally {
is_requesting = false;
}
}
let buttonClasses = $derived(
is_disabled ? variantClasses[variant].disabled : variantClasses[variant].enabled
);
</script>
<button
class="text-white font-bold py-2 px-2 sm:px-4 rounded-md flex flex-row items-center gap-1 {buttonClasses}"
onclick={handleClick}
disabled={is_disabled}
aria-label={ariaLabel || label}
>
<span>{is_requesting && loadingLabel ? loadingLabel : label}</span>
{#if is_requesting}
<svg
class="w-4 h-4 text-white animate-spin"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="m4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
{:else if icon}
{@render icon()}
{/if}
</button>