feat: list markdown files

This commit is contained in:
2025-10-17 17:43:18 +03:00
parent 0cc9367327
commit a256c3e16f
9 changed files with 153 additions and 81 deletions

View File

@@ -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>
);
}

View File

@@ -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;

View 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>
);
}

View 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>
);
}

View 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>
);
}