forked from vercel/mongodb-starter
/
directory.tsx
127 lines (125 loc) · 5.04 KB
/
directory.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import { ResultProps } from '@/lib/api/user';
import Link from 'next/link';
import Image from 'next/image';
import useSWR from 'swr';
import fetcher from '@/lib/fetcher';
import { useDebounce } from '@/lib/hooks/use-debounce';
import { useState } from 'react';
import { CheckInCircleIcon } from '@/components/icons';
import { DirectoryIcon, SearchIcon } from '@/components/icons';
export default function Directory({
results,
totalUsers
}: {
results: ResultProps[];
totalUsers: number;
}) {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 200);
const { data } = useSWR<ResultProps[]>(
`api/user?query=${debouncedQuery}`,
fetcher,
{
fallbackData: results,
keepPreviousData: true
}
);
return (
<aside className="flex-shrink-0 w-full bg-black sm:w-96 h-full overflow-scroll border-r border-gray-800">
<div className="px-6 pt-6 pb-0 sticky top-0 bg-black z-20">
<Link href="/">
<a>
<div className="bg-dark-accent-1 hover:bg-dark-accent-2 transition-all rounded-2xl h-12 w-12 flex justify-center items-center">
<DirectoryIcon className="text-white" />
</div>
</a>
</Link>
<p className="mt-8 text-2xl text-white font-bold">Directory</p>
<p className="mt-2 text-sm text-dark-accent-5">
Search directory of {Intl.NumberFormat('en-us').format(totalUsers)}{' '}
developers
</p>
<form className="py-8 flex space-x-4" action="#">
<div className="flex-1 min-w-0">
<label htmlFor="search" className="sr-only">
Search
</label>
<div className="relative shadow-sm border-0 border-b-dark-accent-2 rounded-none border-b-[1px] ">
<div className="absolute bg-black inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<SearchIcon className="h-4 w-4 text-dark-accent-3" />
</div>
<input
type="search"
name="search"
id="search"
className="text-white placeholder:text-dark-accent-3 focus:ring-transparent border-none bg-black focus:border-transparent block w-full pl-10 sm:text-sm rounded-md"
placeholder="Search"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</div>
</div>
</form>
</div>
{/* Directory list */}
<nav
className="flex-1 min-h-0 overflow-y-auto overflow-x-hidden"
aria-label="Directory"
>
{data && data.length > 0 ? (
data.map(({ _id: letter, users }) => (
<div key={letter} className="relative">
<div className="bg-dark-accent-1 px-6 py-1 text-sm font-bold text-white uppercase">
<h3>{letter}</h3>
</div>
<ul role="list" className="relative z-0 directory-divide-y">
{users.map((user) => (
<li key={user.username}>
<Link href={`/${user.username}`}>
<a>
<div className="relative px-6 py-4 flex items-center space-x-3 focus-within:ring-0">
<div className="flex-shrink-0 h-12 w-12 rounded-full overflow-hidden">
<Image
src={user.image}
alt={user.name}
width={300}
height={300}
placeholder="blur"
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYV2PYsGHDfwAHNAMQumvbogAAAABJRU5ErkJggg=="
/>
</div>
<div className="flex-1 min-w-0">
{/* Extend touch target to entire panel */}
<span
className="absolute inset-0"
aria-hidden="true"
/>
<div className="flex items-center space-x-1">
<p className="text-sm font-medium text-white truncate">
{user.name}
</p>
{user.verified && (
<CheckInCircleIcon className="w-4 h-4 text-white" />
)}
</div>
<p className="text-sm text-dark-accent-5 truncate">
@{user.username}
</p>
</div>
</div>
</a>
</Link>
</li>
))}
</ul>
</div>
))
) : (
<div className="px-6 py-6">
<p className="text-center text-gray-500">No results found</p>
</div>
)}
</nav>
</aside>
);
}