update
This commit is contained in:
+1
-1
@@ -36,7 +36,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
- /app/node_modules
|
- /app/node_modules
|
||||||
- /mnt/simrs/logs:/app/logs
|
- /mnt/simrs-dev/logs:/app/logs
|
||||||
|
|
||||||
# ─── Production ────────────────────────────
|
# ─── Production ────────────────────────────
|
||||||
prod:
|
prod:
|
||||||
|
|||||||
+1
-1
@@ -307,7 +307,7 @@ export function App() {
|
|||||||
<FilterToolbar
|
<FilterToolbar
|
||||||
filters={filters}
|
filters={filters}
|
||||||
backups={backups}
|
backups={backups}
|
||||||
onChange={(updated) => setFilters((prev) => ({ ...prev, ...updated }))}
|
onApply={(applied) => setFilters(applied)}
|
||||||
onReset={() =>
|
onReset={() =>
|
||||||
setFilters({
|
setFilters({
|
||||||
source: 'auto',
|
source: 'auto',
|
||||||
|
|||||||
@@ -1,33 +1,43 @@
|
|||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Database, Calendar, Clock, Layers, AlertCircle, Search, Filter, RotateCcw } from 'lucide-react';
|
import { Database, Calendar, Clock, Layers, AlertCircle, Search, Filter, RotateCcw } from 'lucide-react';
|
||||||
import { FilterState, BackupFile } from '../types';
|
import { FilterState, BackupFile } from '../types';
|
||||||
|
|
||||||
interface FilterToolbarProps {
|
interface FilterToolbarProps {
|
||||||
filters: FilterState;
|
filters: FilterState;
|
||||||
backups: BackupFile[];
|
backups: BackupFile[];
|
||||||
onChange: (updated: Partial<FilterState>) => void;
|
onApply: (applied: FilterState) => void;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups, onChange, onReset }) => {
|
export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups, onApply, onReset }) => {
|
||||||
|
const [localFilters, setLocalFilters] = useState<FilterState>(filters);
|
||||||
|
|
||||||
|
// Sync local filters if parent filters change externally (e.g., Reset or selecting backup ZIP file)
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalFilters(filters);
|
||||||
|
}, [filters]);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onApply({ ...localFilters, page: 1 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLocalChange = (updated: Partial<FilterState>) => {
|
||||||
|
setLocalFilters((prev) => ({ ...prev, ...updated }));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="glass-card p-4 rounded-xl mb-6">
|
<div className="glass-card p-4 rounded-xl mb-6">
|
||||||
<form
|
<form onSubmit={handleSubmit} className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-7 gap-3 items-end">
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
onChange({ page: 1 });
|
|
||||||
}}
|
|
||||||
className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-7 gap-3 items-end"
|
|
||||||
>
|
|
||||||
{/* Sumber Log */}
|
{/* Sumber Log */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-[11px] font-semibold text-slate-400 mb-1 flex items-center gap-1">
|
<label className="block text-[11px] font-semibold text-slate-400 mb-1 flex items-center gap-1">
|
||||||
<Database className="w-3.5 h-3.5 text-cyan-400" /> Sumber Log
|
<Database className="w-3.5 h-3.5 text-cyan-400" /> Sumber Log
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={filters.source}
|
value={localFilters.source}
|
||||||
onChange={(e) => onChange({ source: e.target.value, page: 1 })}
|
onChange={(e) => handleLocalChange({ source: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none cursor-pointer"
|
||||||
>
|
>
|
||||||
<option value="auto">⚡ Auto (Aktif + ZIP)</option>
|
<option value="auto">⚡ Auto (Aktif + ZIP)</option>
|
||||||
<option value="active">📁 Log Aktif</option>
|
<option value="active">📁 Log Aktif</option>
|
||||||
@@ -51,9 +61,9 @@ export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups,
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="date"
|
||||||
value={filters.date}
|
value={localFilters.date}
|
||||||
onChange={(e) => onChange({ date: e.target.value, page: 1 })}
|
onChange={(e) => handleLocalChange({ date: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -65,15 +75,15 @@ export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups,
|
|||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<input
|
<input
|
||||||
type="time"
|
type="time"
|
||||||
value={filters.time_start}
|
value={localFilters.time_start}
|
||||||
onChange={(e) => onChange({ time_start: e.target.value, page: 1 })}
|
onChange={(e) => handleLocalChange({ time_start: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-1.5 py-1.5 text-[11px] text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-1.5 py-1.5 text-[11px] text-white focus:border-cyan-500 outline-none"
|
||||||
/>
|
/>
|
||||||
<span className="text-slate-500 text-xs">-</span>
|
<span className="text-slate-500 text-xs">-</span>
|
||||||
<input
|
<input
|
||||||
type="time"
|
type="time"
|
||||||
value={filters.time_end}
|
value={localFilters.time_end}
|
||||||
onChange={(e) => onChange({ time_end: e.target.value, page: 1 })}
|
onChange={(e) => handleLocalChange({ time_end: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-1.5 py-1.5 text-[11px] text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-1.5 py-1.5 text-[11px] text-white focus:border-cyan-500 outline-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,9 +95,9 @@ export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups,
|
|||||||
<Layers className="w-3.5 h-3.5 text-cyan-400" /> Channel
|
<Layers className="w-3.5 h-3.5 text-cyan-400" /> Channel
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={filters.channel}
|
value={localFilters.channel}
|
||||||
onChange={(e) => onChange({ channel: e.target.value, page: 1 })}
|
onChange={(e) => handleLocalChange({ channel: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none cursor-pointer"
|
||||||
>
|
>
|
||||||
<option value="all">-- Semua Channel --</option>
|
<option value="all">-- Semua Channel --</option>
|
||||||
<option value="session">Session Lifecycle</option>
|
<option value="session">Session Lifecycle</option>
|
||||||
@@ -107,9 +117,9 @@ export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups,
|
|||||||
<AlertCircle className="w-3.5 h-3.5 text-cyan-400" /> Level
|
<AlertCircle className="w-3.5 h-3.5 text-cyan-400" /> Level
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={filters.level}
|
value={localFilters.level}
|
||||||
onChange={(e) => onChange({ level: e.target.value, page: 1 })}
|
onChange={(e) => handleLocalChange({ level: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2 py-1.5 text-xs text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2 py-1.5 text-xs text-white focus:border-cyan-500 outline-none cursor-pointer"
|
||||||
>
|
>
|
||||||
<option value="all">Semua</option>
|
<option value="all">Semua</option>
|
||||||
<option value="DEBUG">DEBUG</option>
|
<option value="DEBUG">DEBUG</option>
|
||||||
@@ -128,8 +138,8 @@ export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups,
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="NIP, URI, IP, Pesan..."
|
placeholder="NIP, URI, IP, Pesan..."
|
||||||
value={filters.search}
|
value={localFilters.search}
|
||||||
onChange={(e) => onChange({ search: e.target.value })}
|
onChange={(e) => handleLocalChange({ search: e.target.value })}
|
||||||
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none"
|
className="w-full bg-slate-950 border border-slate-800 rounded-lg px-2.5 py-1.5 text-xs text-white focus:border-cyan-500 outline-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,7 +148,7 @@ export const FilterToolbar: React.FC<FilterToolbarProps> = ({ filters, backups,
|
|||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex-1 py-1.5 px-3 rounded-lg bg-teal-600 hover:bg-teal-500 text-white font-semibold text-xs flex items-center justify-center gap-1 transition-colors cursor-pointer"
|
className="flex-1 py-1.5 px-3 rounded-lg bg-teal-600 hover:bg-teal-500 text-white font-semibold text-xs flex items-center justify-center gap-1 transition-colors cursor-pointer shadow-lg shadow-teal-900/30"
|
||||||
>
|
>
|
||||||
<Filter className="w-3.5 h-3.5" />
|
<Filter className="w-3.5 h-3.5" />
|
||||||
Filter
|
Filter
|
||||||
|
|||||||
Reference in New Issue
Block a user