| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- import { keepPreviousData } from '@tanstack/react-query'
- import { useDebounce } from '@uidotdev/usehooks'
- import { LOCAL_STORAGE_KEYS, useParams } from 'common'
- import { Grid, List, Loader2, Plus, Search, X } from 'lucide-react'
- import Link from 'next/link'
- import { parseAsArrayOf, parseAsString, parseAsStringLiteral, useQueryState } from 'nuqs'
- import { useEffect } from 'react'
- import { Button, ToggleGroup, ToggleGroupItem } from 'ui'
- import { Input } from 'ui-patterns/DataInputs/Input'
- import { FilterPopover } from '../ui/FilterPopover'
- import { SortDropdown } from '../ui/SortDropdown'
- import {
- PROJECT_LIST_SORT_VALUES,
- type ProjectListSort,
- } from '@/components/interfaces/Home/ProjectList/ProjectListSort.utils'
- import { useOrgProjectsInfiniteQuery } from '@/data/projects/org-projects-infinite-query'
- import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
- import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
- import { PROJECT_STATUS } from '@/lib/constants'
- interface HomePageActionsProps {
- slug?: string
- hideNewProject?: boolean
- }
- export const HomePageActions = ({ slug: _slug, hideNewProject = false }: HomePageActionsProps) => {
- const { slug: urlSlug } = useParams()
- const projectCreationEnabled = useIsFeatureEnabled('projects:create')
- const slug = _slug ?? urlSlug
- const [search, setSearch] = useQueryState('search', parseAsString.withDefault(''))
- const debouncedSearch = useDebounce(search, 500)
- const [filterStatus, setFilterStatus] = useQueryState(
- 'status',
- parseAsArrayOf(parseAsString, ',').withDefault([])
- )
- const [sort, setSort] = useQueryState(
- 'sort',
- parseAsStringLiteral(PROJECT_LIST_SORT_VALUES).withDefault('name_asc')
- )
- const [viewMode, setViewMode] = useLocalStorageQuery(LOCAL_STORAGE_KEYS.PROJECTS_VIEW, 'grid')
- const [filterStatusStorage, setFilterStatusStorage, { isSuccess: isSuccessFilterStatusStorage }] =
- useLocalStorageQuery<string[]>(LOCAL_STORAGE_KEYS.PROJECTS_FILTER, [])
- const [sortStorage, setSortStorage, { isSuccess: isSuccessSortStorage }] =
- useLocalStorageQuery<ProjectListSort>(LOCAL_STORAGE_KEYS.PROJECTS_SORT, 'name_asc')
- const { isFetching: isFetchingProjects } = useOrgProjectsInfiniteQuery(
- {
- slug,
- sort,
- search: search.length === 0 ? search : debouncedSearch,
- statuses: filterStatus,
- },
- { placeholderData: keepPreviousData }
- )
- useEffect(() => {
- if (isSuccessFilterStatusStorage && !!slug) setFilterStatus(filterStatusStorage)
- }, [filterStatusStorage, isSuccessFilterStatusStorage, setFilterStatus, slug])
- useEffect(() => {
- if (isSuccessSortStorage && slug) setSort(sortStorage)
- }, [sortStorage, isSuccessSortStorage, setSort, slug])
- return (
- <div className="flex flex-wrap items-center justify-between gap-2 w-full">
- <div className="flex flex-col gap-2 min-w-0 flex-1 basis-full md:basis-auto sm:flex-row sm:flex-wrap sm:items-center">
- <Input
- placeholder="Search for a project"
- icon={<Search />}
- size="tiny"
- className="w-full sm:w-32 md:w-64"
- value={search}
- onChange={(event) => setSearch(event.target.value)}
- actions={[
- search && (
- <Button
- key="clear"
- size="tiny"
- type="text"
- icon={<X />}
- onClick={() => setSearch('')}
- className="p-0 h-5 w-5"
- />
- ),
- ]}
- />
- <div className="flex items-center gap-2">
- <FilterPopover
- name="Status"
- title="Filter projects by status"
- options={[
- { key: PROJECT_STATUS.ACTIVE_HEALTHY, label: 'Active' },
- { key: PROJECT_STATUS.INACTIVE, label: 'Paused' },
- ]}
- activeOptions={filterStatus}
- valueKey="key"
- labelKey="label"
- onSaveFilters={(options) => setFilterStatusStorage(options)}
- />
- <SortDropdown
- options={[
- { label: 'name', value: 'name' },
- { label: 'creation date', value: 'created' },
- ]}
- value={sort}
- setValue={(val) => setSortStorage(val as ProjectListSort)}
- />
- {isFetchingProjects && <Loader2 className="animate-spin" size={14} />}
- </div>
- </div>
- <div className="flex items-center gap-2 shrink-0">
- {viewMode && setViewMode && (
- <ToggleGroup
- type="single"
- size="sm"
- value={viewMode}
- onValueChange={(value) => value && setViewMode(value as 'grid' | 'table')}
- >
- <ToggleGroupItem value="grid" size="sm" className="h-[26px] w-[26px] p-0">
- <Grid size={14} strokeWidth={1.5} />
- </ToggleGroupItem>
- <ToggleGroupItem value="table" size="sm" className="h-[26px] w-[26px] p-0">
- <List size={14} strokeWidth={1.5} />
- </ToggleGroupItem>
- </ToggleGroup>
- )}
- {projectCreationEnabled && !hideNewProject && (
- <Button asChild icon={<Plus />} type="primary" size="tiny">
- <Link href={`/new/${slug}`}>New project</Link>
- </Button>
- )}
- </div>
- </div>
- )
- }
|