responsive and new date time picker
All checks were successful
Build and Release / release (push) Successful in 28s

This commit is contained in:
2026-05-02 16:04:45 +07:00
parent ff70fc78f5
commit 7ec7fda0b4
9 changed files with 462 additions and 159 deletions

View File

@@ -21,6 +21,8 @@ import { useRouter } from "next/navigation";
import { LIMIT_ITEM_TABLE } from "../../../../../../constant";
import { ProjectsResponse } from "@/interface/project";
import CustomDateRangePicker from "@/components/common/CustomDateRangePicker";
const formatDateTimeToISO = (
dateStr: string,
timeStr: string,
@@ -41,7 +43,6 @@ export default function ProjectsPage(_props: {
LIMIT_ITEM_TABLE.toString(),
);
// Filters state
const [searchTerm, setSearchTerm] = useState<string>("");
const [statusFilter, setStatusFilter] = useState<string>("");
const [userIdsFilter, setUserIdsFilter] = useState<string>("");
@@ -50,6 +51,8 @@ export default function ProjectsPage(_props: {
const [toDate, setToDate] = useState<string>("");
const [toTime, setToTime] = useState<string>("");
const [resetKey, setResetKey] = useState<number>(0);
const [debouncedParams, setDebouncedParams] = useState({
search: "",
limit: LIMIT_ITEM_TABLE,
@@ -77,6 +80,14 @@ export default function ProjectsPage(_props: {
setToDate("");
setToTime("");
setPage(1);
setResetKey(prev => prev + 1);
};
const handleDateFilterChange = (startD: string, endD: string, startT: string, endT: string) => {
setFromDate(startD);
setToDate(endD);
setFromTime(startT);
setToTime(endT);
};
useEffect(() => {
@@ -122,7 +133,7 @@ export default function ProjectsPage(_props: {
if (createdFrom) payload.created_from = createdFrom;
const createdTo = formatDateTimeToISO(debouncedParams.toDate, debouncedParams.toTime, true);
if (createdTo) payload.created_to = createdTo;
const response = await getProjects(payload);
if (response?.status) {
@@ -174,23 +185,38 @@ export default function ProjectsPage(_props: {
}
>
<div className="grid grid-cols-1 gap-4 mb-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<input type="text" placeholder="Tên dự án, ID..." value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
<select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)} className="w-full px-3 py-2 bg-white dark:bg-gray-900 border rounded-lg cursor-pointer outline-none focus:border-brand-500">
<option value="">Tất cả trạng thái</option>
<option value="PUBLIC">PUBLIC</option>
<option value="PRIVATE">PRIVATE</option>
<option value="ARCHIVE">ARCHIVE</option>
</select>
<input type="text" placeholder="IDs người dùng (cách nhau bởi dấu phẩy)" value={userIdsFilter} onChange={(e) => setUserIdsFilter(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
<div className="flex gap-2">
<input type="date" value={fromDate} onChange={(e) => setFromDate(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
<input type="time" value={fromTime} onChange={(e) => setFromTime(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
<div>
<label className="block mb-2 text-sm font-medium">Tìm kiếm</label>
<input type="text" placeholder="Tên dự án, ID..." value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
</div>
<div className="flex gap-2">
<input type="date" value={toDate} onChange={(e) => setToDate(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
<input type="time" value={toTime} onChange={(e) => setToTime(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
<div>
<label className="block mb-2 text-sm font-medium">Trạng thái</label>
<select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)} className="w-full px-3 py-2 bg-white dark:bg-gray-900 border rounded-lg cursor-pointer outline-none focus:border-brand-500">
<option value="">Tất cả trạng thái</option>
<option value="PUBLIC">PUBLIC</option>
<option value="PRIVATE">PRIVATE</option>
<option value="ARCHIVE">ARCHIVE</option>
</select>
</div>
<div>
<label className="block mb-2 text-sm font-medium">ID người dùng</label>
<input type="text" placeholder="IDs (cách nhau bởi dấu phẩy)" value={userIdsFilter} onChange={(e) => setUserIdsFilter(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
</div>
<div>
<label className="block mb-2 text-sm font-medium">Thời gian</label>
<CustomDateRangePicker
key={resetKey}
onFilterChange={handleDateFilterChange}
/>
</div>
<div>
<label className="block mb-2 text-sm font-medium">Hiển thị (Limit)</label>
<input type="number" value={limitInput} onChange={(e) => setLimitInput(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
</div>
<input type="number" value={limitInput} onChange={(e) => setLimitInput(e.target.value)} className="w-full px-3 py-2 border rounded-lg dark:bg-gray-800 dark:border-gray-700 outline-none focus:border-brand-500" />
</div>
</ComponentCard>
@@ -209,7 +235,6 @@ export default function ProjectsPage(_props: {
onSort={handleSort}
sortBy={sortBy}
sortOrder={sortOrder}
onViewDetails={handleViewDetails}
/>
</div>
@@ -231,4 +256,4 @@ export default function ProjectsPage(_props: {
</div>
</div>
);
}
}