From 9e4ce0419a570e0109ccc4364c0d534c435f4804 Mon Sep 17 00:00:00 2001 From: Calvin Secrest Date: Fri, 5 Sep 2025 18:29:50 -0400 Subject: [PATCH] feat(chat-ui): add API helper module --- frontend/chat-ui/src/api.js | 36 ++++++++++++++++++++ frontend/chat-ui/src/components/InputBox.jsx | 12 ++----- frontend/chat-ui/src/components/Sidebar.jsx | 32 +++-------------- 3 files changed, 43 insertions(+), 37 deletions(-) create mode 100644 frontend/chat-ui/src/api.js diff --git a/frontend/chat-ui/src/api.js b/frontend/chat-ui/src/api.js new file mode 100644 index 0000000..e3d9a01 --- /dev/null +++ b/frontend/chat-ui/src/api.js @@ -0,0 +1,36 @@ +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; +const API_KEY = import.meta.env.VITE_API_KEY || ''; + +async function request(method, path, body) { + const headers = { 'x-api-key': API_KEY }; + const options = { method, headers }; + if (body !== undefined) { + headers['Content-Type'] = 'application/json'; + options.body = JSON.stringify(body); + } + + const res = await fetch(`${API_BASE_URL}${path}`, options); + const text = await res.text(); + let data = null; + if (text) { + try { + data = JSON.parse(text); + } catch (err) { + throw err; + } + } + if (!res.ok) { + const error = new Error(data?.message || `Request failed with status ${res.status}`); + error.status = res.status; + error.data = data; + throw error; + } + return data; +} + +export const get = (path) => request('GET', path); +export const post = (path, body) => request('POST', path, body); +export const put = (path, body) => request('PUT', path, body); +export const del = (path) => request('DELETE', path); + +export default { get, post, put, del }; diff --git a/frontend/chat-ui/src/components/InputBox.jsx b/frontend/chat-ui/src/components/InputBox.jsx index 01d9a2a..b7a95e3 100644 --- a/frontend/chat-ui/src/components/InputBox.jsx +++ b/frontend/chat-ui/src/components/InputBox.jsx @@ -1,7 +1,6 @@ import React, { useState, useRef } from 'react'; import { useStore } from '../store'; - -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; +import { post } from '../api'; export default function InputBox() { const { state, dispatch } = useStore(); @@ -29,14 +28,7 @@ export default function InputBox() { data: fileData, }; } - await fetch(`${API_BASE_URL}/messages/${state.currentConversation.id}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': import.meta.env.VITE_API_KEY, - }, - body: JSON.stringify(payload), - }); + await post(`/messages/${state.currentConversation.id}`, payload); dispatch({ type: 'ADD_MESSAGE', message: { id: Date.now().toString(), ...payload } }); setText(''); setFile(null); diff --git a/frontend/chat-ui/src/components/Sidebar.jsx b/frontend/chat-ui/src/components/Sidebar.jsx index 59900b0..5f640f9 100644 --- a/frontend/chat-ui/src/components/Sidebar.jsx +++ b/frontend/chat-ui/src/components/Sidebar.jsx @@ -1,7 +1,6 @@ import React, { useEffect } from 'react'; import { useStore } from '../store'; - -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || ''; +import { get, post } from '../api'; export default function Sidebar() { const { state, dispatch } = useStore(); @@ -9,10 +8,7 @@ export default function Sidebar() { useEffect(() => { const load = async () => { try { - const res = await fetch(`${API_BASE_URL}/conversations`, { - headers: { 'x-api-key': import.meta.env.VITE_API_KEY } - }); - const data = await res.json(); + const data = await get('/conversations'); dispatch({ type: 'SET_CONVERSATIONS', conversations: data }); } catch (e) { console.error(e); @@ -23,15 +19,7 @@ export default function Sidebar() { const newChat = async () => { try { - const res = await fetch(`${API_BASE_URL}/conversations`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': import.meta.env.VITE_API_KEY, - }, - body: JSON.stringify({}), - }); - const convo = await res.json(); + const convo = await post('/conversations', {}); dispatch({ type: 'SET_CURRENT_CONVERSATION', conversation: convo }); dispatch({ type: 'SET_MESSAGES', messages: [] }); } catch (e) { @@ -41,10 +29,7 @@ export default function Sidebar() { const openConversation = async (c) => { try { - const res = await fetch(`${API_BASE_URL}/messages/${c.id}`, { - headers: { 'x-api-key': import.meta.env.VITE_API_KEY }, - }); - const msgs = await res.json(); + const msgs = await get(`/messages/${c.id}`); dispatch({ type: 'SET_CURRENT_CONVERSATION', conversation: c }); dispatch({ type: 'SET_MESSAGES', messages: msgs }); } catch (e) { @@ -54,14 +39,7 @@ export default function Sidebar() { const togglePin = async (c) => { try { - await fetch(`${API_BASE_URL}/conversations/${c.id}/pin`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': import.meta.env.VITE_API_KEY, - }, - body: JSON.stringify({ pinned: !c.pinned }), - }); + await post(`/conversations/${c.id}/pin`, { pinned: !c.pinned }); const updated = { ...c, pinned: !c.pinned }; dispatch({ type: 'SET_CONVERSATIONS',