|
@@ -1,6 +1,7 @@
|
|
|
"use client";
|
|
"use client";
|
|
|
|
|
|
|
|
import React from "react";
|
|
import React from "react";
|
|
|
|
|
+import { AlertCircleIcon } from "lucide-react";
|
|
|
|
|
|
|
|
import { SEARCH_SCOPE } from "@/lib/frontend/search/urlState";
|
|
import { SEARCH_SCOPE } from "@/lib/frontend/search/urlState";
|
|
|
|
|
|
|
@@ -13,7 +14,6 @@ import SearchDateRangePicker from "@/components/search/form/SearchDateRangePicke
|
|
|
import SearchDateFilterChip from "@/components/search/form/SearchDateFilterChip";
|
|
import SearchDateFilterChip from "@/components/search/form/SearchDateFilterChip";
|
|
|
|
|
|
|
|
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
|
|
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
|
|
|
-import { AlertCircleIcon } from "lucide-react";
|
|
|
|
|
|
|
|
|
|
export default function SearchForm({
|
|
export default function SearchForm({
|
|
|
branch,
|
|
branch,
|
|
@@ -40,11 +40,7 @@ export default function SearchForm({
|
|
|
}) {
|
|
}) {
|
|
|
const canSearch = typeof qDraft === "string" && qDraft.trim().length > 0;
|
|
const canSearch = typeof qDraft === "string" && qDraft.trim().length > 0;
|
|
|
|
|
|
|
|
- // RHL-038 UI behavior:
|
|
|
|
|
- // - Single scope shows an extra combobox (branch picker).
|
|
|
|
|
- // - Multi/All does not.
|
|
|
|
|
- // - We animate the combobox container (max-width/max-height/opacity) so the
|
|
|
|
|
- // query block (input + search button) grows/shrinks smoothly.
|
|
|
|
|
|
|
+ // SINGLE scope (admin/dev only) shows an extra branch combobox.
|
|
|
const showSingleBranchCombobox = Boolean(
|
|
const showSingleBranchCombobox = Boolean(
|
|
|
isAdminDev && scope === SEARCH_SCOPE.SINGLE
|
|
isAdminDev && scope === SEARCH_SCOPE.SINGLE
|
|
|
);
|
|
);
|
|
@@ -61,72 +57,82 @@ export default function SearchForm({
|
|
|
}}
|
|
}}
|
|
|
className="space-y-4"
|
|
className="space-y-4"
|
|
|
>
|
|
>
|
|
|
- {/*
|
|
|
|
|
- Layout goal:
|
|
|
|
|
- - Desktop: everything on one line (scope + optional single combobox | query | date range | limit)
|
|
|
|
|
- - Mobile: stack to avoid horizontal overflow
|
|
|
|
|
- */}
|
|
|
|
|
- <div className="flex flex-col gap-3 lg:flex-row items-start lg:flex-nowrap">
|
|
|
|
|
- {/* Left block: scope + (animated) single-branch combobox */}
|
|
|
|
|
- {isAdminDev ? (
|
|
|
|
|
- <div className="flex items-end shrink-0">
|
|
|
|
|
- <SearchScopeSelect
|
|
|
|
|
- branch={branch}
|
|
|
|
|
- scope={scope}
|
|
|
|
|
- onScopeChange={onScopeChange}
|
|
|
|
|
- isSubmitting={isSubmitting}
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ {/* Row 1: query full-width */}
|
|
|
|
|
+ <div className="w-full">
|
|
|
|
|
+ <SearchQueryBox
|
|
|
|
|
+ qDraft={qDraft}
|
|
|
|
|
+ onQDraftChange={onQDraftChange}
|
|
|
|
|
+ onSubmit={onSubmit}
|
|
|
|
|
+ currentQuery={currentQuery}
|
|
|
|
|
+ isSubmitting={isSubmitting}
|
|
|
|
|
+ canSearch={canSearch}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- {/*
|
|
|
|
|
- Animated presence (no mount/unmount):
|
|
|
|
|
- - When hidden: max-w-0/max-h-0 + opacity-0 so it takes no space.
|
|
|
|
|
- - When shown: expands to a reasonable max size.
|
|
|
|
|
- This animation drives the smooth resize of the query input block.
|
|
|
|
|
- */}
|
|
|
|
|
- <div
|
|
|
|
|
- className={[
|
|
|
|
|
- "overflow-hidden transition-all duration-200 ease-in-out",
|
|
|
|
|
- showSingleBranchCombobox
|
|
|
|
|
- ? "ml-2 max-w-44 max-h-24 opacity-100"
|
|
|
|
|
- : "ml-0 max-w-0 max-h-0 opacity-0 pointer-events-none",
|
|
|
|
|
- ].join(" ")}
|
|
|
|
|
- aria-hidden={showSingleBranchCombobox ? "false" : "true"}
|
|
|
|
|
- >
|
|
|
|
|
- <SearchSingleBranchCombobox
|
|
|
|
|
|
|
+ {/* Row 2: left controls + spacer + limit on the right */}
|
|
|
|
|
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
|
|
|
|
+ {/* Left group: scope (+ optional single branch combobox) + date range */}
|
|
|
|
|
+ <div className="flex flex-col gap-3 sm:flex-row sm:items-end">
|
|
|
|
|
+ {isAdminDev ? (
|
|
|
|
|
+ <div className="flex items-end gap-3">
|
|
|
|
|
+ <SearchScopeSelect
|
|
|
branch={branch}
|
|
branch={branch}
|
|
|
- branchesStatus={branchesStatus}
|
|
|
|
|
- availableBranches={availableBranches}
|
|
|
|
|
- onSingleBranchChange={onSingleBranchChange}
|
|
|
|
|
- // Disable when hidden so it can't be focused/clicked.
|
|
|
|
|
- isSubmitting={isSubmitting || !showSingleBranchCombobox}
|
|
|
|
|
|
|
+ scope={scope}
|
|
|
|
|
+ onScopeChange={onScopeChange}
|
|
|
|
|
+ isSubmitting={isSubmitting}
|
|
|
/>
|
|
/>
|
|
|
|
|
+
|
|
|
|
|
+ {/* Animated presence (kept for smoothness when SINGLE shows the extra combobox) */}
|
|
|
|
|
+ <div
|
|
|
|
|
+ className={[
|
|
|
|
|
+ "overflow-hidden transition-all duration-200 ease-in-out",
|
|
|
|
|
+ showSingleBranchCombobox
|
|
|
|
|
+ ? "max-w-44 max-h-24 opacity-100"
|
|
|
|
|
+ : "max-w-0 max-h-0 opacity-0 pointer-events-none",
|
|
|
|
|
+ ].join(" ")}
|
|
|
|
|
+ aria-hidden={showSingleBranchCombobox ? "false" : "true"}
|
|
|
|
|
+ >
|
|
|
|
|
+ <SearchSingleBranchCombobox
|
|
|
|
|
+ branch={branch}
|
|
|
|
|
+ branchesStatus={branchesStatus}
|
|
|
|
|
+ availableBranches={availableBranches}
|
|
|
|
|
+ onSingleBranchChange={onSingleBranchChange}
|
|
|
|
|
+ isSubmitting={isSubmitting || !showSingleBranchCombobox}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
- ) : null}
|
|
|
|
|
-
|
|
|
|
|
- {/* Middle block: query must take the most space */}
|
|
|
|
|
- <div className="flex-1 min-w-0">
|
|
|
|
|
- <SearchQueryBox
|
|
|
|
|
- qDraft={qDraft}
|
|
|
|
|
- onQDraftChange={onQDraftChange}
|
|
|
|
|
- onSubmit={onSubmit}
|
|
|
|
|
- currentQuery={currentQuery}
|
|
|
|
|
- isSubmitting={isSubmitting}
|
|
|
|
|
- canSearch={canSearch}
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ ) : null}
|
|
|
|
|
|
|
|
- {/* Date range picker */}
|
|
|
|
|
- <div className="shrink-0">
|
|
|
|
|
- <SearchDateRangePicker
|
|
|
|
|
- from={from}
|
|
|
|
|
- to={to}
|
|
|
|
|
- onDateRangeChange={onDateRangeChange}
|
|
|
|
|
- isSubmitting={isSubmitting}
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <div className="shrink-0 flex items-end gap-4">
|
|
|
|
|
+ <SearchDateRangePicker
|
|
|
|
|
+ from={from}
|
|
|
|
|
+ to={to}
|
|
|
|
|
+ onDateRangeChange={onDateRangeChange}
|
|
|
|
|
+ isSubmitting={isSubmitting}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ {/* Active date filter chip (quick clear) */}
|
|
|
|
|
+ {hasDateFilter ? (
|
|
|
|
|
+ <div className="flex flex-wrap items-center gap-2 mb-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}
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- {/* Right block: limit stays “content-sized” */}
|
|
|
|
|
|
|
+ {/* Right group: limit aligned right */}
|
|
|
<div className="shrink-0">
|
|
<div className="shrink-0">
|
|
|
<SearchLimitSelect
|
|
<SearchLimitSelect
|
|
|
limit={limit}
|
|
limit={limit}
|
|
@@ -146,23 +152,6 @@ export default function SearchForm({
|
|
|
</Alert>
|
|
</Alert>
|
|
|
) : null}
|
|
) : 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
|