# Responsive Filter System Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build a shared, token-based, responsive dashboard filter system and update every existing `DatePicker` consumer without changing API or URL contracts.

**Architecture:** Keep feature-specific filter state and query serialization in each existing panel. Add presentation-only filter primitives for the responsive surface, grid, field, and actions; update the shared `DatePicker` once so all consumers inherit the same shadcn styling and behavior. Use the existing responsive hook to render one inline surface at `md` and above or one shadcn Sheet below `md`.

**Tech Stack:** Next.js 16 App Router, React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Radix UI, `next-intl`, `date-fns`, `react-day-picker`, `react-responsive`, Node 24 test runner.

## Global Constraints

- Preserve the `DatePicker` value contract as an ISO `yyyy-MM-dd` string.
- Preserve existing API payloads, URL query parameter names, translations, and filter semantics.
- Use semantic theme tokens only for foundational surfaces and states; add no hard-coded hex colors.
- Use four filter columns on large screens, two on medium screens, and one column in a mobile shadcn Sheet.
- Keep desktop and tablet filter surfaces inline.
- Keep feature-specific state and URL behavior inside each feature panel.
- Preserve Arabic RTL and English LTR behavior.
- Preserve unrelated modifications in the dirty worktree.
- Do not redesign unrelated confirmation, edit, preview, or destructive dialogs.
- Report existing unrelated lint failures separately; do not fix them as part of this plan.

---

## File Map

### New Files

- `lib/filter/date-range.ts`: pure ISO date-range validation.
- `tests/filter-date-range.test.ts`: Node test coverage for date-range validation.
- `components/ui/filter/filter-surface.tsx`: inline desktop/tablet and mobile Sheet presentation.
- `components/ui/filter/filter-grid.tsx`: responsive 1/2/4-column layout.
- `components/ui/filter/filter-field.tsx`: shared label, control, description, and error layout.
- `components/ui/filter/filter-actions.tsx`: shared apply/clear actions and mobile sticky footer styling.

### Shared Files To Modify

- `components/ui/inputs/date-picker.tsx`: token styling, invalid state, accessibility, and shared control sizing.
- `components/ui/filter/filter-button.tsx`: semantic-token styling and explicit button semantics.
- `components/ui/filter/filter-panel.tsx`: generic LIFI/featured filter migration and date-range validation.
- `messages/ar.json`: localized filter Sheet title, date range labels, and validation copy.
- `messages/en.json`: localized filter Sheet title, date range labels, and validation copy.

### Feature Panels To Modify

- `components/dashboard/blogs/blogs-filter-panel.tsx`
- `components/dashboard/blogs/categories-filter-panel.tsx`
- `components/dashboard/blogs/tags-filter-panel.tsx`
- `components/dashboard/pages/pages-filter-panel.tsx`
- `components/dashboard/users/users-filter-panel.tsx`
- `components/dashboard/users/roles-filter-panel.tsx`
- `components/dashboard/appearance/menu-filter-panel.tsx`
- `components/dashboard/appearance/widgets-filter-panel.tsx`
- `components/dashboard/settings/maintenance-filter-panel.tsx`
- `components/dashboard/tickets/tickets-date-range-picker.tsx`

### Page Integration Files To Review And Modify As Needed

- `components/dashboard/lifi/lifi-page-view.tsx`
- `components/dashboard/blogs/featured-page-view.tsx`
- `components/dashboard/blogs/blogs-page-view.tsx`
- `components/dashboard/blogs/categories-page-view.tsx`
- `components/dashboard/blogs/tags-page-view.tsx`
- `components/dashboard/pages/pages-page-view.tsx`
- `components/dashboard/users/users-page-view.tsx`
- `components/dashboard/users/roles-page-view.tsx`
- `components/dashboard/appearance/menu-page-view.tsx`
- `components/dashboard/appearance/widgets-page-view.tsx`
- `components/dashboard/settings/maintenance-page-view.tsx`

### Non-filter DatePicker Consumers To Verify

- `components/dashboard/blogs/blog-upsert-form-view.tsx`
- `components/dashboard/lifi/lifi-upsert-form-view.tsx`
- `components/dashboard/profile/profile-edit-dialog.tsx`

---

### Task 1: Add Pure Date-Range Validation

**Files:**
- Create: `lib/filter/date-range.ts`
- Create: `tests/filter-date-range.test.ts`

**Interfaces:**
- Produces: `isValidIsoDateRange(from: string, to: string): boolean`
- Produces: `getIsoDateRangeError(from: string, to: string): "invalid-order" | null`
- Consumes: ISO `yyyy-MM-dd` strings already used by all filter panels.

- [ ] **Step 1: Write the failing Node test**

```ts
import assert from "node:assert/strict"
import test from "node:test"

import {
  getIsoDateRangeError,
  isValidIsoDateRange,
} from "../lib/filter/date-range.ts"

test("allows empty and partial ranges", () => {
  assert.equal(isValidIsoDateRange("", ""), true)
  assert.equal(isValidIsoDateRange("2026-07-01", ""), true)
  assert.equal(isValidIsoDateRange("", "2026-07-31"), true)
})

test("allows equal and ascending ISO ranges", () => {
  assert.equal(isValidIsoDateRange("2026-07-12", "2026-07-12"), true)
  assert.equal(isValidIsoDateRange("2026-07-01", "2026-07-31"), true)
})

test("rejects descending ISO ranges", () => {
  assert.equal(isValidIsoDateRange("2026-08-01", "2026-07-31"), false)
  assert.equal(getIsoDateRangeError("2026-08-01", "2026-07-31"), "invalid-order")
})
```

- [ ] **Step 2: Run the test and verify it fails because the module is missing**

Run: `node --experimental-strip-types --test tests/filter-date-range.test.ts`

Expected: FAIL with `ERR_MODULE_NOT_FOUND` for `lib/filter/date-range.ts`.

- [ ] **Step 3: Implement the minimal pure helper**

```ts
export type IsoDateRangeError = "invalid-order"

export function isValidIsoDateRange(from: string, to: string): boolean {
  if (!from || !to) return true
  return from <= to
}

export function getIsoDateRangeError(
  from: string,
  to: string
): IsoDateRangeError | null {
  return isValidIsoDateRange(from, to) ? null : "invalid-order"
}
```

- [ ] **Step 4: Run the test and verify it passes**

Run: `node --experimental-strip-types --test tests/filter-date-range.test.ts`

Expected: 3 tests pass, 0 fail.

- [ ] **Step 5: Commit the helper and test**

```bash
git add lib/filter/date-range.ts tests/filter-date-range.test.ts
git commit -m "Add filter date range validation"
```

---

### Task 2: Redesign The Shared DatePicker

**Files:**
- Modify: `components/ui/inputs/date-picker.tsx`

**Interfaces:**
- Preserves: `value?: string`, `onChange?: (value: string) => void`, `placeholder?: string`, `className?: string`, `disabled?: boolean`
- Adds: `invalid?: boolean`, `aria-label?: string`, `fromYear?: number`, `toYear?: number`
- Consumes: shadcn `Button`, `Popover`, and `Calendar` primitives.

- [ ] **Step 1: Record the current focused lint baseline**

Run: `npx eslint components/ui/inputs/date-picker.tsx`

Expected: exit 0. Record any existing warning before editing.

- [ ] **Step 2: Extend the public props without changing the ISO contract**

```ts
interface DatePickerProps {
  value?: string
  onChange?: (value: string) => void
  placeholder?: string
  className?: string
  disabled?: boolean
  invalid?: boolean
  "aria-label"?: string
  fromYear?: number
  toYear?: number
}
```

Destructure the new props and keep `handleSelect` returning `format(date, "yyyy-MM-dd")`.

- [ ] **Step 3: Replace custom surface styling with semantic states**

Use this trigger class contract:

```tsx
className={cn(
  "h-10 w-full justify-between rounded-lg border-input bg-background px-3 font-normal text-foreground shadow-xs",
  "hover:bg-accent hover:text-accent-foreground",
  "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
  !selected && "text-muted-foreground",
  invalid && "border-destructive focus-visible:border-destructive focus-visible:ring-destructive/20",
  className
)}
```

Set `type="button"`, `aria-invalid={invalid}`, pass the accessible label, and keep the calendar icon at `size-4` with `text-muted-foreground`.

- [ ] **Step 4: Keep localized display and constrain calendar navigation when requested**

```tsx
<Calendar
  mode="single"
  selected={selected}
  onSelect={handleSelect}
  locale={dateFnsLocale}
  dir={isRtl ? "rtl" : "ltr"}
  startMonth={fromYear ? new Date(fromYear, 0) : undefined}
  endMonth={toYear ? new Date(toYear, 11) : undefined}
  autoFocus
/>
```

Keep the Popover semantic: `className="w-auto border-border bg-popover p-0 text-popover-foreground"`.

- [ ] **Step 5: Run focused verification**

Run: `npx eslint components/ui/inputs/date-picker.tsx`

Expected: exit 0 with no new findings.

Run: `npm run build`

Expected: production compilation and TypeScript pass.

- [ ] **Step 6: Commit the shared control**

```bash
git add components/ui/inputs/date-picker.tsx
git commit -m "Refine shared date picker styling"
```

---

### Task 3: Add Shared Filter Presentation Primitives

**Files:**
- Create: `components/ui/filter/filter-surface.tsx`
- Create: `components/ui/filter/filter-grid.tsx`
- Create: `components/ui/filter/filter-field.tsx`
- Create: `components/ui/filter/filter-actions.tsx`
- Modify: `components/ui/filter/filter-button.tsx`
- Modify: `messages/ar.json`
- Modify: `messages/en.json`

**Interfaces:**
- Produces: `FilterSurface({ open, onOpenChange, title, children }: FilterSurfaceProps)`
- Produces: `FilterGrid({ children, className }: React.ComponentProps<"div">)`
- Produces: `FilterField({ label, error, className, children }: FilterFieldProps)`
- Produces: `FilterActions({ onApply, onClear, applyLabel, clearLabel, applyDisabled }: FilterActionsProps)`
- Consumes: `useReactResponsive`, shadcn `Sheet`, semantic theme tokens, and existing `Button` variants.

- [ ] **Step 1: Add the translation contract before component usage**

Under `dashboard.filters`, add equivalent localized keys:

```json
{
  "title": "Filters",
  "invalidDateRange": "The start date must be before or equal to the end date"
}
```

Arabic values:

```json
{
  "title": "التصفية",
  "invalidDateRange": "يجب أن يكون تاريخ البداية قبل أو مساويًا لتاريخ النهاية"
}
```

- [ ] **Step 2: Implement `FilterGrid` and `FilterField`**

```tsx
export function FilterGrid({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      className={cn("grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4", className)}
      {...props}
    />
  )
}
```

```tsx
interface FilterFieldProps extends React.ComponentProps<"div"> {
  label: React.ReactNode
  error?: React.ReactNode
}

export function FilterField({ label, error, className, children, ...props }: FilterFieldProps) {
  return (
    <div className={cn("flex min-w-0 flex-col gap-2", className)} {...props}>
      <span className="text-sm font-medium text-foreground">{label}</span>
      {children}
      {error ? <p className="text-xs text-destructive">{error}</p> : null}
    </div>
  )
}
```

- [ ] **Step 3: Implement `FilterActions` using existing Button variants**

```tsx
interface FilterActionsProps {
  onApply: () => void
  onClear: () => void
  applyLabel: string
  clearLabel: string
  applyDisabled?: boolean
  className?: string
}
```

Render Clear first as `variant="outline"` and Apply second as the default variant. Both are `type="button"`, `h-10`, and `rounded-lg`. Wrap them in `flex flex-col-reverse gap-2 sm:flex-row sm:justify-end`; accept `className` so the Sheet can supply `sticky bottom-0 border-t border-border bg-background p-4`.

- [ ] **Step 4: Implement `FilterSurface` with one responsive branch**

Use `useReactResponsive().md` and render exactly one branch:

```tsx
interface FilterSurfaceProps {
  open: boolean
  onOpenChange: (open: boolean) => void
  title: string
  children: React.ReactNode
}
```

Desktop/tablet branch:

```tsx
if (md) {
  return open ? (
    <section className="space-y-4 rounded-xl border border-border bg-card p-4 text-card-foreground">
      {children}
    </section>
  ) : null
}
```

Mobile branch uses controlled shadcn Sheet:

```tsx
<Sheet open={open} onOpenChange={onOpenChange}>
  <SheetContent side="bottom" className="max-h-[90dvh] rounded-t-xl border-border bg-background p-0">
    <SheetHeader className="border-b border-border px-4 py-3">
      <SheetTitle>{title}</SheetTitle>
    </SheetHeader>
    <div className="min-h-0 flex-1 overflow-y-auto p-4">{children}</div>
  </SheetContent>
</Sheet>
```

Do not render a second Sheet trigger; existing page `FilterButton` controls `open`.

- [ ] **Step 5: Tokenize `FilterButton`**

Replace custom `rounded-[12px]`, `bg-highlights-bg`, and `border-primary-100` classes with existing variants and semantic state classes:

```tsx
className={cn(
  "h-10 gap-2 rounded-lg px-4",
  active && "border-primary bg-accent text-primary",
  className
)}
```

Add `type="button"` and preserve active-filter detection.

- [ ] **Step 6: Run focused verification**

Run:

```bash
npx eslint components/ui/filter/filter-surface.tsx components/ui/filter/filter-grid.tsx components/ui/filter/filter-field.tsx components/ui/filter/filter-actions.tsx components/ui/filter/filter-button.tsx
```

Expected: exit 0 with no findings.

Run: `npm run build`

Expected: production compilation and translation typing pass.

- [ ] **Step 7: Commit the shared filter system**

```bash
git add components/ui/filter/filter-surface.tsx components/ui/filter/filter-grid.tsx components/ui/filter/filter-field.tsx components/ui/filter/filter-actions.tsx components/ui/filter/filter-button.tsx messages/ar.json messages/en.json
git commit -m "Add responsive filter layout primitives"
```

---

### Task 4: Migrate The Generic LIFI And Featured Filter Panel

**Files:**
- Modify: `components/ui/filter/filter-panel.tsx`
- Modify if wrapper spacing is duplicated: `components/dashboard/lifi/lifi-page-view.tsx`
- Modify if wrapper spacing is duplicated: `components/dashboard/blogs/featured-page-view.tsx`

**Interfaces:**
- Consumes: `FilterSurface`, `FilterGrid`, `FilterField`, `FilterActions`, `DatePicker`, `isValidIsoDateRange`.
- Preserves: `FilterPanel({ className, isOpen, onClose })`.
- Preserves query keys: `status`, `publishDateFrom`, `publishDateTo`, `releaseDateFrom`, `releaseDateTo`, `views`, `page`.

- [ ] **Step 1: Add derived validation without changing state or query keys**

```ts
const publishDateValid = isValidIsoDateRange(publishDateFrom, publishDateTo)
const releaseDateValid = isValidIsoDateRange(releaseDateFrom, releaseDateTo)
const rangesValid = publishDateValid && releaseDateValid
```

Guard `handleApplyFilters` with `if (!rangesValid) return`.

- [ ] **Step 2: Replace the panel shell and grid**

```tsx
<FilterSurface
  open={isOpen}
  onOpenChange={(open) => {
    if (!open) onClose()
  }}
  title={t("title")}
>
  <FilterGrid>
    <FilterField label={t("status")}>
      <Select value={statusFilter} onValueChange={setStatusFilter}>
        <SelectTrigger className="h-10 w-full rounded-lg border-input bg-background">
          <SelectValue placeholder={t("all")} />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="all">{t("all")}</SelectItem>
          <SelectItem value="published">{t("statuses.published")}</SelectItem>
          <SelectItem value="disabled">{t("statuses.disabled")}</SelectItem>
          <SelectItem value="scheduled">{t("statuses.scheduled")}</SelectItem>
          <SelectItem value="draft">{t("statuses.draft")}</SelectItem>
        </SelectContent>
      </Select>
    </FilterField>
    <FilterField label={t("releaseDate")} error={!releaseDateValid ? t("invalidDateRange") : undefined}>
      <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
        <DatePicker value={releaseDateFrom} onChange={setReleaseDateFrom} invalid={!releaseDateValid} />
        <DatePicker value={releaseDateTo} onChange={setReleaseDateTo} invalid={!releaseDateValid} />
      </div>
    </FilterField>
    <FilterField label={t("publishDate")} error={!publishDateValid ? t("invalidDateRange") : undefined}>
      <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
        <DatePicker value={publishDateFrom} onChange={setPublishDateFrom} invalid={!publishDateValid} />
        <DatePicker value={publishDateTo} onChange={setPublishDateTo} invalid={!publishDateValid} />
      </div>
    </FilterField>
    <FilterField label={t("views")}>
      <Select value={viewsFilter} onValueChange={setViewsFilter}>
        <SelectTrigger className="h-10 w-full rounded-lg border-input bg-background">
          <SelectValue placeholder={t("all")} />
        </SelectTrigger>
        <SelectContent>
          <SelectItem value="all">{t("all")}</SelectItem>
          <SelectItem value="most">{t("viewsOptions.most")}</SelectItem>
          <SelectItem value="least">{t("viewsOptions.least")}</SelectItem>
        </SelectContent>
      </Select>
    </FilterField>
  </FilterGrid>
  <FilterActions
    onApply={handleApplyFilters}
    onClear={handleClearFilters}
    applyLabel={tCommon("filter")}
    clearLabel={tCommon("cancel")}
    applyDisabled={!rangesValid}
  />
</FilterSurface>
```

Use `FilterField` for each logical field. Add `className="md:col-span-2 xl:col-span-1"` to date-range fields. Inside each range use `grid grid-cols-1 gap-2 sm:grid-cols-2`.

- [ ] **Step 3: Wire invalid state and localized errors**

For each pair:

```tsx
<FilterField
  label={t("releaseDate")}
  error={!releaseDateValid ? t("invalidDateRange") : undefined}
>
  <div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
    <DatePicker value={releaseDateFrom} onChange={setReleaseDateFrom} invalid={!releaseDateValid} />
    <DatePicker value={releaseDateTo} onChange={setReleaseDateTo} invalid={!releaseDateValid} />
  </div>
</FilterField>
```

Repeat explicitly for publish dates with their existing values.

- [ ] **Step 4: Remove page-level wrappers only where they duplicate FilterSurface spacing**

Keep the existing conditional active-filter badges. Ensure each page renders `FilterPanel` directly without an extra bordered card around it.

- [ ] **Step 5: Run focused verification**

Run:

```bash
npx eslint components/ui/filter/filter-panel.tsx components/dashboard/lifi/lifi-page-view.tsx components/dashboard/blogs/featured-page-view.tsx
```

Expected: no new findings.

Manually verify `/ar/dashboard/lifi` and `/en/dashboard/lifi` at 390px, 768px, and 1440px. Expected: mobile Sheet, two tablet columns, four desktop columns; applying and clearing preserve existing query keys.

- [ ] **Step 6: Commit the generic migration**

```bash
git add components/ui/filter/filter-panel.tsx components/dashboard/lifi/lifi-page-view.tsx components/dashboard/blogs/featured-page-view.tsx
git commit -m "Migrate shared dashboard filters"
```

Only add page files that actually changed.

---

### Task 5: Migrate Date-Range Feature Filters

**Files:**
- Modify: `components/dashboard/blogs/categories-filter-panel.tsx`
- Modify: `components/dashboard/blogs/tags-filter-panel.tsx`
- Modify: `components/dashboard/pages/pages-filter-panel.tsx`
- Modify: `components/dashboard/tickets/tickets-date-range-picker.tsx`
- Modify as needed: `components/dashboard/blogs/categories-page-view.tsx`
- Modify as needed: `components/dashboard/blogs/tags-page-view.tsx`
- Modify as needed: `components/dashboard/pages/pages-page-view.tsx`

**Interfaces:**
- Consumes: all shared filter primitives and `isValidIsoDateRange`.
- Preserves: each component's existing props and query keys.
- Produces: consistent range validation and responsive layout.

- [ ] **Step 1: Migrate categories filtering**

Keep its current state and query mutation. Derive:

```ts
const dateRangeValid = isValidIsoDateRange(createdFrom, createdTo)
```

Use one `FilterField` with `md:col-span-2 xl:col-span-1`, two `DatePicker`s, localized error, and `FilterActions applyDisabled={!dateRangeValid}`. Use the file's exact existing state variable names if they differ from this example.

- [ ] **Step 2: Migrate tags filtering**

Keep the controlled `value`, `onApply`, and `onClear` API. Compute validity from `value.createdFrom` and `value.createdTo` using the file's actual keys. Wrap its fields in `FilterGrid` and use `FilterActions`; do not move filter ownership into shared components.

- [ ] **Step 3: Migrate pages filtering**

Preserve status and date query keys. Keep selects as individual `FilterField`s and the date pair as one logical field. Disable Apply only for reversed ranges.

- [ ] **Step 4: Align the ticket date-range popover**

Keep the ticket component's existing popover behavior rather than converting it to a full filter Sheet. Replace duplicated date layout with two tokenized `DatePicker`s, add `isValidIsoDateRange(draftFrom, draftTo)`, show the localized range error, and disable its Apply action while invalid.

- [ ] **Step 5: Run focused verification**

Run:

```bash
npx eslint components/dashboard/blogs/categories-filter-panel.tsx components/dashboard/blogs/tags-filter-panel.tsx components/dashboard/pages/pages-filter-panel.tsx components/dashboard/tickets/tickets-date-range-picker.tsx
```

Expected: no new findings.

Run: `node --experimental-strip-types --test tests/filter-date-range.test.ts`

Expected: 3 tests pass, 0 fail.

Manually test valid, partial, equal, and reversed ranges in Arabic and English.

- [ ] **Step 6: Commit the date-range migrations**

```bash
git add components/dashboard/blogs/categories-filter-panel.tsx components/dashboard/blogs/tags-filter-panel.tsx components/dashboard/pages/pages-filter-panel.tsx components/dashboard/tickets/tickets-date-range-picker.tsx components/dashboard/blogs/categories-page-view.tsx components/dashboard/blogs/tags-page-view.tsx components/dashboard/pages/pages-page-view.tsx
git commit -m "Standardize dashboard date filters"
```

Only add page files that actually changed.

---

### Task 6: Migrate Select-Based Feature Filters

**Files:**
- Modify: `components/dashboard/blogs/blogs-filter-panel.tsx`
- Modify: `components/dashboard/users/roles-filter-panel.tsx`
- Modify: `components/dashboard/appearance/menu-filter-panel.tsx`
- Modify: `components/dashboard/appearance/widgets-filter-panel.tsx`
- Modify: `components/dashboard/settings/maintenance-filter-panel.tsx`
- Modify as needed: corresponding page-view files listed in the File Map.

**Interfaces:**
- Consumes: `FilterSurface`, `FilterGrid`, `FilterField`, `FilterActions`.
- Preserves: existing component props, select options, translations, URL keys, pagination reset, and close behavior.

- [ ] **Step 1: Replace each duplicated shell with `FilterSurface`**

For every panel, preserve the existing `if (!isOpen) return null` only if `FilterSurface` is not mounted persistently. Prefer always returning `FilterSurface` so mobile Sheet close animations work.

Use:

```tsx
<FilterSurface
  open={isOpen}
  onOpenChange={(open) => {
    if (!open) onClose()
  }}
  title={tCommon("filter")}
>
  <FilterGrid>{fields}</FilterGrid>
  <FilterActions
    onApply={applyFilters}
    onClear={clearFilters}
    applyLabel={tCommon("filter")}
    clearLabel={tCommon("cancel")}
  />
</FilterSurface>
```

- [ ] **Step 2: Replace field wrappers, not business logic**

In the preceding composition, define `fields` immediately before `return` as the panel's complete array of `FilterField` elements. Do not introduce a shared schema. For every select element in that array:

```tsx
<FilterField label={t("status")}>
  <Select value={statusValue} onValueChange={setStatusValue}>
    <SelectTrigger className="h-10 w-full rounded-lg border-input bg-background">
      <SelectValue placeholder={t("all")} />
    </SelectTrigger>
    <SelectContent>
      {statusOptions.map((status) => (
        <SelectItem key={status} value={status}>
          {t(`statuses.${status}`)}
        </SelectItem>
      ))}
    </SelectContent>
  </Select>
</FilterField>
```

Do not rename values, change option order, or consolidate feature-specific types.

- [ ] **Step 3: Remove duplicated card wrappers from page views**

For blogs, roles, menus, widgets, and maintenance, ensure the page renders the panel directly. Preserve active-filter badges and existing ternaries unless they prevent the mobile Sheet from staying mounted during close animation.

- [ ] **Step 4: Run focused verification**

Run:

```bash
npx eslint components/dashboard/blogs/blogs-filter-panel.tsx components/dashboard/users/roles-filter-panel.tsx components/dashboard/appearance/menu-filter-panel.tsx components/dashboard/appearance/widgets-filter-panel.tsx components/dashboard/settings/maintenance-filter-panel.tsx
```

Expected: no new findings.

At 390px, confirm each FilterButton opens a bottom Sheet and closing without Apply leaves the URL unchanged. At 768px and 1440px, confirm inline two- and four-column layouts.

- [ ] **Step 5: Commit the select-filter migrations**

Stage only files changed in this task, then run:

```bash
git commit -m "Unify dashboard filter layouts"
```

---

### Task 7: Migrate Users Filtering And Audit DatePicker Consumers

**Files:**
- Modify: `components/dashboard/users/users-filter-panel.tsx`
- Modify as needed: `components/dashboard/users/users-page-view.tsx`
- Review and modify only if compatibility requires it: `components/dashboard/blogs/blog-upsert-form-view.tsx`
- Review and modify only if compatibility requires it: `components/dashboard/lifi/lifi-upsert-form-view.tsx`
- Review and modify only if compatibility requires it: `components/dashboard/profile/profile-edit-dialog.tsx`

**Interfaces:**
- Consumes: shared filter primitives and redesigned `DatePicker`.
- Preserves users query keys: `status`, `role`, `language`, `joinedAt`, `page`.
- Preserves non-filter form values and submission behavior.

- [ ] **Step 1: Migrate the four-field users panel**

Use exactly four `FilterField`s in one large-screen row: status, role, language, and joined date. Use the existing option arrays and type guards without change. Replace the joined date control with:

```tsx
<DatePicker
  value={joinedAtValue}
  onChange={setJoinedAtValue}
  aria-label={t("joinedAt")}
/>
```

Use `FilterActions` and preserve current URL serialization.

- [ ] **Step 2: Verify non-filter DatePicker layouts**

Inspect each non-filter consumer after the shared `h-10` change. Do not add local colors. If a form requires the existing `h-12` density, pass `className="h-12 rounded-xl"` while retaining the shared semantic state classes.

Expected consumer-specific decisions:

- Blog scheduling: keep form density with `h-12` only if adjacent controls are `h-12`.
- LIFI form: match adjacent form controls without changing scheduled-date logic.
- Profile edit dialog: keep full width and mobile-safe popover alignment.

- [ ] **Step 3: Run focused verification**

Run:

```bash
npx eslint components/dashboard/users/users-filter-panel.tsx components/dashboard/users/users-page-view.tsx components/dashboard/blogs/blog-upsert-form-view.tsx components/dashboard/lifi/lifi-upsert-form-view.tsx components/dashboard/profile/profile-edit-dialog.tsx
```

Expected: no new errors in touched files; record pre-existing warnings separately.

Manually select and clear dates in all three non-filter forms in both locales.

- [ ] **Step 4: Commit the users migration and compatibility adjustments**

Stage only files changed in this task, then run:

```bash
git commit -m "Complete shared date picker rollout"
```

---

### Task 8: Final System Verification

**Files:**
- No planned production edits.
- Modify only files with defects discovered by this verification and repeat their focused checks.

**Interfaces:**
- Verifies the complete system against the approved design spec.

- [ ] **Step 1: Run pure logic tests**

Run: `node --experimental-strip-types --test tests/filter-date-range.test.ts`

Expected: 3 tests pass, 0 fail.

- [ ] **Step 2: Run focused lint over the complete migration surface**

Run:

```bash
npx eslint components/ui/inputs/date-picker.tsx components/ui/filter components/dashboard/blogs/*filter-panel.tsx components/dashboard/pages/pages-filter-panel.tsx components/dashboard/users/users-filter-panel.tsx components/dashboard/users/roles-filter-panel.tsx components/dashboard/appearance/menu-filter-panel.tsx components/dashboard/appearance/widgets-filter-panel.tsx components/dashboard/settings/maintenance-filter-panel.tsx components/dashboard/tickets/tickets-date-range-picker.tsx
```

Expected: exit 0. If unrelated files fail full lint, report them separately.

- [ ] **Step 3: Run the production build**

Run: `npm run build`

Expected: Next.js production compilation, TypeScript, page-data collection, and route generation pass.

- [ ] **Step 4: Check patch integrity**

Run: `git diff --check`

Expected: no whitespace errors.

Run:

```bash
rg -n "#[0-9A-Fa-f]{3,8}|bg-\[|text-\[|border-\[" components/ui/filter components/ui/inputs/date-picker.tsx components/dashboard --glob '*filter-panel.tsx'
```

Expected: no new hard-coded foundational colors in the migrated surface. Investigate each match rather than bulk-editing unrelated styles.

- [ ] **Step 5: Verify responsive behavior in a browser**

Run: `npm run dev`

Check representative routes at 390px, 768px, and 1440px:

- `/ar/dashboard/lifi`
- `/en/dashboard/blogs`
- `/ar/dashboard/users`
- `/en/dashboard/pages`
- `/ar/dashboard/appearance/widgets`
- `/en/dashboard/settings/maintenance`

Expected:

- 390px: one-column bottom Sheet, scrollable body, sticky actions, no horizontal overflow.
- 768px: inline two-column panel.
- 1440px: inline four-column panel with compact controls and no more than four logical fields per row.
- Closing mobile Sheet without Apply leaves query parameters unchanged.
- Apply updates only owned keys and resets `page=1`.
- Clear removes only owned keys and resets `page=1`.
- Reversed date ranges show localized errors and disable Apply.
- Arabic uses RTL alignment; English uses LTR.

- [ ] **Step 6: Verify all DatePicker contexts**

Check blog scheduling, LIFI scheduling, profile editing, ticket range filtering, and all filter panels. Expected: localized display, ISO values internally, keyboard-accessible Popover/Calendar behavior, and token-correct light/dark states.

- [ ] **Step 7: Commit verification fixes only if needed**

If verification required fixes, run `git status --short`, then stage each file fixed during Task 8 by its exact path. Do not use `git add .`. Commit the staged fixes with:

```bash
git commit -m "Fix responsive filter verification issues"
```

If no fixes were needed, do not create an empty commit.
