Webhookの編集をモーダル化
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# CertRemind 開発進捗
|
# CertRemind 開発進捗
|
||||||
|
|
||||||
最終更新: 2026-05-25
|
最終更新: 2026-05-27
|
||||||
|
|
||||||
## 現在の実装状況
|
## 現在の実装状況
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ pnpm monitor:worker
|
|||||||
- 確認ダイアログ付き履歴削除
|
- 確認ダイアログ付き履歴削除
|
||||||
- 通知方法管理画面
|
- 通知方法管理画面
|
||||||
- Webhook 登録
|
- Webhook 登録
|
||||||
- Webhook 編集
|
- モーダルでの Webhook 編集
|
||||||
- 確認ダイアログ付き Webhook 削除
|
- 確認ダイアログ付き Webhook 削除
|
||||||
- ブラウザ Push 通知の許可状態表示
|
- ブラウザ Push 通知の許可状態表示
|
||||||
- 現在のブラウザの Push 登録状態表示
|
- 現在のブラウザの Push 登録状態表示
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
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 { ArrowLeft, BellOff, BellRing, Link, Pencil, Plus, Trash2 } from 'lucide-react';
|
||||||
import { request } from '../api/client.js';
|
import { request } from '../api/client.js';
|
||||||
import { ConfirmDialog } from '../components/ConfirmDialog.jsx';
|
import { ConfirmDialog } from '../components/ConfirmDialog.jsx';
|
||||||
@@ -56,7 +57,9 @@ export function NotificationMethodsView({ onBack }) {
|
|||||||
const [vapidPublicKey, setVapidPublicKey] = useState('');
|
const [vapidPublicKey, setVapidPublicKey] = useState('');
|
||||||
const [currentPushStatus, setCurrentPushStatus] = useState('unchecked');
|
const [currentPushStatus, setCurrentPushStatus] = useState('unchecked');
|
||||||
const [form, setForm] = useState({ alias: '', url: '' });
|
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(
|
const [permission, setPermission] = useState(
|
||||||
typeof Notification === 'undefined' ? 'unsupported' : Notification.permission,
|
typeof Notification === 'undefined' ? 'unsupported' : Notification.permission,
|
||||||
);
|
);
|
||||||
@@ -121,19 +124,12 @@ export function NotificationMethodsView({ onBack }) {
|
|||||||
setBusy(true);
|
setBusy(true);
|
||||||
try {
|
try {
|
||||||
validateWebhookForm(form);
|
validateWebhookForm(form);
|
||||||
const endpoint = editingId
|
await request('/api/notification-methods/webhooks', {
|
||||||
? `/api/notification-methods/webhooks/${editingId}`
|
method: 'POST',
|
||||||
: '/api/notification-methods/webhooks';
|
|
||||||
await request(endpoint, {
|
|
||||||
method: editingId ? 'PATCH' : 'POST',
|
|
||||||
body: JSON.stringify({ alias: form.alias.trim(), url: form.url.trim() }),
|
body: JSON.stringify({ alias: form.alias.trim(), url: form.url.trim() }),
|
||||||
});
|
});
|
||||||
setForm({ alias: '', url: '' });
|
setForm({ alias: '', url: '' });
|
||||||
setEditingId('');
|
showToast({ type: 'success', message: 'Webhookを登録しました' });
|
||||||
showToast({
|
|
||||||
type: 'success',
|
|
||||||
message: editingId ? 'Webhookを更新しました' : 'Webhookを登録しました',
|
|
||||||
});
|
|
||||||
await loadMethods();
|
await loadMethods();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showToast({ type: 'error', message: err.message });
|
showToast({ type: 'error', message: err.message });
|
||||||
@@ -143,8 +139,38 @@ export function NotificationMethodsView({ onBack }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startEdit(webhook) {
|
function startEdit(webhook) {
|
||||||
setEditingId(webhook.notificationMethodId);
|
setEditingWebhook(webhook);
|
||||||
setForm({ alias: webhook.alias, url: webhook.url });
|
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) {
|
async function deleteWebhook(methodId) {
|
||||||
@@ -273,11 +299,50 @@ export function NotificationMethodsView({ onBack }) {
|
|||||||
</Field>
|
</Field>
|
||||||
<button className="primary" disabled={busy}>
|
<button className="primary" disabled={busy}>
|
||||||
<Plus aria-hidden="true" size={18} />
|
<Plus aria-hidden="true" size={18} />
|
||||||
{editingId ? '更新' : '登録'}
|
登録
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<Dialog.Root open={editDialogOpen} onOpenChange={handleEditDialogOpenChange}>
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Overlay className="dialog-overlay" />
|
||||||
|
<Dialog.Content className="dialog-content">
|
||||||
|
<Dialog.Title className="dialog-title">Webhookを編集</Dialog.Title>
|
||||||
|
<form className="dialog-form" onSubmit={submitWebhookEdit}>
|
||||||
|
<Field label="エイリアス名">
|
||||||
|
<input
|
||||||
|
value={editForm.alias}
|
||||||
|
onChange={(event) => setEditForm({ ...editForm, alias: event.target.value })}
|
||||||
|
placeholder="Slack 通知"
|
||||||
|
maxLength="120"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field label="URL">
|
||||||
|
<input
|
||||||
|
value={editForm.url}
|
||||||
|
onChange={(event) => setEditForm({ ...editForm, url: event.target.value })}
|
||||||
|
placeholder="https://hooks.slack.com/services/..."
|
||||||
|
maxLength="2048"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<div className="dialog-actions">
|
||||||
|
<Dialog.Close asChild>
|
||||||
|
<button className="secondary" type="button">
|
||||||
|
キャンセル
|
||||||
|
</button>
|
||||||
|
</Dialog.Close>
|
||||||
|
<button className="primary" disabled={busy || !editingWebhook}>
|
||||||
|
更新
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Portal>
|
||||||
|
</Dialog.Root>
|
||||||
|
|
||||||
<section className="panel">
|
<section className="panel">
|
||||||
<h2>登録済みWebhook</h2>
|
<h2>登録済みWebhook</h2>
|
||||||
<div className="method-list">
|
<div className="method-list">
|
||||||
|
|||||||
Reference in New Issue
Block a user