access logs + audio

This commit is contained in:
hamid zarghami
2025-09-02 17:00:37 +03:30
parent 860f7c446f
commit 0e16af827f
25 changed files with 3394 additions and 83 deletions
+219
View File
@@ -0,0 +1,219 @@
import React, { useState } from "react";
import {
DocumentText,
DocumentDownload,
Refresh,
Activity,
InfoCircle
} from "iconsax-react";
import Button from "../../components/Button";
import Pagination from "../../components/Pagination";
import TitleLine from "../../components/TitleLine";
import PageLoading from "../../components/PageLoading";
// import Error from "../../components/Error"; // Using custom error display
import FilterPanel from "./components/FilterPanel";
import AccessLogsTable from "./components/AccessLogsTable";
import LogDetailsModal from "./components/LogDetailsModal";
import { useAccessLogs, useAccessLogDetail } from "./hooks/useAccessLogs";
import { AccessLog } from "./types/AccessLogTypes";
const AccessLogsList: React.FC = () => {
const {
logs,
loading,
error,
total,
filters,
updateFilters,
resetFilters,
exportLogs,
refetch,
} = useAccessLogs();
const { log: selectedLog, fetchLog, clearLog } = useAccessLogDetail();
const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false);
const handleViewDetails = async (log: AccessLog) => {
await fetchLog(log.id);
setIsDetailsModalOpen(true);
};
const handleCloseDetailsModal = () => {
setIsDetailsModalOpen(false);
clearLog();
};
const handleSort = (field: string, order: "ASC" | "DESC") => {
updateFilters({ sortBy: field, sortOrder: order });
};
const handlePageChange = (page: number) => {
updateFilters({ page });
};
const handleExport = async () => {
try {
await exportLogs();
} catch (error) {
console.error("خطا در خروجی گرفتن:", error);
}
};
if (error) {
return (
<div className="p-6">
<TitleLine title="لاگ‌های دسترسی" />
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<div className="flex items-center justify-between">
<p className="text-red-800">{error}</p>
<Button
onClick={refetch}
className="bg-red-600 text-white hover:bg-red-700"
>
تلاش مجدد
</Button>
</div>
</div>
</div>
);
}
return (
<div className="p-6 space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<TitleLine title="لاگ‌های دسترسی" />
<p className="text-gray-600 mt-2">
مشاهده و مدیریت تمام فعالیتهای سیستم
</p>
</div>
<div className="flex items-center gap-3">
<Button
onClick={refetch}
disabled={loading}
className="flex items-center gap-2 border border-gray-300 bg-white text-gray-700 hover:bg-gray-50"
>
<Refresh size={16} className={loading ? "animate-spin" : ""} />
بروزرسانی
</Button>
<Button
onClick={handleExport}
disabled={loading || !logs || logs.length === 0}
className="flex items-center gap-2 border border-gray-300 bg-white text-gray-700 hover:bg-gray-50"
>
<DocumentDownload size={16} />
خروجی CSV
</Button>
</div>
</div>
{/* Stats Summary */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div className="flex items-center gap-3">
<div className="flex-shrink-0 p-2 bg-blue-100 rounded-lg">
<DocumentText size={20} className="text-blue-600" />
</div>
<div>
<p className="text-sm font-medium text-gray-600">کل لاگها</p>
<p className="text-xl font-bold text-gray-900">
{(total || 0).toLocaleString('fa-IR')}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div className="flex items-center gap-3">
<div className="flex-shrink-0 p-2 bg-green-100 rounded-lg">
<Activity size={20} className="text-green-600" />
</div>
<div>
<p className="text-sm font-medium text-gray-600">صفحه فعلی</p>
<p className="text-xl font-bold text-gray-900">
{filters.page || 1}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
<div className="flex items-center gap-3">
<div className="flex-shrink-0 p-2 bg-purple-100 rounded-lg">
<InfoCircle size={20} className="text-purple-600" />
</div>
<div>
<p className="text-sm font-medium text-gray-600">نمایش در صفحه</p>
<p className="text-xl font-bold text-gray-900">
{filters.limit || 20}
</p>
</div>
</div>
</div>
</div>
{/* Filter Panel */}
<FilterPanel
filters={filters}
onFiltersChange={updateFilters}
onReset={resetFilters}
loading={loading}
/>
{/* Loading State */}
{loading && <PageLoading />}
{/* Table */}
{!loading && (
<>
<AccessLogsTable
logs={logs}
loading={loading}
onViewDetails={handleViewDetails}
onSort={handleSort}
sortBy={filters.sortBy}
sortOrder={filters.sortOrder}
/>
{/* Pagination */}
{total > (filters.limit || 20) && (
<div className="flex justify-center">
<Pagination
currentPage={filters.page || 1}
totalPages={Math.ceil(total / (filters.limit || 20))}
onPageChange={handlePageChange}
/>
</div>
)}
</>
)}
{/* Details Modal */}
<LogDetailsModal
log={selectedLog}
isOpen={isDetailsModalOpen}
onClose={handleCloseDetailsModal}
/>
{/* Empty State Info */}
{!loading && (!logs || logs.length === 0) && !error && (
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<div className="flex items-center gap-3">
<InfoCircle size={20} className="text-blue-600" />
<div>
<h3 className="text-sm font-medium text-blue-900">راهنما</h3>
<p className="text-sm text-blue-700 mt-1">
از فیلترهای بالا برای جستجوی دقیقتر استفاده کنید. میتوانید بر اساس نوع عملیات، کاربر، تاریخ و سایر معیارها جستجو کنید.
</p>
</div>
</div>
</div>
)}
</div>
);
};
export default AccessLogsList;