Skip to content

Commit bb2ff77

Browse files
committed
Move logic to the page
1 parent c7ac047 commit bb2ff77

File tree

3 files changed

+198
-137
lines changed

3 files changed

+198
-137
lines changed

site/src/pages/WorkspacesPage/Filter.tsx

Lines changed: 141 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import MenuList from "@mui/material/MenuList"
2020
import { useSearchParams } from "react-router-dom"
2121
import { useQuery } from "@tanstack/react-query"
2222
import { getUsers, getTemplates } from "api/api"
23-
import { useOrganizationId } from "hooks/useOrganizationId"
2423

2524
/** Filter */
2625

@@ -94,6 +93,18 @@ type BaseOption = {
9493
value: string
9594
}
9695

96+
type OwnerOption = BaseOption & {
97+
avatarUrl?: string
98+
}
99+
100+
type StatusOption = BaseOption & {
101+
color: string
102+
}
103+
104+
type TemplateOption = BaseOption & {
105+
icon?: string
106+
}
107+
97108
type UseAutocompleteOptions<TOption extends BaseOption> = {
98109
id: string
99110
initialQuery?: string
@@ -146,21 +157,118 @@ const useAutocomplete = <TOption extends BaseOption = BaseOption>({
146157
}
147158
}
148159

149-
/** Components */
160+
export const useUsersAutocomplete = (
161+
initialOptionValue: string | undefined,
162+
onChange: (option: OwnerOption | undefined) => void,
163+
) =>
164+
useAutocomplete({
165+
id: "owner",
166+
getInitialOption: async () => {
167+
const usersRes = await getUsers({ q: initialOptionValue, limit: 1 })
168+
const firstUser = usersRes.users.at(0)
169+
if (firstUser && firstUser.username === initialOptionValue) {
170+
return {
171+
label: firstUser.username,
172+
value: firstUser.username,
173+
avatarUrl: firstUser.avatar_url,
174+
}
175+
}
176+
return null
177+
},
178+
getOptions: async (query) => {
179+
const usersRes = await getUsers({ q: query, limit: 25 })
180+
return usersRes.users.map((user) => ({
181+
label: user.username,
182+
value: user.username,
183+
avatarUrl: user.avatar_url,
184+
}))
185+
},
186+
onChange,
187+
})
150188

151-
type OwnerOption = BaseOption & {
152-
avatarUrl?: string
153-
}
189+
type UsersAutocomplete = ReturnType<typeof useUsersAutocomplete>
154190

155-
type StatusOption = BaseOption & {
156-
color: string
191+
export const useTemplatesAutocomplete = (
192+
orgId: string,
193+
initialOptionValue: string | undefined,
194+
onChange: (option: TemplateOption | undefined) => void,
195+
) => {
196+
return useAutocomplete({
197+
id: "template",
198+
getInitialOption: async () => {
199+
const templates = await getTemplates(orgId)
200+
const template = templates.find(
201+
(template) => template.name === initialOptionValue,
202+
)
203+
if (template) {
204+
return {
205+
label:
206+
template.display_name !== ""
207+
? template.display_name
208+
: template.name,
209+
value: template.name,
210+
icon: template.icon,
211+
}
212+
}
213+
return null
214+
},
215+
getOptions: async (query) => {
216+
const templates = await getTemplates(orgId)
217+
const filteredTemplates = templates.filter(
218+
(template) =>
219+
template.name.toLowerCase().includes(query.toLowerCase()) ||
220+
template.display_name.toLowerCase().includes(query.toLowerCase()),
221+
)
222+
return filteredTemplates.map((template) => ({
223+
label:
224+
template.display_name !== "" ? template.display_name : template.name,
225+
value: template.name,
226+
icon: template.icon,
227+
}))
228+
},
229+
onChange,
230+
})
157231
}
158232

159-
type TemplateOption = BaseOption & {
160-
icon?: string
233+
type TemplatesAutocomplete = ReturnType<typeof useTemplatesAutocomplete>
234+
235+
export const useStatusAutocomplete = (
236+
initialOptionValue: string | undefined,
237+
onChange: (option: StatusOption | undefined) => void,
238+
) => {
239+
const statusOptions = WorkspaceStatuses.map((status) => {
240+
const display = getDisplayWorkspaceStatus(status)
241+
return {
242+
label: display.text,
243+
value: status,
244+
color: display.type ?? "warning",
245+
} as StatusOption
246+
})
247+
return useAutocomplete({
248+
id: "status",
249+
getInitialOption: async () =>
250+
statusOptions.find((option) => option.value === initialOptionValue) ??
251+
null,
252+
getOptions: async () => statusOptions,
253+
onChange,
254+
})
161255
}
162256

163-
export const Filter = ({ filter }: { filter: UseFilterResult }) => {
257+
type StatusAutocomplete = ReturnType<typeof useStatusAutocomplete>
258+
259+
/** Components */
260+
261+
export const Filter = ({
262+
filter,
263+
autocomplete,
264+
}: {
265+
filter: UseFilterResult
266+
autocomplete: {
267+
users: UsersAutocomplete
268+
templates: TemplatesAutocomplete
269+
status: StatusAutocomplete
270+
}
271+
}) => {
164272
const hasFilterQuery = filter.query !== ""
165273

166274
return (
@@ -200,56 +308,14 @@ export const Filter = ({ filter }: { filter: UseFilterResult }) => {
200308
),
201309
}}
202310
/>
203-
<OwnerFilter
204-
initialOptionValue={filter.values.owner}
205-
onChange={(option) =>
206-
filter.update({ ...filter.values, owner: option?.value })
207-
}
208-
/>
209-
<TemplateFilter
210-
initialOptionValue={filter.values.template}
211-
onChange={(option) =>
212-
filter.update({ ...filter.values, template: option?.value })
213-
}
214-
/>
215-
<StatusFilter
216-
initialOptionValue={filter.values.status}
217-
onChange={(option) =>
218-
filter.update({ ...filter.values, status: option?.value })
219-
}
220-
/>
311+
<OwnerFilter autocomplete={autocomplete.users} />
312+
<TemplatesFilter autocomplete={autocomplete.templates} />
313+
<StatusFilter autocomplete={autocomplete.status} />
221314
</Box>
222315
)
223316
}
224317

225-
const OwnerFilter: FC<{
226-
initialOptionValue?: string
227-
onChange: (option: OwnerOption | undefined) => void
228-
}> = ({ initialOptionValue, onChange }) => {
229-
const usersAutocomplete = useAutocomplete({
230-
id: "owner",
231-
getInitialOption: async () => {
232-
const usersRes = await getUsers({ q: initialOptionValue, limit: 1 })
233-
const firstUser = usersRes.users.at(0)
234-
if (firstUser && firstUser.username === initialOptionValue) {
235-
return {
236-
label: firstUser.username,
237-
value: firstUser.username,
238-
avatarUrl: firstUser.avatar_url,
239-
}
240-
}
241-
return null
242-
},
243-
getOptions: async (query) => {
244-
const usersRes = await getUsers({ q: query, limit: 25 })
245-
return usersRes.users.map((user) => ({
246-
label: user.username,
247-
value: user.username,
248-
avatarUrl: user.avatar_url,
249-
}))
250-
},
251-
onChange,
252-
})
318+
const OwnerFilter = ({ autocomplete }: { autocomplete: UsersAutocomplete }) => {
253319
const buttonRef = useRef<HTMLButtonElement>(null)
254320
const [isMenuOpen, setIsMenuOpen] = useState(false)
255321

@@ -267,13 +333,13 @@ const OwnerFilter: FC<{
267333
anchorEl={buttonRef.current}
268334
open={isMenuOpen}
269335
onClose={handleClose}
270-
options={usersAutocomplete.searchOptions}
336+
options={autocomplete.searchOptions}
271337
renderOption={(option) => (
272338
<MenuItem
273339
key={option.label}
274-
selected={option.value === usersAutocomplete.selectedOption?.value}
340+
selected={option.value === autocomplete.selectedOption?.value}
275341
onClick={() => {
276-
usersAutocomplete.selectOption(option)
342+
autocomplete.selectOption(option)
277343
handleClose()
278344
}}
279345
>
@@ -292,46 +358,11 @@ const OwnerFilter: FC<{
292358
)
293359
}
294360

295-
const TemplateFilter: FC<{
296-
initialOptionValue?: string
297-
onChange: (value: TemplateOption | undefined) => void
298-
}> = ({ initialOptionValue, onChange }) => {
299-
const orgId = useOrganizationId()
300-
const templatesAutocomplete = useAutocomplete({
301-
id: "template",
302-
getInitialOption: async () => {
303-
const templates = await getTemplates(orgId)
304-
const template = templates.find(
305-
(template) => template.name === initialOptionValue,
306-
)
307-
if (template) {
308-
return {
309-
label:
310-
template.display_name !== ""
311-
? template.display_name
312-
: template.name,
313-
value: template.name,
314-
icon: template.icon,
315-
}
316-
}
317-
return null
318-
},
319-
getOptions: async (query) => {
320-
const templates = await getTemplates(orgId)
321-
const filteredTemplates = templates.filter(
322-
(template) =>
323-
template.name.toLowerCase().includes(query.toLowerCase()) ||
324-
template.display_name.toLowerCase().includes(query.toLowerCase()),
325-
)
326-
return filteredTemplates.map((template) => ({
327-
label:
328-
template.display_name !== "" ? template.display_name : template.name,
329-
value: template.name,
330-
icon: template.icon,
331-
}))
332-
},
333-
onChange,
334-
})
361+
const TemplatesFilter = ({
362+
autocomplete,
363+
}: {
364+
autocomplete: TemplatesAutocomplete
365+
}) => {
335366
const buttonRef = useRef<HTMLButtonElement>(null)
336367
const [isMenuOpen, setIsMenuOpen] = useState(false)
337368

@@ -349,15 +380,13 @@ const TemplateFilter: FC<{
349380
anchorEl={buttonRef.current}
350381
open={isMenuOpen}
351382
onClose={handleClose}
352-
options={templatesAutocomplete.searchOptions}
383+
options={autocomplete.searchOptions}
353384
renderOption={(option) => (
354385
<MenuItem
355386
key={option.label}
356-
selected={
357-
option.value === templatesAutocomplete.selectedOption?.value
358-
}
387+
selected={option.value === autocomplete.selectedOption?.value}
359388
onClick={() => {
360-
templatesAutocomplete.selectOption(option)
389+
autocomplete.selectOption(option)
361390
handleClose()
362391
}}
363392
>
@@ -386,26 +415,11 @@ const TemplateAvatar: FC<
386415
)
387416
}
388417

389-
const StatusFilter: FC<{
390-
initialOptionValue?: string
391-
onChange: (value: StatusOption | undefined) => void
392-
}> = ({ initialOptionValue, onChange }) => {
393-
const statusOptions = WorkspaceStatuses.map((status) => {
394-
const display = getDisplayWorkspaceStatus(status)
395-
return {
396-
label: display.text,
397-
value: status,
398-
color: display.type ?? "warning",
399-
} as StatusOption
400-
})
401-
const statusAutocomplete = useAutocomplete({
402-
id: "status",
403-
getInitialOption: async () =>
404-
statusOptions.find((option) => option.value === initialOptionValue) ??
405-
null,
406-
getOptions: async () => statusOptions,
407-
onChange,
408-
})
418+
const StatusFilter = ({
419+
autocomplete,
420+
}: {
421+
autocomplete: StatusAutocomplete
422+
}) => {
409423
const buttonRef = useRef<HTMLButtonElement>(null)
410424
const [isMenuOpen, setIsMenuOpen] = useState(false)
411425

@@ -424,12 +438,12 @@ const StatusFilter: FC<{
424438
open={isMenuOpen}
425439
onClose={handleClose}
426440
>
427-
{statusAutocomplete.searchOptions?.map((option) => (
441+
{autocomplete.searchOptions?.map((option) => (
428442
<MenuItem
429443
key={option.label}
430-
selected={option.value === statusAutocomplete.selectedOption?.value}
444+
selected={option.value === autocomplete.selectedOption?.value}
431445
onClick={() => {
432-
statusAutocomplete.selectOption(option)
446+
autocomplete.selectOption(option)
433447
handleClose()
434448
}}
435449
>

0 commit comments

Comments
 (0)