| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- import { type ChangeEvent, useState } from 'react'
- import { getRouteApi } from '@tanstack/react-router'
- import { SlidersHorizontal, ArrowUpAZ, ArrowDownAZ } from 'lucide-react'
- import { Button } from '@/components/ui/button'
- import { Input } from '@/components/ui/input'
- import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
- } from '@/components/ui/select'
- import { Separator } from '@/components/ui/separator'
- import { ConfigDrawer } from '@/components/config-drawer'
- import { Header } from '@/components/layout/header'
- import { Main } from '@/components/layout/main'
- import { ProfileDropdown } from '@/components/profile-dropdown'
- import { Search } from '@/components/search'
- import { ThemeSwitch } from '@/components/theme-switch'
- import { apps } from './data/apps'
- const route = getRouteApi('/_authenticated/apps/')
- type AppType = 'all' | 'connected' | 'notConnected'
- const appText = new Map<AppType, string>([
- ['all', 'All Apps'],
- ['connected', 'Connected'],
- ['notConnected', 'Not Connected'],
- ])
- export function Apps() {
- const {
- filter = '',
- type = 'all',
- sort: initSort = 'asc',
- } = route.useSearch()
- const navigate = route.useNavigate()
- const [sort, setSort] = useState(initSort)
- const [appType, setAppType] = useState(type)
- const [searchTerm, setSearchTerm] = useState(filter)
- const filteredApps = apps
- .sort((a, b) =>
- sort === 'asc'
- ? a.name.localeCompare(b.name)
- : b.name.localeCompare(a.name)
- )
- .filter((app) =>
- appType === 'connected'
- ? app.connected
- : appType === 'notConnected'
- ? !app.connected
- : true
- )
- .filter((app) => app.name.toLowerCase().includes(searchTerm.toLowerCase()))
- const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {
- setSearchTerm(e.target.value)
- navigate({
- search: (prev) => ({
- ...prev,
- filter: e.target.value || undefined,
- }),
- })
- }
- const handleTypeChange = (value: AppType) => {
- setAppType(value)
- navigate({
- search: (prev) => ({
- ...prev,
- type: value === 'all' ? undefined : value,
- }),
- })
- }
- const handleSortChange = (sort: 'asc' | 'desc') => {
- setSort(sort)
- navigate({ search: (prev) => ({ ...prev, sort }) })
- }
- return (
- <>
- {/* ===== Top Heading ===== */}
- <Header>
- <Search className='me-auto' />
- <ThemeSwitch />
- <ConfigDrawer />
- <ProfileDropdown />
- </Header>
- {/* ===== Content ===== */}
- <Main fixed>
- <div>
- <h1 className='text-2xl font-bold tracking-tight'>
- App Integrations
- </h1>
- <p className='text-muted-foreground'>
- Here's a list of your apps for the integration!
- </p>
- </div>
- <div className='my-4 flex items-end justify-between sm:my-0 sm:items-center'>
- <div className='flex flex-col gap-4 sm:my-4 sm:flex-row'>
- <Input
- placeholder='Filter apps...'
- className='h-9 w-40 lg:w-62.5'
- value={searchTerm}
- onChange={handleSearch}
- />
- <Select value={appType} onValueChange={handleTypeChange}>
- <SelectTrigger className='w-36'>
- <SelectValue>{appText.get(appType)}</SelectValue>
- </SelectTrigger>
- <SelectContent>
- <SelectItem value='all'>All Apps</SelectItem>
- <SelectItem value='connected'>Connected</SelectItem>
- <SelectItem value='notConnected'>Not Connected</SelectItem>
- </SelectContent>
- </Select>
- </div>
- <Select value={sort} onValueChange={handleSortChange}>
- <SelectTrigger className='w-16'>
- <SelectValue>
- <SlidersHorizontal size={18} />
- </SelectValue>
- </SelectTrigger>
- <SelectContent align='end'>
- <SelectItem value='asc'>
- <div className='flex items-center gap-4'>
- <ArrowUpAZ size={16} />
- <span>Ascending</span>
- </div>
- </SelectItem>
- <SelectItem value='desc'>
- <div className='flex items-center gap-4'>
- <ArrowDownAZ size={16} />
- <span>Descending</span>
- </div>
- </SelectItem>
- </SelectContent>
- </Select>
- </div>
- <Separator className='shadow-sm' />
- <ul className='faded-bottom no-scrollbar grid gap-4 overflow-auto pt-4 pb-16 md:grid-cols-2 lg:grid-cols-3'>
- {filteredApps.map((app) => (
- <li
- key={app.name}
- className='rounded-lg border p-4 hover:shadow-md'
- >
- <div className='mb-8 flex items-center justify-between'>
- <div
- className={`flex size-10 items-center justify-center rounded-lg bg-muted p-2`}
- >
- {app.logo}
- </div>
- <Button
- variant='outline'
- size='sm'
- className={`${app.connected ? 'border border-blue-300 bg-blue-50 hover:bg-blue-100 dark:border-blue-700 dark:bg-blue-950 dark:hover:bg-blue-900' : ''}`}
- >
- {app.connected ? 'Connected' : 'Connect'}
- </Button>
- </div>
- <div>
- <h2 className='mb-1 font-semibold'>{app.name}</h2>
- <p className='line-clamp-2 text-gray-500'>{app.desc}</p>
- </div>
- </li>
- ))}
- </ul>
- </Main>
- </>
- )
- }
|