feat: list markdown files
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
import { useRef, type FormEvent } from "react";
|
||||
|
||||
export function APITester() {
|
||||
const responseInputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
const testEndpoint = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
try {
|
||||
const form = e.currentTarget;
|
||||
const formData = new FormData(form);
|
||||
const endpoint = formData.get("endpoint") as string;
|
||||
const url = new URL(endpoint, "http://localhost:8912");
|
||||
const method = formData.get("method") as string;
|
||||
const res = await fetch(url, { method });
|
||||
|
||||
const data = await res.json();
|
||||
responseInputRef.current!.value = JSON.stringify(data, null, 2);
|
||||
} catch (error) {
|
||||
responseInputRef.current!.value = String(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mt-8 mx-auto w-full max-w-2xl text-left flex flex-col gap-4">
|
||||
<form
|
||||
onSubmit={testEndpoint}
|
||||
className="flex items-center gap-2 bg-[#1a1a1a] p-3 rounded-xl font-mono border-2 border-[#fbf0df] transition-colors duration-300 focus-within:border-[#f3d5a3] w-full"
|
||||
>
|
||||
<select
|
||||
name="method"
|
||||
className="bg-[#fbf0df] text-[#1a1a1a] py-1.5 px-3 rounded-lg font-bold text-sm min-w-[0px] appearance-none cursor-pointer hover:bg-[#f3d5a3] transition-colors duration-100"
|
||||
>
|
||||
<option value="GET" className="py-1">
|
||||
GET
|
||||
</option>
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
name="endpoint"
|
||||
defaultValue="/api/v1/ping"
|
||||
className="w-full flex-1 bg-transparent border-0 text-[#fbf0df] font-mono text-base py-1.5 px-2 outline-none focus:text-white placeholder-[#fbf0df]/40"
|
||||
placeholder="/api/v1/ping"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-[#fbf0df] text-[#1a1a1a] border-0 px-5 py-1.5 rounded-lg font-bold transition-all duration-100 hover:bg-[#f3d5a3] hover:-translate-y-px cursor-pointer whitespace-nowrap"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</form>
|
||||
<textarea
|
||||
ref={responseInputRef}
|
||||
readOnly
|
||||
placeholder="Response will appear here..."
|
||||
className="w-full min-h-[140px] bg-[#1a1a1a] border-2 border-[#fbf0df] rounded-xl p-3 text-[#fbf0df] font-mono resize-y focus:border-[#f3d5a3] placeholder-[#fbf0df]/40"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,10 @@
|
||||
import { APITester } from "./APITester";
|
||||
// import { FilesList } from "./components/FilesList";
|
||||
// import { FileContent } from "./components/FileContent";
|
||||
import { Root } from "./components/Root";
|
||||
import "./index.css";
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<div className="max-w-7xl mx-auto p-8 text-center relative z-10">
|
||||
<div className="flex justify-center items-center gap-8 mb-8">
|
||||
</div>
|
||||
|
||||
<h1 className="text-5xl font-bold my-4 leading-tight">React + Gin</h1>
|
||||
<APITester />
|
||||
</div>
|
||||
);
|
||||
return (<Root/ >);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
35
frontend/src/components/FileContent.tsx
Normal file
35
frontend/src/components/FileContent.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
export function FileContent({ filePath }) {
|
||||
const [data, setData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch("http://localhost:8912/api/v1/files"); // Replace with your API endpoint
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
setData(result);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []); // Empty dependency array ensures this effect runs only once on mount
|
||||
|
||||
if (loading) return <p>Loading data...</p>;
|
||||
if (error) return <p>Error: {error.message}</p>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<pre>{filePath}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
41
frontend/src/components/FilesList.tsx
Normal file
41
frontend/src/components/FilesList.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
export function FilesList({ handleFileClick }) {
|
||||
const [data, setData] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await fetch("http://localhost:8912/api/v1/files"); // Replace with your API endpoint
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
setData(result.data);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, []); // Empty dependency array ensures this effect runs only once on mount
|
||||
|
||||
if (loading) return <p>Loading data...</p>;
|
||||
if (error) return <p>Error: {error.message}</p>;
|
||||
|
||||
const onClick = (event) => {
|
||||
handleFileClick(event.target.value); // Call the parent's callback with the new value
|
||||
};
|
||||
|
||||
const listFiles = data.map(filePath => <li onClick={onClick} className="cursor-pointer">{filePath}</li>);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{listFiles}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
24
frontend/src/components/Root.tsx
Normal file
24
frontend/src/components/Root.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { FilesList } from "./FilesList";
|
||||
import { FileContent } from "./FileContent";
|
||||
|
||||
export function Root() {
|
||||
const [filePath, setFilePath] = useState(''); // State to be shared
|
||||
|
||||
const handleFileClick = () => {
|
||||
setFilePath('1000')
|
||||
alert("Button clicked!");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div className="flex-1">
|
||||
<FilesList onClick={handleFileClick} />
|
||||
</div>
|
||||
<div className="flex-2">
|
||||
<FileContent filePath={filePath} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user