diff --git a/development_status.md b/development_status.md index 1f2fb65..a5ecf4f 100644 --- a/development_status.md +++ b/development_status.md @@ -1,6 +1,6 @@ # CertRemind 開発進捗 -最終更新: 2026-05-25 +最終更新: 2026-05-27 ## 現在の実装状況 @@ -244,7 +244,7 @@ pnpm monitor:worker - 確認ダイアログ付き履歴削除 - 通知方法管理画面 - Webhook 登録 - - Webhook 編集 + - モーダルでの Webhook 編集 - 確認ダイアログ付き Webhook 削除 - ブラウザ Push 通知の許可状態表示 - 現在のブラウザの Push 登録状態表示 diff --git a/src/client/routes/NotificationMethodsView.jsx b/src/client/routes/NotificationMethodsView.jsx index 4285601..c4a2437 100644 --- a/src/client/routes/NotificationMethodsView.jsx +++ b/src/client/routes/NotificationMethodsView.jsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useState } from 'react'; +import * as Dialog from '@radix-ui/react-dialog'; import { ArrowLeft, BellOff, BellRing, Link, Pencil, Plus, Trash2 } from 'lucide-react'; import { request } from '../api/client.js'; import { ConfirmDialog } from '../components/ConfirmDialog.jsx'; @@ -56,7 +57,9 @@ export function NotificationMethodsView({ onBack }) { const [vapidPublicKey, setVapidPublicKey] = useState(''); const [currentPushStatus, setCurrentPushStatus] = useState('unchecked'); const [form, setForm] = useState({ alias: '', url: '' }); - const [editingId, setEditingId] = useState(''); + const [editingWebhook, setEditingWebhook] = useState(null); + const [editForm, setEditForm] = useState({ alias: '', url: '' }); + const [editDialogOpen, setEditDialogOpen] = useState(false); const [permission, setPermission] = useState( typeof Notification === 'undefined' ? 'unsupported' : Notification.permission, ); @@ -121,19 +124,12 @@ export function NotificationMethodsView({ onBack }) { setBusy(true); try { validateWebhookForm(form); - const endpoint = editingId - ? `/api/notification-methods/webhooks/${editingId}` - : '/api/notification-methods/webhooks'; - await request(endpoint, { - method: editingId ? 'PATCH' : 'POST', + await request('/api/notification-methods/webhooks', { + method: 'POST', body: JSON.stringify({ alias: form.alias.trim(), url: form.url.trim() }), }); setForm({ alias: '', url: '' }); - setEditingId(''); - showToast({ - type: 'success', - message: editingId ? 'Webhookを更新しました' : 'Webhookを登録しました', - }); + showToast({ type: 'success', message: 'Webhookを登録しました' }); await loadMethods(); } catch (err) { showToast({ type: 'error', message: err.message }); @@ -143,8 +139,38 @@ export function NotificationMethodsView({ onBack }) { } function startEdit(webhook) { - setEditingId(webhook.notificationMethodId); - setForm({ alias: webhook.alias, url: webhook.url }); + setEditingWebhook(webhook); + setEditForm({ alias: webhook.alias, url: webhook.url }); + setEditDialogOpen(true); + } + + function handleEditDialogOpenChange(open) { + setEditDialogOpen(open); + if (!open) { + setEditingWebhook(null); + setEditForm({ alias: '', url: '' }); + } + } + + async function submitWebhookEdit(event) { + event.preventDefault(); + if (!editingWebhook) return; + + setBusy(true); + try { + validateWebhookForm(editForm); + await request(`/api/notification-methods/webhooks/${editingWebhook.notificationMethodId}`, { + method: 'PATCH', + body: JSON.stringify({ alias: editForm.alias.trim(), url: editForm.url.trim() }), + }); + showToast({ type: 'success', message: 'Webhookを更新しました' }); + handleEditDialogOpenChange(false); + await loadMethods(); + } catch (err) { + showToast({ type: 'error', message: err.message }); + } finally { + setBusy(false); + } } async function deleteWebhook(methodId) { @@ -273,11 +299,50 @@ export function NotificationMethodsView({ onBack }) { - {editingId ? '更新' : '登録'} + 登録 + + + + + Webhookを編集 + + + setEditForm({ ...editForm, alias: event.target.value })} + placeholder="Slack 通知" + maxLength="120" + required + /> + + + setEditForm({ ...editForm, url: event.target.value })} + placeholder="https://hooks.slack.com/services/..." + maxLength="2048" + required + /> + + + + + キャンセル + + + + 更新 + + + + + + + 登録済みWebhook