概要
良い視点ですね!👍
Firebaseのセットアップ手順を整理する前に、使用するライブラリと機能を明確にしておきましょう!✨
✅ 1. 使用するFirebaseライブラリ
**「ArchiSkill Studio」**は React + Firebase で開発するため、
→ JavaScript (modular) を選択しましょう!
(理由:モダンで軽量な構成が可能&現在の公式推奨)
📌 公式ドキュメントの選択手順:
Firebaseリファレンス → JavaScript (modular) を選択
→ 最新の構成に対応していて、記述も簡潔です!
✅ 2. 利用するFirebaseの機能
以下の機能を使用します:
🔑 ① Authentication(認証)
→ ログイン、ログアウト、アカウント作成、アカウント削除 に使用
→ メールとパスワード認証を利用予定
💾 ② Cloud Firestore(NoSQLデータベース)
→ 将来的なユーザーデータの保存や学習履歴に使用可能
→ ただし、初期構築では不要
📂 ③ Storage(ストレージ)
→ ファイルアップロード、ダウンロード、削除に使用
→ RhinocerosやTwinmotionのファイルなどを保存
🌐 ④ Hosting(ホスティング)
→ アプリをFirebase上にデプロイする場合に使用
→ MVP開発段階では不要
→ ローカル開発で進める方針で問題なし
✅ 3. 初期段階で必要なもの
🔥 MVPでは以下の2つで十分です:
- Authentication → 認証機能
- Storage → ファイル管理機能
💡 Cloud FirestoreとHostingは、将来的にデータ管理や公開時に必要になったら追加しましょう!
✅ まとめ
使用するライブラリ:
- JavaScript (modular)
使用するFirebase機能:
- MVP → Authentication(認証) + Storage(ストレージ)
- 将来的 → Cloud Firestore(データ保存) + Hosting(デプロイ)
これで、開発の方向性がクリアになりますね!💻🚀
CORS(Cross-Origin Resource Sharing)は不要
✅ 1️⃣ CORSとは?
**CORS(クロスオリジンリソース共有)**は、
→ 異なるドメイン間でリソースをやり取りする場合に必要な仕組みです。
🔥 「オリジン(origin)」とは?
- オリジン = プロトコル + ドメイン + ポート
- 例:
https://example.com:3000→ オリジン①https://example.com:4000→ オリジン②(ポートが異なるため別オリジン)http://example.com→ オリジン③(プロトコルが異なるため別オリジン)
✅ 2️⃣ CORSが不要なケース(同一オリジンの場合)
同じオリジン内で通信する場合は、CORS設定は不要です。
📌 例①: ローカル開発の場合
localhost:3000(Reactアプリ)localhost:3000(Firebase Storageへのアクセス)
→ 同一オリジンなのでCORS設定は不要
📌 例②: 同一ドメインでデプロイする場合
https://archiskill.web.app(アプリ)https://firebasestorage.googleapis.com/v0/b/archiskill.appspot.com/o/...
→ Firebaseは同一オリジン扱いで自動的に許可
→ CORS設定は不要
✅ 結論:
同一オリジンの場合はCORS設定不要です!
🚫 3️⃣ CORSが必要なケース(異なるオリジンの場合)
異なるドメイン間で通信する場合は、CORSが必要です。
📌 例①: 異なるドメインにデプロイする場合
- アプリ →
https://archiskill.com - Storage →
https://firebasestorage.googleapis.com/...
→ 異なるオリジンなので、CORS設定が必要
📌 例②: 外部アプリからファイルを取得する場合
- ArchiSkillのデータを別のサイトから取得
- 例:
fetch("https://firebasestorage.googleapis.com/...")
→ 外部からアクセスするため、CORSエラーが発生
→ CORS設定が必要
📌 例③: CDNなどを利用する場合
- ファイルをCDN経由で配信する
- → CDNとFirebaseは異なるオリジンのためCORSが必要
✅ 結論:
異なるオリジンからアクセスする場合はCORSが必要です!
✅ 4️⃣ 図解で理解
📌 CORS不要なケース(同一オリジン)
┌────────────────────────────┐
│ Reactアプリ │
│ https://archiskill.web.app │
│ → Firebase Storageにアクセス│
│ → ✅ CORS設定不要 │
└────────────────────────────┘
→ 同一オリジンなので問題なし!
📌 CORSが必要なケース(異なるオリジン)
┌────────────────────────────┐
│ ArchiSkillアプリ │
│ https://archiskill.com │
│ │
└────────────────────────────┘
│
│ 異なるオリジン(外部からアクセス)
▼
┌────────────────────────────┐
│ Firebase Storage │
│ https://firebasestorage.googleapis.com │
│ │
│ → 🚫 CORSエラー発生! │
└────────────────────────────┘
→ 異なるオリジンなのでCORSが必要!
✅ 5️⃣ まとめ
💡 CORS設定が不要なケース:
- 同一オリジンでアクセスする場合
- ローカル開発(
localhostで同一ポート) - FirebaseアプリとStorageが同じプロジェクトの場合
🚫 CORS設定が必要なケース:
- 異なるドメインからアクセスする場合
- 別ドメインでデプロイ&ファイルアクセス
- 外部アプリからファイル取得
- CDN経由でファイルを配信する場合
✨ 今回のArchiSkill Studioでは、ローカル開発時やFirebase Hostingにデプロイする場合は同一オリジンになるため、CORS設定は不要です!
→ ただし、将来的に別ドメインで公開する場合は、CORS設定が必要になります!✅
Firebase CLIを使用した初期化は不要
✅ 理由
1️⃣ FirebaseのセットアップはすべてWebコンソールで完結
→ Firebaseのプロジェクト作成、Authentication、Firestore、Storageの設定はコンソール上で完了できます。
→ CLIを使わなくても必要な機能はすべて使えます!
2️⃣ CORS設定も不要
→ 同一オリジンでアクセスするため、CORS設定は不要。
→ CLIでのgsutilコマンドによるCORS設定は必要ありません。
✅ 手順の確認
🔥 今回の手順は以下の流れです:
① Firebaseコンソールでプロジェクト作成
- Authentication、Firestore、Storageを有効化
- ホスティングは使わないので無効のままでOK
② Firebase設定ファイルをReactに追加
firebase-config.jsでAPIキーと設定を記載- ReactからFirebase SDKで接続
③ ローカル開発 & Firebase連携
npm startでローカル開発- ReactからFirebaseにファイルアップロードや認証を実装
→ CLIは不要!
✅ CLIを使う場合はこんなとき!
将来的に以下のケースでFirebase CLIを使う可能性があります:
📌 ① 本番環境にデプロイする場合
- Firebase Hostingでデプロイする場合は、Firebase CLIでデプロイ作業が必要です。
firebase deployでデプロイ可能
📌 ② CORS設定が必要になった場合
- 将来的に別ドメインからStorageにアクセスする場合は、Firebase CLIでCORS設定が必要です。
gsutil cors set cors.json gs://<バケット名>でCORS設定
✅ 結論
🚀 今回の構成では、CLIや初期化は一切不要!
- FirebaseコンソールとReact側のSDK接続のみで完結します!👍
Reactセットアップ
npx create-react-app rhino-share
ディレクトリ構成
archiskill-studio
└─ src
├─ pages
│ └─ Home.js → 全機能のボタンを配置
├─ utils
│ ├─ firebase-config.js → Firebaseの初期設定
│ └─ auth.js → 認証関連の処理
├─ App.js
└─ index.js
ReactからFirebase SDKで接続する手順
✅ 1️⃣ 必要な手順の流れ
1️⃣ Firebaseコンソールでプロジェクト作成と設定
2️⃣ Firebase SDKをReactにインストール
3️⃣ Firebase設定ファイルを作成
4️⃣ ReactでFirebaseを初期化して接続
✅ 2️⃣ Firebaseの設定手順
🔥 ① Firebaseコンソールでプロジェクト作成
1️⃣ Firebaseコンソールにアクセス
2️⃣ 「プロジェクトを作成」をクリック
3️⃣ 名前を入力(例:archiskill-studio)
4️⃣ 「続行」をクリック
5️⃣ GoogleアナリティクスはオフでOK
🔥 ② プロジェクトに必要なサービスを有効化
次に以下を有効化します:
📌 ① Authentication(認証)
- メニュー →「ビルド」→「Authentication」
- 「開始する」をクリック
- サインイン方法で「メール/パスワード」を有効化
- 保存
📌 ② Firestore(データベース)
- メニュー →「ビルド」→「Firestore データベース」
- 「データベースの作成」をクリック
- モードはテストモードを選択
- 次へ → 有効
Firestoreデータベース作成時は以下で設定しましょう!
- データベースID → デフォルトの
(default)でOK! - ロケーション →
asia-northeast1(東京)がおすすめ! ✅
📌 ③ Storage(ファイル保存)
- メニュー →「ビルド」→「Storage」
- 「開始する」をクリック
- セキュリティルールはテストモードで開始
- 完了
🔥 ③ Firebaseの設定ファイルを取得
1️⃣ メニュー → 「プロジェクトの設定」
2️⃣ 「アプリを追加」→「Web」を選択
3️⃣ アプリ名 → archiskill-webなど適当な名前を入力
4️⃣ Firebase構成コードが表示されます
→ このコードを後で使用します!
✅ 3️⃣ ReactでFirebase SDKを接続
🔥 ① Firebase SDKをインストール
ターミナルで以下を実行して、Firebaseライブラリをインストールします:
npm install firebase
✅ これでReactアプリでFirebaseを使えるようになります!
🔥 ② Firebase設定ファイルを作成
/src/utils/firebase-config.jsを作成し、Firebaseの構成を記載します。
📌 firebase-config.js
// Import the Firebase SDK
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import { getStorage } from "firebase/storage";
// Firebase構成情報(Firebaseコンソールから取得した情報に置き換え)
const firebaseConfig = {
apiKey: "YOUR_API_KEY",
authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
projectId: "YOUR_PROJECT_ID",
storageBucket: "YOUR_PROJECT_ID.appspot.com",
messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
appId: "YOUR_APP_ID"
};
// Firebaseアプリを初期化
const app = initializeApp(firebaseConfig);
// 各サービスをエクスポート
export const auth = getAuth(app); // 認証機能
export const db = getFirestore(app); // Firestore
export const storage = getStorage(app); // Storage
✅ ポイント:
getAuth()→ 認証機能getFirestore()→ FirestoreデータベースgetStorage()→ Storage(ファイルアップロード)exportで各サービスを外部で使用できるようにしています
🔥 ③ FirebaseをReactで接続
App.jsでFirebaseに接続します。
📌 App.js
import React from "react";
import { auth, db, storage } from "./utils/firebase-config";
function App() {
console.log("Firebase Auth:", auth);
console.log("Firebase Firestore:", db);
console.log("Firebase Storage:", storage);
return (
<div>
<h1>ArchiSkill Studio</h1>
<p>Firebaseに接続完了!</p>
</div>
);
}
export default App;
✅ ポイント
console.log()で接続確認- アプリを起動したときにFirebaseのオブジェクトが表示されていれば接続成功!
🔥 ④ アプリを起動して動作確認
以下のコマンドでアプリを起動します:
npm start
📌 ブラウザでhttp://localhost:3000にアクセス
→ コンソールに以下のようなログが表示されれば成功!
Firebase Auth: Object { ... }
Firebase Firestore: Object { ... }
Firebase Storage: Object { ... }
✅ 4️⃣ これで接続完了!
🔥 これで、ReactからFirebaseに接続できました!
→ あとは、認証・ファイルアップロード・ダウンロードなどの機能を実装していけばOKです!💡
🚀 ✨ 次のステップ
ファイルアップロード機能や認証機能を実装する際は、
このfirebase-config.jsを使ってFirestoreやStorageと接続できます!👍
🚀 1️⃣ 認証機能(ログイン・ログアウト・アカウント作成・削除)を実装
✅ 💡 実装の流れ
以下の手順で進めます:
1️⃣ FirebaseのAuth設定と関数を作成
2️⃣ Homeページにボタンと入力フォームを設置
3️⃣ ログイン・ログアウト・アカウント作成・削除の処理を実装
4️⃣ 動作確認
✅ 1️⃣ FirebaseのAuth関数を作成
まずはFirebaseの認証関連関数をauth.jsに作成します。
📌 ディレクトリ構成
/src
├── /pages
│ └── Home.js
├── /utils
│ ├── firebase-config.js
│ └── auth.js ← ✅ここに作成
├── App.js
├── index.js
📌 src/utils/auth.js
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, signOut, deleteUser } from "firebase/auth";
import { auth } from "./firebase-config";
// ✅ 新規アカウント作成
export const signup = async (email, password) => {
try {
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
console.log("アカウント作成成功:", userCredential.user);
return userCredential.user;
} catch (error) {
console.error("アカウント作成失敗:", error.message);
throw error;
}
};
// ✅ ログイン
export const login = async (email, password) => {
try {
const userCredential = await signInWithEmailAndPassword(auth, email, password);
console.log("ログイン成功:", userCredential.user);
return userCredential.user;
} catch (error) {
console.error("ログイン失敗:", error.message);
throw error;
}
};
// ✅ ログアウト
export const logout = async () => {
try {
await signOut(auth);
console.log("ログアウト成功");
} catch (error) {
console.error("ログアウト失敗:", error.message);
throw error;
}
};
// ✅ アカウント削除
export const deleteAccount = async () => {
const user = auth.currentUser; // 現在のユーザーを取得
if (user) {
try {
await deleteUser(user);
console.log("アカウント削除成功");
} catch (error) {
console.error("アカウント削除失敗:", error.message);
throw error;
}
} else {
console.error("ユーザーがログインしていません");
throw new Error("ユーザーがログインしていません");
}
};
✅ 3️⃣ Firebase Authにメール/パスワードログインを有効化
1️⃣ Firebaseコンソールにアクセス
2️⃣ 左側メニューから**「Authentication」** → **「Sign-in method」**を選択
3️⃣ **「メール/パスワード」**を有効にする
4️⃣ 保存
ライブラリインストール
npm install react-router-dom
Material-UIインストール
npm install @mui/material @emotion/react @emotion/styled
ディレクトリ構成
/src
├── /pages
│ ├── Home.js → ホーム画面(ボタン配置)
│ ├── Login.js → ログインページ
│ ├── Signup.js → アカウント作成ページ
│ └── Dashboard.js → ログイン後のホーム画面
├── /utils
│ ├── firebase-config.js
│ └── auth.js
├── App.js → ルーティング管理
├── index.js
✅ 2️⃣ Homeページにフォームとボタンを設置
次に、Home.jsにログイン・アカウント作成・ログアウト・削除用のボタンと入力フォームを追加します。
📌 src/pages/Home.js
import React from "react";
import { Container, Typography, Button, Box } from "@mui/material";
import { useNavigate } from "react-router-dom";
const Home = () => {
const navigate = useNavigate();
return (
<Container maxWidth="sm" sx={{ textAlign: "center", mt: 8 }}>
<Typography variant="h3" gutterBottom>
ArchiSkill Studio
</Typography>
<Typography variant="body1" sx={{ mb: 4 }}>
建築・インテリア業界向けの学習ツール
</Typography>
<Box sx={{ display: "flex", justifyContent: "space-around" }}>
<Button
variant="contained"
color="primary"
onClick={() => navigate("/signup")}
>
アカウント作成
</Button>
<Button
variant="outlined"
color="secondary"
onClick={() => navigate("/login")}
>
ログイン
</Button>
</Box>
</Container>
);
};
export default Home;
ログインページ
import React, { useState } from "react";
import { Container, TextField, Button, Typography, Box, Alert } from "@mui/material";
import { useNavigate } from "react-router-dom";
import { login } from "../utils/auth"; // Firebase認証関数をインポート
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const navigate = useNavigate();
const handleLogin = async (e) => {
e.preventDefault();
setError("");
try {
await login(email, password);
navigate("/dashboard"); // ログイン後にダッシュボードへ
} catch (error) {
setError(error.message);
}
};
return (
<Container maxWidth="xs" sx={{ mt: 8 }}>
<Typography variant="h4" align="center" gutterBottom>
ログイン
</Typography>
{error && <Alert severity="error">{error}</Alert>}
<form onSubmit={handleLogin}>
<TextField
label="メールアドレス"
type="email"
fullWidth
margin="normal"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<TextField
label="パスワード"
type="password"
fullWidth
margin="normal"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<Box sx={{ mt: 2 }}>
<Button type="submit" variant="contained" color="primary" fullWidth>
ログイン
</Button>
</Box>
</form>
</Container>
);
};
export default Login;
アカウント作成ページ
import React, { useState } from "react";
import { Container, TextField, Button, Typography, Box, Alert } from "@mui/material";
import { useNavigate } from "react-router-dom";
import { signup } from "../utils/auth"; // Firebase認証関数をインポート
const Signup = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const navigate = useNavigate();
const handleSignup = async (e) => {
e.preventDefault();
setError("");
try {
await signup(email, password);
navigate("/dashboard"); // 登録後にダッシュボードへ
} catch (error) {
setError(error.message);
}
};
return (
<Container maxWidth="xs" sx={{ mt: 8 }}>
<Typography variant="h4" align="center" gutterBottom>
アカウント作成
</Typography>
{error && <Alert severity="error">{error}</Alert>}
<form onSubmit={handleSignup}>
<TextField
label="メールアドレス"
type="email"
fullWidth
margin="normal"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<TextField
label="パスワード"
type="password"
fullWidth
margin="normal"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<Box sx={{ mt: 2 }}>
<Button type="submit" variant="contained" color="primary" fullWidth>
アカウント作成
</Button>
</Box>
</form>
</Container>
);
};
export default Signup;
ダッシュボード画面(ログイン後のホーム画面)
import React, { useState } from "react";
import { Container, Typography, Button, Alert, Box, CircularProgress } from "@mui/material";
import { useNavigate } from "react-router-dom";
import { logout, deleteAccount } from "../utils/auth"; // アカウント削除関数をインポート
const Dashboard = () => {
const navigate = useNavigate();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
// ✅ ログアウト処理
const handleLogout = async () => {
try {
await logout();
navigate("/");
} catch (error) {
console.error("ログアウト失敗:", error.message);
setError("ログアウトに失敗しました");
}
};
// ✅ アカウント削除処理
const handleDeleteAccount = async () => {
const confirmDelete = window.confirm("本当にアカウントを削除しますか?この操作は取り消せません!");
if (!confirmDelete) return;
setLoading(true);
setError("");
try {
await deleteAccount();
alert("アカウントが削除されました");
navigate("/");
} catch (error) {
console.error("アカウント削除失敗:", error.message);
setError("アカウント削除に失敗しました。再度ログインしてください。");
} finally {
setLoading(false);
}
};
return (
<Container maxWidth="md" sx={{ mt: 8, textAlign: "center" }}>
<Typography variant="h4" gutterBottom>
ダッシュボード
</Typography>
<Typography variant="body1" sx={{ mb: 4 }}>
ログイン済みユーザー用画面です。
</Typography>
{error && <Alert severity="error">{error}</Alert>}
<Box sx={{ display: "flex", justifyContent: "center", gap: 2, mt: 4 }}>
<Button
variant="contained"
color="secondary"
onClick={handleLogout}
disabled={loading}
>
{loading ? <CircularProgress size={24} /> : "ログアウト"}
</Button>
<Button
variant="contained"
color="error"
onClick={handleDeleteAccount}
disabled={loading}
>
{loading ? <CircularProgress size={24} /> : "アカウント削除"}
</Button>
</Box>
</Container>
);
};
export default Dashboard;
App.jsでルーティング
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import Signup from "./pages/Signup";
import Dashboard from "./pages/Dashboard";
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Router>
);
};
export default App;
✅ 4️⃣ 動作確認
📌 開発サーバーを再起動
npm start
📌 動作確認手順 1️⃣ メールアドレスとパスワードを入力
2️⃣ 「アカウント作成」ボタン → Firebaseにユーザーが作成される
3️⃣ 「ログイン」ボタン → ログイン成功
4️⃣ 「ログアウト」ボタン → ログアウト
5️⃣ 「アカウント削除」ボタン → Firebaseからユーザーが削除される
ブラウザで確認
✅ 挙動
- ホーム画面でボタンをクリックすると、ログインとアカウント作成ページに遷移
- ダッシュボード画面が表示される
🎯 🔥 成功!
これでFirebase Authenticationを使ったログイン・ログアウト・アカウント作成・削除が完成しました!🎉
🚀 次のステップ
次は「ファイルアップロード・ダウンロード・削除機能」の実装に進みましょうか?💾🔥
🚀 ✅ 🔥 ダッシュボードに「ファイルアップロード・削除機能」を追加
✅ 1️⃣ 機能概要
- ファイルアップロード:
- ダッシュボードでファイルを選択 → Firebase Storage にアップロード
- Firestore にファイル情報(名前・URL)を保存
- ファイル削除:
- アップロード済みファイルをリスト表示
- 選択したファイルを Firebase Storage & Firestore から削除
✅ 2️⃣ Firebase FirestoreとStorageの初期設定
アップロードしたファイル情報を保存するために Firestore、ファイルデータは Storage を利用します。
🔥 Firestoreに「files」コレクションを作成
- Firebase コンソール にアクセス
- Firestore Database → 「データベースの作成」
- ルールをテストモードで開始
- 「ファイル情報を保存する」ために以下のコレクションを作成:
- コレクション名 →
files - フィールド →
name→ 文字列型url→ 文字列型
- コレクション名 →
🔥 Storageのルール設定
- Firebaseコンソール → Storage → ルール
- 以下のルールに変更 → 保存
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null; // 認証ユーザーのみ許可
}
}
}
ライブラリインストール
アイコンライブラリをインストールします。
npm install @mui/icons-material
✅ 3️⃣ Firebase関連の関数を作成
🔥 /src/utils/storage.js → ファイルアップロードと削除処理を作成
import { storage, db } from "./firebase-config";
import { ref, uploadBytes, getDownloadURL, deleteObject } from "firebase/storage";
import { collection, addDoc, getDocs, deleteDoc, doc } from "firebase/firestore";
// ✅ ファイルをアップロード
export const uploadFile = async (file) => {
if (!file) throw new Error("ファイルが選択されていません");
const storageRef = ref(storage, `uploads/${file.name}`);
try {
// Storageにファイルをアップロード
await uploadBytes(storageRef, file);
const url = await getDownloadURL(storageRef);
// Firestoreにファイル情報を保存
await addDoc(collection(db, "files"), {
name: file.name,
url: url,
});
console.log("ファイルアップロード成功:", file.name);
return { name: file.name, url };
} catch (error) {
console.error("ファイルアップロード失敗:", error);
throw error;
}
};
// ✅ Firestoreからファイル情報を取得
export const getFiles = async () => {
try {
const querySnapshot = await getDocs(collection(db, "files"));
return querySnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
} catch (error) {
console.error("ファイル取得失敗:", error);
throw error;
}
};
// ✅ ファイル削除処理
export const deleteFile = async (id, fileName) => {
const storageRef = ref(storage, `uploads/${fileName}`);
try {
// Storageからファイル削除
await deleteObject(storageRef);
// Firestoreからファイル情報を削除
await deleteDoc(doc(db, "files", id));
console.log("ファイル削除成功:", fileName);
} catch (error) {
console.error("ファイル削除失敗:", error);
throw error;
}
};
✅ 4️⃣ ダッシュボードに「ファイルアップロード・削除機能」を追加
🔥 Dashboard.js → ファイルアップロードと削除機能を追加
import React, { useState, useEffect } from "react";
import {
Container,
Typography,
Button,
Box,
CircularProgress,
List,
ListItem,
ListItemText,
IconButton,
Alert
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import { uploadFile, getFiles, deleteFile } from "../utils/storage";
import { logout, deleteAccount } from "../utils/auth";
import { useNavigate } from "react-router-dom";
const Dashboard = () => {
const [file, setFile] = useState(null);
const [files, setFiles] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const navigate = useNavigate();
// ✅ ファイル一覧を取得
useEffect(() => {
const fetchFiles = async () => {
try {
const fileList = await getFiles();
setFiles(fileList);
} catch (error) {
setError("ファイル取得に失敗しました");
}
};
fetchFiles();
}, []);
// ✅ ファイル選択時
const handleFileChange = (event) => {
setFile(event.target.files[0]);
};
// ✅ ファイルアップロード
const handleUpload = async () => {
if (!file) return;
setLoading(true);
setError("");
try {
await uploadFile(file);
alert("ファイルがアップロードされました");
setFile(null);
// 再読み込み
const fileList = await getFiles();
setFiles(fileList);
} catch (error) {
setError("ファイルアップロードに失敗しました");
} finally {
setLoading(false);
}
};
// ✅ ファイル削除
const handleDelete = async (id, fileName) => {
const confirmDelete = window.confirm("このファイルを削除しますか?");
if (!confirmDelete) return;
try {
await deleteFile(id, fileName);
alert("ファイルが削除されました");
// 削除後に再読み込み
const fileList = await getFiles();
setFiles(fileList);
} catch (error) {
setError("ファイル削除に失敗しました");
}
};
// ✅ ログアウト
const handleLogout = async () => {
await logout();
navigate("/");
};
return (
<Container maxWidth="md" sx={{ mt: 8 }}>
<Typography variant="h4" gutterBottom>
ダッシュボード
</Typography>
{error && <Alert severity="error">{error}</Alert>}
<Box sx={{ mt: 4, mb: 2 }}>
<input type="file" onChange={handleFileChange} />
<Button
variant="contained"
color="primary"
onClick={handleUpload}
disabled={loading || !file}
sx={{ ml: 2 }}
>
{loading ? <CircularProgress size={24} /> : "アップロード"}
</Button>
</Box>
<List>
{files.map((file) => (
<ListItem key={file.id}>
<ListItemText primary={file.name} secondary={file.url} />
<IconButton
edge="end"
aria-label="delete"
onClick={() => handleDelete(file.id, file.name)}
>
<DeleteIcon />
</IconButton>
</ListItem>
))}
</List>
<Button variant="contained" color="secondary" onClick={handleLogout} sx={{ mt: 4 }}>
ログアウト
</Button>
</Container>
);
};
export default Dashboard;
✅ 5️⃣ 動作確認
📌 サーバー起動
npm start
📌 ブラウザで確認
http://localhost:3000
🚀 ✅ 完了!
これでダッシュボード画面に「ファイルアップロード・削除機能」が追加されました!🔥
次は「ファイルダウンロード機能」を実装しましょうか? 🚀