163 lines
5.6 KiB
TypeScript
163 lines
5.6 KiB
TypeScript
|
|
import React, { useState, useEffect } from 'react';
|
||
|
|
import { supabase } from '../utils/supabase';
|
||
|
|
import CheckHistory from './CheckHistory';
|
||
|
|
|
||
|
|
const CheckPage: React.FC = () => {
|
||
|
|
const [input, setInput] = useState('');
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
const [currentId, setCurrentId] = useState<string | null>(null);
|
||
|
|
const [history, setHistory] = useState<any[]>([]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!currentId) return;
|
||
|
|
|
||
|
|
const channel = supabase
|
||
|
|
.channel('check_list_updates')
|
||
|
|
.on(
|
||
|
|
'postgres_changes',
|
||
|
|
{
|
||
|
|
event: 'UPDATE',
|
||
|
|
schema: 'public',
|
||
|
|
table: 'check_list',
|
||
|
|
filter: `id=eq.${currentId}`,
|
||
|
|
},
|
||
|
|
(payload) => {
|
||
|
|
if (payload.new.status === 'completed' || payload.new.status === 'failed') {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
.subscribe();
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
supabase.removeChannel(channel);
|
||
|
|
};
|
||
|
|
}, [currentId]);
|
||
|
|
|
||
|
|
// Fetch history on mount
|
||
|
|
useEffect(() => {
|
||
|
|
fetchHistory();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const fetchHistory = async () => {
|
||
|
|
try {
|
||
|
|
const response = await fetch('/api/check/history');
|
||
|
|
const data = await response.json();
|
||
|
|
if (data.success) {
|
||
|
|
setHistory(data.data);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to fetch history:', error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Subscribe to check_list changes for auto-refresh
|
||
|
|
useEffect(() => {
|
||
|
|
const channel = supabase
|
||
|
|
.channel('check_list_all')
|
||
|
|
.on(
|
||
|
|
'postgres_changes',
|
||
|
|
{
|
||
|
|
event: '*',
|
||
|
|
schema: 'public',
|
||
|
|
table: 'check_list',
|
||
|
|
},
|
||
|
|
() => {
|
||
|
|
fetchHistory();
|
||
|
|
}
|
||
|
|
)
|
||
|
|
.subscribe();
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
supabase.removeChannel(channel);
|
||
|
|
};
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
||
|
|
e.preventDefault();
|
||
|
|
if (!input.trim()) return;
|
||
|
|
|
||
|
|
setLoading(true);
|
||
|
|
setCurrentId(null);
|
||
|
|
|
||
|
|
try {
|
||
|
|
const response = await fetch('/api/check/submit', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ check_input: input }),
|
||
|
|
});
|
||
|
|
const data = await response.json();
|
||
|
|
if (data.success) {
|
||
|
|
setCurrentId(data.id);
|
||
|
|
// Refresh history after submission
|
||
|
|
setTimeout(() => fetchHistory(), 1000);
|
||
|
|
} else {
|
||
|
|
alert('Error: ' + data.error);
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error(error);
|
||
|
|
setLoading(false);
|
||
|
|
alert('Error submitting request');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleDeleteHistory = async (id: string) => {
|
||
|
|
try {
|
||
|
|
const response = await fetch(`/api/check/${id}`, {
|
||
|
|
method: 'DELETE',
|
||
|
|
});
|
||
|
|
const data = await response.json();
|
||
|
|
if (data.success) {
|
||
|
|
fetchHistory();
|
||
|
|
} else {
|
||
|
|
alert('Lỗi xoá: ' + data.error);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Failed to delete:', error);
|
||
|
|
alert('Lỗi xoá lịch sử');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||
|
|
<h1 className="text-2xl font-bold text-white mb-6">Check Upload Status (QC Subset)</h1>
|
||
|
|
|
||
|
|
<form onSubmit={handleSubmit} className="mb-8">
|
||
|
|
<div className="mb-4">
|
||
|
|
<label className="block text-sm font-medium text-slate-300 mb-2">
|
||
|
|
Input (GE ID LANG CHAP)
|
||
|
|
</label>
|
||
|
|
<textarea
|
||
|
|
value={input}
|
||
|
|
onChange={(e) => setInput(e.target.value)}
|
||
|
|
className="bg-slate-900/50 border border-slate-700 text-slate-100 text-sm rounded-lg focus:ring-indigo-500 focus:border-indigo-500 block w-full p-2.5 h-32 font-mono"
|
||
|
|
placeholder="4164 FR 3 419 DE 80"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<div className="flex gap-2">
|
||
|
|
<button
|
||
|
|
type="submit"
|
||
|
|
disabled={loading}
|
||
|
|
className="text-white bg-indigo-600 hover:bg-indigo-700 focus:ring-4 focus:outline-none focus:ring-indigo-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center disabled:opacity-50"
|
||
|
|
>
|
||
|
|
{loading ? 'Checking...' : 'Check'}
|
||
|
|
</button>
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => setInput('')}
|
||
|
|
disabled={loading || !input.trim()}
|
||
|
|
className="text-slate-300 bg-slate-700 hover:bg-slate-600 focus:ring-4 focus:outline-none focus:ring-slate-800 font-medium rounded-lg text-sm px-5 py-2.5 text-center disabled:opacity-50"
|
||
|
|
>
|
||
|
|
Clear
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
|
||
|
|
<CheckHistory history={history} onDelete={handleDeleteHistory} />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default CheckPage;
|