You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
joint_exc/frontend/src/services/api.ts

357 lines
11 KiB

/**
* APIサービス
* バックエンドAPIとの通信を担当するモジュール
* 認証、タスク管理などの機能を提供
*/
import { LoginCredentials, RegisterCredentials, AuthResponse, Task, ToBuy, Stuff, Stock } from '../types/types';
import { AUTH_ERRORS, TASK_ERRORS } from '../constants/errorMessages';
// APIのベースURL - 環境変数から取得するか、デフォルト値を使用
const API_BASE_URL = process.env.REACT_APP_API_BASE_URL || 'http://localhost:8080';
/**
* APIリクエスト用のヘッダーを生成する関数
* @param includeAuth 認証トークンをヘッダーに含めるかどうか
* @returns リクエストヘッダーオブジェクト
*/
const getHeaders = (includeAuth: boolean = true) => {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
// 認証トークンが必要な場合はローカルストレージから取得してヘッダーに追加
if (includeAuth) {
const token = localStorage.getItem('token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
}
return headers;
};
/**
* 認証関連のAPI機能を提供するオブジェクト
* ログインと新規ユーザー登録の機能を含む
*/
export const authApi = {
/**
* ユーザーログイン処理
* @param credentials ログイン情報(ユーザー名とパスワード)
* @returns 認証レスポンス(トークンとユーザー情報)
*/
login: async (credentials: LoginCredentials): Promise<AuthResponse> => {
const response = await fetch(`${API_BASE_URL}/api/auth/login`, {
method: 'POST',
headers: getHeaders(false), // 認証前なのでトークンは不要
body: JSON.stringify(credentials),
});
// エラーレスポンスの処理
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(
errorData?.message || AUTH_ERRORS.LOGIN_FAILED
);
}
return response.json();
},
/**
* 新規ユーザー登録処理
* @param credentials 登録情報(ユーザー名、パスワード)
* @returns 認証レスポンス(トークンとユーザー情報)
*/
register: async (credentials: RegisterCredentials): Promise<AuthResponse> => {
const response = await fetch(`${API_BASE_URL}/api/auth/register`, {
method: 'POST',
headers: getHeaders(false), // 認証前なのでトークンは不要
body: JSON.stringify(credentials),
});
// エラーレスポンスの処理
if (!response.ok) {
const errorData = await response.json().catch(() => null);
throw new Error(
errorData?.message || AUTH_ERRORS.REGISTER_FAILED
);
}
return response.json();
},
};
/**
* 買うものリスト管理関連のAPI機能を提供するオブジェクト
* 買うものリストの取得、作成、更新、削除などの機能を含む
*/
export const toBuyApi = {
/**
* 全買うものリストを取得
* @returns 買うものリスト一覧
*/
getToBuys: async (): Promise<{ "tobuy_array": ToBuy[] }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
// headers: getHeaders(), // 認証トークンを含むヘッダー
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// }
// return response.json();
return {
"tobuy_array": [
{
"tobuy_id": 1,
"stuff_id": 2,
"stuff_name": "じゃがいも",
"amount": 3,
"shop": "shopXXX"
},
{
"tobuy_id": 2,
"stuff_id": 5,
"stuff_name": "にんじん",
"amount": 1
}
]
}
},
/**
* 買うものリストへの材料追加
* @param tobuy 作成する材料情報
* @returns 作成された材料情報
*/
addToBuy: async (tobuy: Omit<ToBuy, 'stuff_id' | 'tobuy_id'> & { stuff_id: number | null, category: string }): Promise<any> => {
// const response = await fetch(`${API_BASE_URL}/api/tasks`, {
// method: 'POST',
// headers: getHeaders(),
// body: JSON.stringify(tobuy),
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.CREATE_FAILED);
// }
// return response.json();
return {
"result": true,
"tobuy_id": 1,
"stuff_id": 6,
"message": "追加に成功しました",
}
},
/**
* 買うものリストを削除
* @param id 削除対象の買うものリストID
*/
deleteToBuy: async (tobuy_id: number): Promise<{ result: boolean }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/delete`, {
// method: 'DELETE',
// headers: getHeaders(),
// body: JSON.stringify({tobuy_id}),
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.DELETE_FAILED);
// }
return {
"result": true
}
},
}
export const stuffApi = {
getStuffs: async (category: string): Promise<{ stuff_array: Stuff[] }> => {
const data = [
{ "stuff_id": 1, "stuff_name": "牛乳", "category": "乳製品" },
{ "stuff_id": 2, "stuff_name": "ヨーグルト", "category": "乳製品" },
{ "stuff_id": 3, "stuff_name": "チーズ", "category": "乳製品" },
{ "stuff_id": 4, "stuff_name": "バター", "category": "乳製品" },
{ "stuff_id": 5, "stuff_name": "生クリーム", "category": "乳製品" },
{ "stuff_id": 6, "stuff_name": "鮭", "category": "魚・肉" },
{ "stuff_id": 7, "stuff_name": "鶏むね肉", "category": "魚・肉" },
{ "stuff_id": 8, "stuff_name": "豚バラ肉", "category": "魚・肉" },
{ "stuff_id": 9, "stuff_name": "牛ひき肉", "category": "魚・肉" },
{ "stuff_id": 10, "stuff_name": "まぐろ", "category": "魚・肉" },
{ "stuff_id": 11, "stuff_name": "にんじん", "category": "野菜" },
{ "stuff_id": 12, "stuff_name": "キャベツ", "category": "野菜" },
{ "stuff_id": 13, "stuff_name": "ほうれん草", "category": "野菜" },
{ "stuff_id": 14, "stuff_name": "玉ねぎ", "category": "野菜" },
{ "stuff_id": 15, "stuff_name": "ピーマン", "category": "野菜" },
{ "stuff_id": 16, "stuff_name": "醤油", "category": "調味料" },
{ "stuff_id": 17, "stuff_name": "味噌", "category": "調味料" },
{ "stuff_id": 18, "stuff_name": "塩", "category": "調味料" },
{ "stuff_id": 19, "stuff_name": "砂糖", "category": "調味料" },
{ "stuff_id": 20, "stuff_name": "酢", "category": "調味料" },
{ "stuff_id": 21, "stuff_name": "米", "category": "その他" },
{ "stuff_id": 22, "stuff_name": "パスタ", "category": "その他" },
{ "stuff_id": 23, "stuff_name": "小麦粉", "category": "その他" },
{ "stuff_id": 24, "stuff_name": "卵", "category": "その他" },
{ "stuff_id": 25, "stuff_name": "豆腐", "category": "その他" }
]
const filtered = data.filter(stuff => stuff.category == category)
return {
"stuff_array": filtered
}
}
}
export const stockApi = {
/**
* 全在庫リストを取得
* @returns 買在庫リスト一覧
*/
getStocks: async (): Promise<{ "stock_array": Stock[] }> => {
// const response = await fetch(`${API_BASE_URL}/api/tobuy/get`, {
// headers: getHeaders(), // 認証トークンを含むヘッダー
// });
// if (!response.ok) {
// throw new Error(TASK_ERRORS.FETCH_FAILED);
// }
// return response.json();
return {
"stock_array": [
{
"stock_id": 1,
"stuff_id": 10,
"stuff_name": "豚肉",
"amount": 100,
"price": 200,
"buy_date": "2025-05-18T09:00:00.000Z",
"last_update": "2025-05-18T09:00:00.000Z",
"exp_date": "2025-05-19T10:15:00.000Z",
"category": "肉"
},
{
"stock_id": 2,
"stuff_id": 1,
"stuff_name": "トマト",
"amount": 10,
"price": 200,
"buy_date": "2025-05-18T09:00:00.000Z",
"last_update": "2025-05-18T09:00:00.000Z",
"exp_date": "2025-05-19T10:15:00.000Z",
"category": "野菜"
}
]
}
},
}
/**
* (サンプル,実際には不要)
* タスク管理関連のAPI機能を提供するオブジェクト
* タスクの取得、作成、更新、削除などの機能を含む
*/
export const taskApi = {
/**
* 全タスクを取得
* @returns タスク一覧
*/
getTasks: async (): Promise<Task[]> => {
const response = await fetch(`${API_BASE_URL}/api/tasks`, {
headers: getHeaders(), // 認証トークンを含むヘッダー
});
if (!response.ok) {
throw new Error(TASK_ERRORS.FETCH_FAILED);
}
return response.json();
},
/**
* 指定IDのタスクを取得
* @param id タスクID
* @returns 単一のタスク情報
*/
getTask: async (id: number): Promise<Task> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
headers: getHeaders(),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.FETCH_FAILED);
}
return response.json();
},
/**
* 新規材料を作成
* @param task 作成するタスク情報(価格,作成日時、更新日時は除外)
* @returns 作成されたタスク情報
*/
addStuff: async (task: Omit<Task, 'userId' | 'createdAt' | 'price' | 'buyDate' | 'expirationDate' | 'newAddition'>): Promise<Task> => {
const response = await fetch(`${API_BASE_URL}/api/tubuy/add`, {
method: 'POST',
headers: getHeaders(),
body: JSON.stringify(task),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.CREATE_FAILED);
}
return response.json();
},
/**
* タスクを更新
* @param id 更新対象のタスクID
* @param task 更新するタスク情報(部分的な更新も可能)
* @returns 更新後のタスク情報
*/
updateTask: async (id: number, task: Partial<Task>): Promise<Task> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
method: 'PUT',
headers: getHeaders(),
body: JSON.stringify(task),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.UPDATE_FAILED);
}
return response.json();
},
/**
* タスクを削除
* @param id 削除対象のタスクID
*/
deleteTask: async (id: number): Promise<void> => {
const response = await fetch(`${API_BASE_URL}/api/tasks/${id}`, {
method: 'DELETE',
headers: getHeaders(),
});
if (!response.ok) {
throw new Error(TASK_ERRORS.DELETE_FAILED);
}
},
};