|
@@ -9,6 +9,11 @@ import SearchScopeSelect from "@/components/search/form/SearchScopeSelect";
|
|
|
import SearchLimitSelect from "@/components/search/form/SearchLimitSelect";
|
|
import SearchLimitSelect from "@/components/search/form/SearchLimitSelect";
|
|
|
import SearchSingleBranchCombobox from "@/components/search/form/SearchSingleBranchCombobox";
|
|
import SearchSingleBranchCombobox from "@/components/search/form/SearchSingleBranchCombobox";
|
|
|
import SearchMultiBranchPicker from "@/components/search/form/SearchMultiBranchPicker";
|
|
import SearchMultiBranchPicker from "@/components/search/form/SearchMultiBranchPicker";
|
|
|
|
|
+import SearchDateRangePicker from "@/components/search/form/SearchDateRangePicker";
|
|
|
|
|
+import SearchDateFilterChip from "@/components/search/form/SearchDateFilterChip";
|
|
|
|
|
+
|
|
|
|
|
+import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
|
|
|
|
|
+import { AlertCircleIcon } from "lucide-react";
|
|
|
|
|
|
|
|
export default function SearchForm({
|
|
export default function SearchForm({
|
|
|
branch,
|
|
branch,
|
|
@@ -28,6 +33,10 @@ export default function SearchForm({
|
|
|
onClearAllBranches,
|
|
onClearAllBranches,
|
|
|
limit,
|
|
limit,
|
|
|
onLimitChange,
|
|
onLimitChange,
|
|
|
|
|
+ from,
|
|
|
|
|
+ to,
|
|
|
|
|
+ onDateRangeChange,
|
|
|
|
|
+ validationError,
|
|
|
}) {
|
|
}) {
|
|
|
const canSearch = typeof qDraft === "string" && qDraft.trim().length > 0;
|
|
const canSearch = typeof qDraft === "string" && qDraft.trim().length > 0;
|
|
|
|
|
|
|
@@ -40,6 +49,8 @@ export default function SearchForm({
|
|
|
isAdminDev && scope === SEARCH_SCOPE.SINGLE
|
|
isAdminDev && scope === SEARCH_SCOPE.SINGLE
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
|
|
+ const hasDateFilter = Boolean(from || to);
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div className="space-y-4">
|
|
<div className="space-y-4">
|
|
|
<form
|
|
<form
|
|
@@ -52,7 +63,7 @@ export default function SearchForm({
|
|
|
>
|
|
>
|
|
|
{/*
|
|
{/*
|
|
|
Layout goal:
|
|
Layout goal:
|
|
|
- - Desktop: everything on one line (scope + optional single combobox | query | limit)
|
|
|
|
|
|
|
+ - Desktop: everything on one line (scope + optional single combobox | query | date range | limit)
|
|
|
- Mobile: stack to avoid horizontal overflow
|
|
- Mobile: stack to avoid horizontal overflow
|
|
|
*/}
|
|
*/}
|
|
|
<div className="flex flex-col gap-3 lg:flex-row items-start lg:flex-nowrap">
|
|
<div className="flex flex-col gap-3 lg:flex-row items-start lg:flex-nowrap">
|
|
@@ -105,6 +116,16 @@ export default function SearchForm({
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ {/* Date range picker */}
|
|
|
|
|
+ <div className="shrink-0">
|
|
|
|
|
+ <SearchDateRangePicker
|
|
|
|
|
+ from={from}
|
|
|
|
|
+ to={to}
|
|
|
|
|
+ onDateRangeChange={onDateRangeChange}
|
|
|
|
|
+ isSubmitting={isSubmitting}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
{/* Right block: limit stays “content-sized” */}
|
|
{/* Right block: limit stays “content-sized” */}
|
|
|
<div className="shrink-0">
|
|
<div className="shrink-0">
|
|
|
<SearchLimitSelect
|
|
<SearchLimitSelect
|
|
@@ -116,6 +137,32 @@ export default function SearchForm({
|
|
|
</div>
|
|
</div>
|
|
|
</form>
|
|
</form>
|
|
|
|
|
|
|
|
|
|
+ {/* Validation feedback belongs near the inputs (not in results). */}
|
|
|
|
|
+ {validationError ? (
|
|
|
|
|
+ <Alert variant="destructive">
|
|
|
|
|
+ <AlertCircleIcon />
|
|
|
|
|
+ <AlertTitle>{validationError.title}</AlertTitle>
|
|
|
|
|
+ <AlertDescription>{validationError.description}</AlertDescription>
|
|
|
|
|
+ </Alert>
|
|
|
|
|
+ ) : null}
|
|
|
|
|
+
|
|
|
|
|
+ {/* Active date filter chip (quick clear) */}
|
|
|
|
|
+ {hasDateFilter ? (
|
|
|
|
|
+ <div className="flex flex-wrap items-center gap-2">
|
|
|
|
|
+ <span className="text-xs text-muted-foreground">Aktive Filter:</span>
|
|
|
|
|
+
|
|
|
|
|
+ <SearchDateFilterChip
|
|
|
|
|
+ from={from}
|
|
|
|
|
+ to={to}
|
|
|
|
|
+ isSubmitting={isSubmitting}
|
|
|
|
|
+ onClear={() => {
|
|
|
|
|
+ if (typeof onDateRangeChange !== "function") return;
|
|
|
|
|
+ onDateRangeChange({ from: null, to: null });
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ) : null}
|
|
|
|
|
+
|
|
|
{/* Multi scope branch picker remains below */}
|
|
{/* Multi scope branch picker remains below */}
|
|
|
{isAdminDev && scope === SEARCH_SCOPE.MULTI ? (
|
|
{isAdminDev && scope === SEARCH_SCOPE.MULTI ? (
|
|
|
<SearchMultiBranchPicker
|
|
<SearchMultiBranchPicker
|