171 lines
5.1 KiB
TypeScript
171 lines
5.1 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { atprotoOAuthService } from '../services/atproto-oauth';
|
|
import { Card } from './Card';
|
|
import '../styles/CardBox.css';
|
|
|
|
interface CardBoxProps {
|
|
userDid: string;
|
|
}
|
|
|
|
export const CardBox: React.FC<CardBoxProps> = ({ userDid }) => {
|
|
const [boxData, setBoxData] = useState<any>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [showJson, setShowJson] = useState(false);
|
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
|
|
useEffect(() => {
|
|
loadBoxData();
|
|
}, [userDid]);
|
|
|
|
const loadBoxData = async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const data = await atprotoOAuthService.getCardsFromBox();
|
|
setBoxData(data);
|
|
} catch (err) {
|
|
// Failed to load card box
|
|
setError(err instanceof Error ? err.message : 'カードボックスの読み込みに失敗しました');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSaveToBox = async () => {
|
|
// 現在のカードデータを取得してボックスに保存
|
|
// この部分は親コンポーネントから渡すか、APIから取得する必要があります
|
|
alert('カードボックスへの保存機能は親コンポーネントから実行してください');
|
|
};
|
|
|
|
const handleDeleteBox = async () => {
|
|
if (!window.confirm('カードボックスを削除してもよろしいですか?\nこの操作は取り消せません。')) {
|
|
return;
|
|
}
|
|
|
|
setIsDeleting(true);
|
|
setError(null);
|
|
|
|
try {
|
|
await atprotoOAuthService.deleteCardBox();
|
|
setBoxData({ records: [] });
|
|
alert('カードボックスを削除しました');
|
|
} catch (err) {
|
|
// Failed to delete card box
|
|
setError(err instanceof Error ? err.message : 'カードボックスの削除に失敗しました');
|
|
} finally {
|
|
setIsDeleting(false);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="card-box-container">
|
|
<div className="loading">カードボックスを読み込み中...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="card-box-container">
|
|
<div className="error">エラー: {error}</div>
|
|
<button onClick={loadBoxData} className="retry-button">
|
|
再試行
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const records = boxData?.records || [];
|
|
const selfRecord = records.find((record: any) => record.uri.includes('/self'));
|
|
const cards = selfRecord?.value?.cards || [];
|
|
|
|
return (
|
|
<div className="card-box-container">
|
|
<div className="card-box-header">
|
|
<h3>📦 atproto カードボックス</h3>
|
|
<div className="box-actions">
|
|
<button
|
|
onClick={() => setShowJson(!showJson)}
|
|
className="json-button"
|
|
>
|
|
{showJson ? 'JSON非表示' : 'JSON表示'}
|
|
</button>
|
|
<button onClick={loadBoxData} className="refresh-button">
|
|
🔄 更新
|
|
</button>
|
|
{cards.length > 0 && (
|
|
<button
|
|
onClick={handleDeleteBox}
|
|
className="delete-button"
|
|
disabled={isDeleting}
|
|
>
|
|
{isDeleting ? '削除中...' : '🗑️ 削除'}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="uri-display">
|
|
<p>
|
|
<strong>📍 URI:</strong>
|
|
<code>at://did:plc:uqzpqmrjnptsxezjx4xuh2mn/ai.card.box/self</code>
|
|
</p>
|
|
</div>
|
|
|
|
{showJson && (
|
|
<div className="json-display">
|
|
<h4>Raw JSON データ:</h4>
|
|
<pre className="json-content">
|
|
{JSON.stringify(boxData, null, 2)}
|
|
</pre>
|
|
</div>
|
|
)}
|
|
|
|
<div className="box-stats">
|
|
<p>
|
|
<strong>総カード数:</strong> {cards.length}枚
|
|
{selfRecord?.value?.updated_at && (
|
|
<>
|
|
<br />
|
|
<strong>最終更新:</strong> {new Date(selfRecord.value.updated_at).toLocaleString()}
|
|
</>
|
|
)}
|
|
</p>
|
|
</div>
|
|
|
|
{cards.length > 0 ? (
|
|
<>
|
|
<div className="card-grid">
|
|
{cards.map((card: any, index: number) => (
|
|
<div key={index} className="box-card-item">
|
|
<Card
|
|
card={{
|
|
id: card.id,
|
|
cp: card.cp,
|
|
status: card.status,
|
|
skill: card.skill,
|
|
owner_did: card.owner_did,
|
|
obtained_at: card.obtained_at,
|
|
is_unique: card.is_unique,
|
|
unique_id: card.unique_id
|
|
}}
|
|
/>
|
|
<div className="card-info">
|
|
<small>ID: {card.id} | CP: {card.cp}</small>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</>
|
|
) : (
|
|
<div className="empty-box">
|
|
<p>カードボックスにカードがありません</p>
|
|
<p>カードを引いてからバックアップボタンを押してください</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}; |