Commit 9ae40825 authored by BangNSK's avatar BangNSK

feat(frontend): connect to deploy api and implement users/logs pages

- Update API_BASE_URL to http://171.247.68.96:4011/api/v1
- Implement Users page: CRUD (list, create, update, delete) with pagination and search
- Implement Logs page: audit logs with userId/action filter, expandable details, pagination
- Support URL_LIST mode in Dashboard create job form (textarea for batch URLs)
- Fix unused mimeType param in JobDetail to pass TypeScript strict check
parent dd2ea5d4
......@@ -30,6 +30,7 @@ export const Dashboard: React.FC = () => {
const [mode, setMode] = useState<'SCRAPE' | 'CRAWL' | 'SITEMAP' | 'URL_LIST'>('SCRAPE');
const [maxPages, setMaxPages] = useState<number>(20);
const [maxDepth, setMaxDepth] = useState<number>(1);
const [urlsList, setUrlsList] = useState(''); // URL_LIST mode: danh sách URL mỗi dòng 1 URL
const [createError, setCreateError] = useState('');
const [creating, setCreating] = useState(false);
......@@ -39,7 +40,7 @@ export const Dashboard: React.FC = () => {
try {
const response = await api.get('/crawl-jobs');
if (response.data.success) {
setJobs(response.data.data.items || []);
setJobs(response.data.data.jobs || response.data.data.items || []);
}
} catch (error) {
console.error('Error fetching jobs', error);
......@@ -81,11 +82,16 @@ export const Dashboard: React.FC = () => {
setCreating(true);
try {
const isUrlListMode = mode === 'URL_LIST';
const parsedUrls = isUrlListMode
? urlsList.split('\n').map((u) => u.trim()).filter(Boolean)
: undefined;
const response = await api.post('/crawl-jobs', {
startUrl,
...(isUrlListMode ? { urls: parsedUrls } : { startUrl }),
mode,
maxPages: Number(maxPages),
maxDepth: Number(maxDepth),
maxDepth: isUrlListMode ? undefined : Number(maxDepth),
});
if (response.data.success) {
......@@ -95,6 +101,7 @@ export const Dashboard: React.FC = () => {
setMode('SCRAPE');
setMaxPages(20);
setMaxDepth(1);
setUrlsList('');
fetchJobs(true);
}
} catch (error: any) {
......@@ -270,6 +277,24 @@ export const Dashboard: React.FC = () => {
)}
<form onSubmit={handleCreateJob} className="space-y-4">
{mode === 'URL_LIST' ? (
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Danh sách URL (mỗi dòng 1 URL) *
</label>
<textarea
required
rows={5}
placeholder={`https://example.com/page1\nhttps://example.com/page2\nhttps://example.com/page3`}
value={urlsList}
onChange={(e) => setUrlsList(e.target.value)}
className="block w-full rounded-lg border border-slate-300 bg-white py-2 px-3 text-slate-900 placeholder-slate-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm font-mono text-xs"
/>
<p className="text-xs text-slate-400 mt-1">
Tối đa 1000 URL. Mỗi dòng một URL hợp lệ.
</p>
</div>
) : (
<div>
<label className="block text-sm font-medium text-slate-700 mb-1">
Đường dẫn bắt đầu (Start URL) *
......@@ -283,6 +308,7 @@ export const Dashboard: React.FC = () => {
className="block w-full rounded-lg border border-slate-300 bg-white py-2 px-3 text-slate-900 placeholder-slate-400 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm"
/>
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
......
......@@ -158,7 +158,7 @@ export const JobDetail: React.FC = () => {
}
};
const handleDownload = async (exportId: string, fileName: string, mimeType: string) => {
const handleDownload = async (exportId: string, fileName: string, _mimeType: string) => {
try {
console.log(`Downloading export ${exportId}, requesting blob...`);
const res = await api.get(`/exports/${exportId}/download`, { responseType: 'blob' });
......
This diff is collapsed.
This diff is collapsed.
import axios from 'axios';
const API_BASE_URL = 'http://localhost:3000/api/v1';
const API_BASE_URL = 'http://171.247.68.96:4011/api/v1';
export const api = axios.create({
baseURL: API_BASE_URL,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment