Fix database conflict and update schema

This commit is contained in:
2025-06-07 13:58:08 +09:00
parent 9580740398
commit 9b18963041
6 changed files with 260 additions and 214 deletions

View File

@ -23,7 +23,7 @@ function App() {
className={`tab ${activeTab === 'feeds' ? 'active' : ''}`}
onClick={() => setActiveTab('feeds')}
>
</button>
</div>

View File

@ -1,141 +1,61 @@
import { useState, useEffect } from 'react'
interface Feed {
id: string
url: string
title?: string
description?: string
active: boolean
lastUpdated?: string
}
import { useState } from 'react'
function FeedManager() {
const [feeds, setFeeds] = useState<Feed[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [success, setSuccess] = useState<string | null>(null)
const [newFeedUrl, setNewFeedUrl] = useState('')
const [adding, setAdding] = useState(false)
const [requestMessage, setRequestMessage] = useState('')
const [requesting, setRequesting] = useState(false)
useEffect(() => {
fetchFeeds()
}, [])
const fetchFeeds = async () => {
try {
setLoading(true)
const response = await fetch('/api/feeds')
if (!response.ok) throw new Error('フィードの取得に失敗しました')
const data = await response.json()
setFeeds(data)
} catch (err) {
setError(err instanceof Error ? err.message : 'エラーが発生しました')
} finally {
setLoading(false)
}
}
const addFeed = async (e: React.FormEvent) => {
const submitRequest = async (e: React.FormEvent) => {
e.preventDefault()
if (!newFeedUrl.trim()) return
try {
setAdding(true)
setRequesting(true)
setError(null)
setSuccess(null)
const response = await fetch('/api/feeds', {
const response = await fetch('/api/feed-requests', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: newFeedUrl.trim() }),
body: JSON.stringify({
url: newFeedUrl.trim(),
requestMessage: requestMessage.trim() || undefined
}),
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'フィードの追加に失敗しました')
throw new Error(errorData.error || 'リクエストの送信に失敗しました')
}
setSuccess('フィードを追加しました')
setSuccess('フィードリクエストを送信しました。管理者の承認をお待ちください。')
setNewFeedUrl('')
await fetchFeeds()
setRequestMessage('')
} catch (err) {
setError(err instanceof Error ? err.message : 'エラーが発生しました')
} finally {
setAdding(false)
setRequesting(false)
}
}
const deleteFeed = async (feedId: string) => {
if (!confirm('このフィードを削除しますか?関連するエピソードも削除されます。')) {
return
}
try {
setError(null)
setSuccess(null)
const response = await fetch(`/api/feeds/${feedId}`, {
method: 'DELETE',
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'フィードの削除に失敗しました')
}
setSuccess('フィードを削除しました')
await fetchFeeds()
} catch (err) {
setError(err instanceof Error ? err.message : 'エラーが発生しました')
}
}
const toggleFeed = async (feedId: string, active: boolean) => {
try {
setError(null)
setSuccess(null)
const response = await fetch(`/api/feeds/${feedId}/toggle`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ active }),
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'フィードの状態変更に失敗しました')
}
setSuccess(`フィードを${active ? '有効' : '無効'}にしました`)
await fetchFeeds()
} catch (err) {
setError(err instanceof Error ? err.message : 'エラーが発生しました')
}
}
const formatDate = (dateString?: string) => {
if (!dateString) return '未更新'
return new Date(dateString).toLocaleString('ja-JP')
}
if (loading) {
return <div className="loading">...</div>
}
return (
<div>
{error && <div className="error">{error}</div>}
{success && <div className="success">{success}</div>}
<div style={{ marginBottom: '30px' }}>
<h2></h2>
<form onSubmit={addFeed}>
<h2></h2>
<p style={{ color: '#666', marginBottom: '20px' }}>
RSSフィードのURLを送信してください
</p>
<form onSubmit={submitRequest}>
<div className="form-group">
<label className="form-label">RSS URL</label>
<label className="form-label">RSS URL *</label>
<input
type="url"
className="form-input"
@ -145,66 +65,37 @@ function FeedManager() {
required
/>
</div>
<div className="form-group">
<label className="form-label"></label>
<textarea
className="form-input"
value={requestMessage}
onChange={(e) => setRequestMessage(e.target.value)}
placeholder="このフィードについての説明や追加理由があれば記載してください"
rows={3}
style={{ resize: 'vertical', minHeight: '80px' }}
/>
</div>
<button
type="submit"
className="btn btn-primary"
disabled={adding}
disabled={requesting}
>
{adding ? '追加中...' : 'フィードを追加'}
{requesting ? 'リクエスト送信中...' : 'フィードをリクエスト'}
</button>
</form>
</div>
<div>
<h2> ({feeds.length})</h2>
{feeds.length === 0 ? (
<div className="empty-state">
<p></p>
</div>
) : (
<div>
{feeds.map((feed) => (
<div key={feed.id} className="feed-item">
<div className="feed-info">
<div className="feed-title">
{feed.title || 'タイトル不明'}
{!feed.active && (
<span style={{
marginLeft: '8px',
padding: '2px 6px',
backgroundColor: '#dc3545',
color: 'white',
fontSize: '12px',
borderRadius: '3px'
}}>
</span>
)}
</div>
<div className="feed-url">{feed.url}</div>
<div style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>
: {formatDate(feed.lastUpdated)}
</div>
</div>
<div className="feed-actions">
<button
className={`btn ${feed.active ? 'btn-secondary' : 'btn-primary'}`}
onClick={() => toggleFeed(feed.id, !feed.active)}
>
{feed.active ? '無効化' : '有効化'}
</button>
<button
className="btn btn-danger"
onClick={() => deleteFeed(feed.id)}
>
</button>
</div>
</div>
))}
</div>
)}
<div style={{ backgroundColor: '#f8f9fa', padding: '20px', borderRadius: '8px' }}>
<h3 style={{ marginBottom: '15px' }}></h3>
<ul style={{ paddingLeft: '20px', color: '#666' }}>
<li></li>
<li>RSSフィードと判断された場合</li>
<li></li>
<li></li>
</ul>
</div>
</div>
)