mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-22 03:04:03 +00:00
fix: use Index instead of For loop for webhook header inputs (addresses #412)
Switched from For to Index loop which provides better stability for input elements in SolidJS. Also removed circular dependency between headerInputs and formData that was causing re-renders.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { createSignal, createEffect, Show, For } from 'solid-js';
|
||||
import { createSignal, createEffect, Show, For, Index } from 'solid-js';
|
||||
import { NotificationsAPI, Webhook } from '@/api/notifications';
|
||||
|
||||
interface WebhookTemplate {
|
||||
@@ -38,34 +38,6 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
// Track header inputs separately to avoid focus loss
|
||||
const [headerInputs, setHeaderInputs] = createSignal<Array<{id: string; key: string; value: string}>>([]);
|
||||
|
||||
// Sync headerInputs with formData.headers
|
||||
createEffect(() => {
|
||||
const headers = formData().headers || {};
|
||||
const currentInputs = headerInputs();
|
||||
|
||||
// Only update if we're starting fresh (not already editing headers)
|
||||
if (currentInputs.length === 0 && Object.keys(headers).length > 0) {
|
||||
setHeaderInputs(
|
||||
Object.entries(headers).map(([key, value], index) => ({
|
||||
id: `header-${Date.now()}-${index}`,
|
||||
key,
|
||||
value
|
||||
}))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Update formData.headers when headerInputs change
|
||||
const updateHeadersInFormData = () => {
|
||||
const headers: Record<string, string> = {};
|
||||
headerInputs().forEach(input => {
|
||||
if (input.key) {
|
||||
headers[input.key] = input.value;
|
||||
}
|
||||
});
|
||||
setFormData({ ...formData(), headers });
|
||||
};
|
||||
|
||||
// Load webhook templates
|
||||
createEffect(async () => {
|
||||
try {
|
||||
@@ -77,16 +49,22 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
});
|
||||
|
||||
const saveWebhook = () => {
|
||||
// First sync headers from inputs to formData
|
||||
updateHeadersInFormData();
|
||||
|
||||
const data = formData();
|
||||
if (!data.name || !data.url) return;
|
||||
|
||||
// Build headers from headerInputs
|
||||
const headers: Record<string, string> = {};
|
||||
headerInputs().forEach(input => {
|
||||
if (input.key) {
|
||||
headers[input.key] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
if (editingId()) {
|
||||
props.onUpdate({
|
||||
...data,
|
||||
id: editingId()!,
|
||||
headers,
|
||||
service: data.service,
|
||||
template: data.payloadTemplate
|
||||
});
|
||||
@@ -99,7 +77,7 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
name: data.name,
|
||||
url: data.url,
|
||||
method: data.method,
|
||||
headers: data.headers,
|
||||
headers,
|
||||
enabled: data.enabled,
|
||||
service: data.service,
|
||||
template: data.payloadTemplate
|
||||
@@ -431,29 +409,33 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
</span>
|
||||
</label>
|
||||
<div class="space-y-2">
|
||||
<For each={headerInputs()}>
|
||||
{(header) => (
|
||||
<Index each={headerInputs()}>
|
||||
{(header, index) => (
|
||||
<div class="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={header.key}
|
||||
value={header().key}
|
||||
onInput={(e) => {
|
||||
const newKey = e.currentTarget.value;
|
||||
setHeaderInputs(inputs =>
|
||||
inputs.map(h => h.id === header.id ? { ...h, key: newKey } : h)
|
||||
);
|
||||
setHeaderInputs(inputs => {
|
||||
const newInputs = [...inputs];
|
||||
newInputs[index] = { ...newInputs[index], key: newKey };
|
||||
return newInputs;
|
||||
});
|
||||
}}
|
||||
placeholder="Header Name"
|
||||
class="flex-1 px-3 py-2 text-sm border rounded-lg dark:bg-gray-700 dark:border-gray-600"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={header.value}
|
||||
value={header().value}
|
||||
onInput={(e) => {
|
||||
const newValue = e.currentTarget.value;
|
||||
setHeaderInputs(inputs =>
|
||||
inputs.map(h => h.id === header.id ? { ...h, value: newValue } : h)
|
||||
);
|
||||
setHeaderInputs(inputs => {
|
||||
const newInputs = [...inputs];
|
||||
newInputs[index] = { ...newInputs[index], value: newValue };
|
||||
return newInputs;
|
||||
});
|
||||
}}
|
||||
placeholder="Header Value"
|
||||
class="flex-1 px-3 py-2 text-sm border rounded-lg dark:bg-gray-700 dark:border-gray-600"
|
||||
@@ -461,7 +443,7 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setHeaderInputs(inputs => inputs.filter(h => h.id !== header.id));
|
||||
setHeaderInputs(inputs => inputs.filter((_, i) => i !== index));
|
||||
}}
|
||||
class="px-3 py-2 text-sm text-red-600 hover:text-red-700 dark:text-red-400 border border-red-300 dark:border-red-600 rounded-lg hover:bg-red-50 dark:hover:bg-red-900/20"
|
||||
>
|
||||
@@ -469,7 +451,7 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Index>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
@@ -513,9 +495,16 @@ export function WebhookConfig(props: WebhookConfigProps) {
|
||||
<button
|
||||
onClick={() => {
|
||||
// Test the webhook with current form data
|
||||
// Build headers from headerInputs
|
||||
const headers: Record<string, string> = {};
|
||||
headerInputs().forEach(input => {
|
||||
if (input.key) {
|
||||
headers[input.key] = input.value;
|
||||
}
|
||||
});
|
||||
// Use a consistent temporary ID for this form session
|
||||
const tempId = editingId() || 'temp-new-webhook';
|
||||
props.onTest(tempId, formData());
|
||||
props.onTest(tempId, { ...formData(), headers });
|
||||
}}
|
||||
disabled={props.testing === (editingId() || 'temp-new-webhook')}
|
||||
class="px-4 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||||
|
||||
Reference in New Issue
Block a user