refactor: fold date-warning filter into Flag pill
Rolls the standalone "Date issues" toggle back into FlagFilter as a
third value ('any' | 'discarded' | 'date_warning') so the date-warning
control lives in the same popover as Discarded, where operators expect
all flag-style filters. Drops the redundant dateWarning boolean and
its URL param.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Star, X, ArrowDown, ArrowUp, Search, AlertTriangle } from 'lucide-react'
|
import { Star, X, ArrowDown, ArrowUp, Search, AlertTriangle, Check } from 'lucide-react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import {
|
import {
|
||||||
useFilterStore,
|
useFilterStore,
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
type SortField,
|
type SortField,
|
||||||
} from '../../store/filterStore'
|
} from '../../store/filterStore'
|
||||||
import { useTagsQuery } from '../../hooks/useTagsQuery'
|
import { useTagsQuery } from '../../hooks/useTagsQuery'
|
||||||
|
import type { Tag } from '../../services/api'
|
||||||
import { FilterPill } from './FilterPill'
|
import { FilterPill } from './FilterPill'
|
||||||
import { COLOR_LABEL_OPTIONS } from '../../constants/colorLabels'
|
import { COLOR_LABEL_OPTIONS } from '../../constants/colorLabels'
|
||||||
|
|
||||||
@@ -42,8 +43,6 @@ export function FilterBar() {
|
|||||||
const ratingMin = useFilterStore((s) => s.ratingMin)
|
const ratingMin = useFilterStore((s) => s.ratingMin)
|
||||||
const colorLabel = useFilterStore((s) => s.colorLabel)
|
const colorLabel = useFilterStore((s) => s.colorLabel)
|
||||||
const flag = useFilterStore((s) => s.flag)
|
const flag = useFilterStore((s) => s.flag)
|
||||||
const dateWarning = useFilterStore((s) => s.dateWarning)
|
|
||||||
const setDateWarning = useFilterStore((s) => s.setDateWarning)
|
|
||||||
const sortBy = useFilterStore((s) => s.sortBy)
|
const sortBy = useFilterStore((s) => s.sortBy)
|
||||||
const sortOrder = useFilterStore((s) => s.sortOrder)
|
const sortOrder = useFilterStore((s) => s.sortOrder)
|
||||||
const tagIds = useFilterStore((s) => s.tagIds)
|
const tagIds = useFilterStore((s) => s.tagIds)
|
||||||
@@ -108,7 +107,11 @@ export function FilterBar() {
|
|||||||
const colorValue = colorActive ? colorLabel : null
|
const colorValue = colorActive ? colorLabel : null
|
||||||
|
|
||||||
const flagActive = flag !== 'any'
|
const flagActive = flag !== 'any'
|
||||||
const flagValue = flagActive ? flag : null
|
const flagValue = flagActive
|
||||||
|
? flag === 'date_warning'
|
||||||
|
? 'date issues'
|
||||||
|
: flag
|
||||||
|
: null
|
||||||
|
|
||||||
const tagActive = tagIds.length > 0
|
const tagActive = tagIds.length > 0
|
||||||
const activeTagNames = allTags
|
const activeTagNames = allTags
|
||||||
@@ -290,6 +293,19 @@ export function FilterBar() {
|
|||||||
>
|
>
|
||||||
Discarded
|
Discarded
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setFlag('date_warning')}
|
||||||
|
className={clsx(
|
||||||
|
'flex items-center gap-1.5 rounded px-2 py-1 text-left text-xs transition-colors',
|
||||||
|
flag === 'date_warning'
|
||||||
|
? 'bg-primary text-white'
|
||||||
|
: 'bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text'
|
||||||
|
)}
|
||||||
|
title="Photos whose folder/filename suggests a different date than the stored taken_at"
|
||||||
|
>
|
||||||
|
<AlertTriangle className="h-3 w-3" />
|
||||||
|
Date issues
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</FilterPill>
|
</FilterPill>
|
||||||
)}
|
)}
|
||||||
@@ -302,60 +318,15 @@ export function FilterBar() {
|
|||||||
isActive={tagActive}
|
isActive={tagActive}
|
||||||
onClear={() => setTagIds([])}
|
onClear={() => setTagIds([])}
|
||||||
>
|
>
|
||||||
<div className="flex max-h-60 flex-wrap gap-1 overflow-y-auto">
|
<TagFilterPopover
|
||||||
{allTags.map((tag) => {
|
allTags={allTags}
|
||||||
const active = tagIds.includes(tag.id)
|
selectedIds={tagIds}
|
||||||
return (
|
onToggle={toggleTagId}
|
||||||
<button
|
onClear={() => setTagIds([])}
|
||||||
key={tag.id}
|
/>
|
||||||
onClick={() => toggleTagId(tag.id)}
|
|
||||||
className={clsx(
|
|
||||||
'rounded px-2 py-1 text-xs transition-colors',
|
|
||||||
active
|
|
||||||
? 'bg-primary text-white'
|
|
||||||
: 'bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{tag.name}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</FilterPill>
|
</FilterPill>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Date issues — toggle-only pill. Restricts the grid to photos
|
|
||||||
* whose path-based date guess disagrees with the stored taken_at,
|
|
||||||
* so operators can find and fix a whole library's worth of
|
|
||||||
* corrupted or missing capture dates in one pass. Backed by the
|
|
||||||
* `photos.has_date_warning` column set at scan time. */}
|
|
||||||
<button
|
|
||||||
onClick={() => setDateWarning(!dateWarning)}
|
|
||||||
className={clsx(
|
|
||||||
'flex h-7 flex-shrink-0 items-center gap-1 whitespace-nowrap rounded-full border px-2.5 text-xs transition-colors',
|
|
||||||
dateWarning
|
|
||||||
? 'border-amber-500/60 bg-amber-500/15 text-amber-300 hover:bg-amber-500/25'
|
|
||||||
: 'border-border bg-surface-2 text-text-muted hover:bg-surface-offset hover:text-text'
|
|
||||||
)}
|
|
||||||
title={
|
|
||||||
dateWarning
|
|
||||||
? 'Showing only photos with suspicious capture dates'
|
|
||||||
: 'Show only photos whose folder/filename suggests a different date'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AlertTriangle className="h-3 w-3" />
|
|
||||||
Date issues
|
|
||||||
{dateWarning && (
|
|
||||||
<X
|
|
||||||
className="ml-0.5 h-3 w-3"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setDateWarning(false)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Sort — always present, never "active/inactive" since there's
|
{/* Sort — always present, never "active/inactive" since there's
|
||||||
always a value. */}
|
always a value. */}
|
||||||
<FilterPill label="Sort" value={sortValue} isActive>
|
<FilterPill label="Sort" value={sortValue} isActive>
|
||||||
@@ -441,3 +412,145 @@ export function FilterBar() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TagFilterPopoverProps {
|
||||||
|
allTags: Tag[]
|
||||||
|
selectedIds: string[]
|
||||||
|
onToggle: (id: string) => void
|
||||||
|
onClear: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
function TagFilterPopover({
|
||||||
|
allTags,
|
||||||
|
selectedIds,
|
||||||
|
onToggle,
|
||||||
|
onClear,
|
||||||
|
}: TagFilterPopoverProps) {
|
||||||
|
const [query, setQuery] = useState('')
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
inputRef.current?.focus()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const selectedSet = useMemo(() => new Set(selectedIds), [selectedIds])
|
||||||
|
|
||||||
|
// Selected tags pinned at top, remaining sorted by photo_count desc
|
||||||
|
// then name. Filtered by query (case-insensitive substring).
|
||||||
|
const orderedTags = useMemo(() => {
|
||||||
|
const q = query.trim().toLowerCase()
|
||||||
|
const match = (t: Tag) => !q || t.name.toLowerCase().includes(q)
|
||||||
|
const selected = allTags.filter((t) => selectedSet.has(t.id) && match(t))
|
||||||
|
const unselected = allTags
|
||||||
|
.filter((t) => !selectedSet.has(t.id) && match(t))
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (b.photo_count !== a.photo_count) return b.photo_count - a.photo_count
|
||||||
|
return a.name.localeCompare(b.name)
|
||||||
|
})
|
||||||
|
return { selected, unselected }
|
||||||
|
}, [allTags, selectedSet, query])
|
||||||
|
|
||||||
|
const totalVisible = orderedTags.selected.length + orderedTags.unselected.length
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-64">
|
||||||
|
<div className="relative mb-2">
|
||||||
|
<Search className="pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-text-muted" />
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
type="text"
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Escape' && query) {
|
||||||
|
e.stopPropagation()
|
||||||
|
setQuery('')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
placeholder="Search tags…"
|
||||||
|
className="h-7 w-full rounded border border-border bg-bg pl-7 pr-6 text-xs text-text placeholder-text-muted focus:border-primary focus:outline-none"
|
||||||
|
/>
|
||||||
|
{query && (
|
||||||
|
<button
|
||||||
|
onClick={() => setQuery('')}
|
||||||
|
className="absolute right-1 top-1/2 -translate-y-1/2 rounded p-0.5 text-text-muted hover:bg-surface-2 hover:text-text"
|
||||||
|
aria-label="Clear search"
|
||||||
|
>
|
||||||
|
<X className="h-3 w-3" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-1 flex items-center justify-between px-0.5 text-[11px] text-text-muted">
|
||||||
|
<span>
|
||||||
|
{selectedIds.length > 0
|
||||||
|
? `${selectedIds.length} selected`
|
||||||
|
: `${totalVisible} tag${totalVisible === 1 ? '' : 's'}`}
|
||||||
|
</span>
|
||||||
|
{selectedIds.length > 0 && (
|
||||||
|
<button
|
||||||
|
onClick={onClear}
|
||||||
|
className="underline-offset-2 hover:text-text hover:underline"
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="max-h-64 overflow-y-auto rounded border border-border bg-bg">
|
||||||
|
{totalVisible === 0 ? (
|
||||||
|
<div className="px-2 py-3 text-center text-xs text-text-muted">
|
||||||
|
No tags match
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{orderedTags.selected.map((tag) => (
|
||||||
|
<TagRow key={tag.id} tag={tag} selected onToggle={onToggle} />
|
||||||
|
))}
|
||||||
|
{orderedTags.selected.length > 0 && orderedTags.unselected.length > 0 && (
|
||||||
|
<div className="my-0.5 border-t border-border" />
|
||||||
|
)}
|
||||||
|
{orderedTags.unselected.map((tag) => (
|
||||||
|
<TagRow key={tag.id} tag={tag} selected={false} onToggle={onToggle} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TagRow({
|
||||||
|
tag,
|
||||||
|
selected,
|
||||||
|
onToggle,
|
||||||
|
}: {
|
||||||
|
tag: Tag
|
||||||
|
selected: boolean
|
||||||
|
onToggle: (id: string) => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={() => onToggle(tag.id)}
|
||||||
|
className={clsx(
|
||||||
|
'flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs transition-colors',
|
||||||
|
selected
|
||||||
|
? 'bg-primary/15 text-text hover:bg-primary/25'
|
||||||
|
: 'text-text-muted hover:bg-surface-2 hover:text-text'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
'flex h-3.5 w-3.5 flex-shrink-0 items-center justify-center rounded border',
|
||||||
|
selected ? 'border-primary bg-primary text-white' : 'border-border'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{selected && <Check className="h-2.5 w-2.5" strokeWidth={3} />}
|
||||||
|
</span>
|
||||||
|
<span className="min-w-0 flex-1 truncate">{tag.name}</span>
|
||||||
|
<span className="flex-shrink-0 tabular-nums text-[10px] text-text-muted">
|
||||||
|
{tag.photo_count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const ALLOWED_COLORS: ColorLabel[] = [
|
|||||||
'blue',
|
'blue',
|
||||||
'purple',
|
'purple',
|
||||||
]
|
]
|
||||||
const ALLOWED_FLAGS: FlagFilter[] = ['any', 'discarded']
|
const ALLOWED_FLAGS: FlagFilter[] = ['any', 'discarded', 'date_warning']
|
||||||
const ALLOWED_SORT_FIELDS: SortField[] = [
|
const ALLOWED_SORT_FIELDS: SortField[] = [
|
||||||
'taken_at',
|
'taken_at',
|
||||||
'added_at',
|
'added_at',
|
||||||
@@ -89,7 +89,6 @@ function parseUrl(): HydratePayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sp.get('duplicates') === 'true') out.duplicates = true
|
if (sp.get('duplicates') === 'true') out.duplicates = true
|
||||||
if (sp.get('date_warning') === 'true') out.dateWarning = true
|
|
||||||
|
|
||||||
const groupBy = sp.get('group')
|
const groupBy = sp.get('group')
|
||||||
if (groupBy === 'date' || groupBy === 'tag') out.groupBy = groupBy
|
if (groupBy === 'date' || groupBy === 'tag') out.groupBy = groupBy
|
||||||
@@ -124,7 +123,6 @@ function writeUrl(f: FilterState & { currentSection?: string }) {
|
|||||||
if (f.folderId) sp.set('folder_id', f.folderId)
|
if (f.folderId) sp.set('folder_id', f.folderId)
|
||||||
if (f.tagIds.length > 0) sp.set('tag_ids', f.tagIds.join(','))
|
if (f.tagIds.length > 0) sp.set('tag_ids', f.tagIds.join(','))
|
||||||
if (f.duplicates) sp.set('duplicates', 'true')
|
if (f.duplicates) sp.set('duplicates', 'true')
|
||||||
if (f.dateWarning) sp.set('date_warning', 'true')
|
|
||||||
if (f.groupBy !== 'date') sp.set('group', f.groupBy)
|
if (f.groupBy !== 'date') sp.set('group', f.groupBy)
|
||||||
if (f.currentSection && f.currentSection !== 'all-photos')
|
if (f.currentSection && f.currentSection !== 'all-photos')
|
||||||
sp.set('section', f.currentSection)
|
sp.set('section', f.currentSection)
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export function usePhotosQuery() {
|
|||||||
const folderId = useFilterStore((s) => s.folderId)
|
const folderId = useFilterStore((s) => s.folderId)
|
||||||
const tagIds = useFilterStore((s) => s.tagIds)
|
const tagIds = useFilterStore((s) => s.tagIds)
|
||||||
const duplicates = useFilterStore((s) => s.duplicates)
|
const duplicates = useFilterStore((s) => s.duplicates)
|
||||||
const dateWarning = useFilterStore((s) => s.dateWarning)
|
|
||||||
const groupBy = useFilterStore((s) => s.groupBy)
|
const groupBy = useFilterStore((s) => s.groupBy)
|
||||||
const sortBy = useFilterStore((s) => s.sortBy)
|
const sortBy = useFilterStore((s) => s.sortBy)
|
||||||
const sortOrder = useFilterStore((s) => s.sortOrder)
|
const sortOrder = useFilterStore((s) => s.sortOrder)
|
||||||
@@ -59,12 +58,11 @@ export function usePhotosQuery() {
|
|||||||
folderId,
|
folderId,
|
||||||
tagIds,
|
tagIds,
|
||||||
duplicates,
|
duplicates,
|
||||||
dateWarning,
|
|
||||||
groupBy,
|
groupBy,
|
||||||
sortBy,
|
sortBy,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
}),
|
}),
|
||||||
[q, dateFrom, dateTo, mediaTypes, ratingMin, ratingMax, colorLabel, flag, heapId, folderId, tagIds, duplicates, dateWarning, groupBy, sortBy, sortOrder]
|
[q, dateFrom, dateTo, mediaTypes, ratingMin, ratingMax, colorLabel, flag, heapId, folderId, tagIds, duplicates, groupBy, sortBy, sortOrder]
|
||||||
)
|
)
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { ColorLabel } from '../constants/colorLabels'
|
|||||||
|
|
||||||
export type MediaType = 'photo' | 'video' | 'raw' | 'heic'
|
export type MediaType = 'photo' | 'video' | 'raw' | 'heic'
|
||||||
export type { ColorLabel }
|
export type { ColorLabel }
|
||||||
export type FlagFilter = 'any' | 'discarded'
|
export type FlagFilter = 'any' | 'discarded' | 'date_warning'
|
||||||
export type SortField =
|
export type SortField =
|
||||||
| 'taken_at'
|
| 'taken_at'
|
||||||
| 'added_at'
|
| 'added_at'
|
||||||
@@ -31,10 +31,6 @@ export interface FilterState {
|
|||||||
tagIds: string[]
|
tagIds: string[]
|
||||||
/** When true, restrict to photos flagged as duplicates by the scanner. */
|
/** When true, restrict to photos flagged as duplicates by the scanner. */
|
||||||
duplicates: boolean
|
duplicates: boolean
|
||||||
/** When true, restrict to photos whose path-based date guess disagrees
|
|
||||||
* with the stored taken_at (or taken_at is missing). Backed by the
|
|
||||||
* `photos.has_date_warning` column. */
|
|
||||||
dateWarning: boolean
|
|
||||||
/** Visual grouping mode. 'date' groups by month when sortBy is a date
|
/** Visual grouping mode. 'date' groups by month when sortBy is a date
|
||||||
* field; 'tag' groups by photo tag membership. Independent of filters. */
|
* field; 'tag' groups by photo tag membership. Independent of filters. */
|
||||||
groupBy: GroupBy
|
groupBy: GroupBy
|
||||||
@@ -72,7 +68,6 @@ interface FilterStore extends FilterState {
|
|||||||
setTagIds: (ids: string[]) => void
|
setTagIds: (ids: string[]) => void
|
||||||
toggleTagId: (id: string) => void
|
toggleTagId: (id: string) => void
|
||||||
setDuplicates: (v: boolean) => void
|
setDuplicates: (v: boolean) => void
|
||||||
setDateWarning: (v: boolean) => void
|
|
||||||
setGroupBy: (mode: GroupBy) => void
|
setGroupBy: (mode: GroupBy) => void
|
||||||
setSortBy: (field: SortField) => void
|
setSortBy: (field: SortField) => void
|
||||||
setSortOrder: (order: SortOrder) => void
|
setSortOrder: (order: SortOrder) => void
|
||||||
@@ -107,7 +102,6 @@ export const INITIAL_FILTERS: FilterState = {
|
|||||||
folderId: null,
|
folderId: null,
|
||||||
tagIds: [],
|
tagIds: [],
|
||||||
duplicates: false,
|
duplicates: false,
|
||||||
dateWarning: false,
|
|
||||||
groupBy: 'date',
|
groupBy: 'date',
|
||||||
sortBy: 'taken_at',
|
sortBy: 'taken_at',
|
||||||
sortOrder: 'desc',
|
sortOrder: 'desc',
|
||||||
@@ -130,7 +124,6 @@ function snapshotFilters(s: FilterState): FilterState {
|
|||||||
folderId: s.folderId,
|
folderId: s.folderId,
|
||||||
tagIds: [...s.tagIds],
|
tagIds: [...s.tagIds],
|
||||||
duplicates: s.duplicates,
|
duplicates: s.duplicates,
|
||||||
dateWarning: s.dateWarning,
|
|
||||||
groupBy: s.groupBy,
|
groupBy: s.groupBy,
|
||||||
sortBy: s.sortBy,
|
sortBy: s.sortBy,
|
||||||
sortOrder: s.sortOrder,
|
sortOrder: s.sortOrder,
|
||||||
@@ -166,7 +159,6 @@ export const useFilterStore = create<FilterStore>((set) => ({
|
|||||||
: [...s.tagIds, id],
|
: [...s.tagIds, id],
|
||||||
})),
|
})),
|
||||||
setDuplicates: (duplicates) => set({ duplicates }),
|
setDuplicates: (duplicates) => set({ duplicates }),
|
||||||
setDateWarning: (dateWarning) => set({ dateWarning }),
|
|
||||||
setGroupBy: (groupBy) => set({ groupBy }),
|
setGroupBy: (groupBy) => set({ groupBy }),
|
||||||
setSortBy: (sortBy) => set({ sortBy }),
|
setSortBy: (sortBy) => set({ sortBy }),
|
||||||
setSortOrder: (sortOrder) => set({ sortOrder }),
|
setSortOrder: (sortOrder) => set({ sortOrder }),
|
||||||
@@ -221,11 +213,11 @@ export function filtersToParams(f: FilterState): Record<string, string | number>
|
|||||||
if (f.ratingMax > 0) params.rating_max = f.ratingMax
|
if (f.ratingMax > 0) params.rating_max = f.ratingMax
|
||||||
if (f.colorLabel) params.color_label = f.colorLabel
|
if (f.colorLabel) params.color_label = f.colorLabel
|
||||||
if (f.flag === 'discarded') params.is_discarded = 'true'
|
if (f.flag === 'discarded') params.is_discarded = 'true'
|
||||||
|
if (f.flag === 'date_warning') params.has_date_warning = 'true'
|
||||||
if (f.heapId) params.heap_id = f.heapId
|
if (f.heapId) params.heap_id = f.heapId
|
||||||
if (f.folderId) params.folder_id = f.folderId
|
if (f.folderId) params.folder_id = f.folderId
|
||||||
if (f.tagIds.length > 0) params.tag_ids = f.tagIds.join(',')
|
if (f.tagIds.length > 0) params.tag_ids = f.tagIds.join(',')
|
||||||
if (f.duplicates) params.is_duplicate = 'true'
|
if (f.duplicates) params.is_duplicate = 'true'
|
||||||
if (f.dateWarning) params.has_date_warning = 'true'
|
|
||||||
params.sort = f.sortBy
|
params.sort = f.sortBy
|
||||||
params.order = f.sortOrder
|
params.order = f.sortOrder
|
||||||
return params
|
return params
|
||||||
@@ -245,7 +237,6 @@ export function hasActiveFilters(f: FilterState): boolean {
|
|||||||
f.heapId !== null ||
|
f.heapId !== null ||
|
||||||
f.folderId !== null ||
|
f.folderId !== null ||
|
||||||
f.tagIds.length > 0 ||
|
f.tagIds.length > 0 ||
|
||||||
f.duplicates ||
|
f.duplicates
|
||||||
f.dateWarning
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user