Fix database conflict and update schema
This commit is contained in:
@ -23,7 +23,7 @@ function App() {
|
||||
className={`tab ${activeTab === 'feeds' ? 'active' : ''}`}
|
||||
onClick={() => setActiveTab('feeds')}
|
||||
>
|
||||
フィード管理
|
||||
フィードリクエスト
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -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>
|
||||
)
|
||||
|
Reference in New Issue
Block a user