-
Notifications
You must be signed in to change notification settings - Fork 6
feat(UI): Device selector #839
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /** | ||
| * Copyright (C) 2025 Intel Corporation | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { DeviceOption } from '../device-selector.types'; | ||
|
|
||
| // TODO: Replace with $api.useSuspenseQuery('get', '/api/v1/system/devices', ...) once the endpoint is available. | ||
| const MOCK_DEVICES: DeviceOption[] = [ | ||
| { id: 'cpu', name: 'CPU' }, | ||
| { id: 'xpu', name: 'Intel GPU' }, | ||
| { id: 'cuda', name: 'NVIDIA GPU' }, | ||
| ]; | ||
|
|
||
| export const useGetDevices = (): DeviceOption[] => { | ||
| return MOCK_DEVICES; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /** | ||
| * Copyright (C) 2025 Intel Corporation | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { $api } from '@/api'; | ||
| import { useProjectIdentifier } from '@/hooks'; | ||
| import { getQueryKey } from '@/query-client'; | ||
| import { useQueryClient } from '@tanstack/react-query'; | ||
|
|
||
| export const useUpdateProjectDevice = () => { | ||
| const { projectId } = useProjectIdentifier(); | ||
| const queryClient = useQueryClient(); | ||
|
|
||
| // TODO: Replace with proper data type once the API is ready. | ||
| const mutation = $api.useMutation('put', '/api/v1/projects/{project_id}', { | ||
| meta: { | ||
| error: { notify: true }, | ||
| }, | ||
| }); | ||
|
|
||
| const updateDevice = (device: string): void => { | ||
| mutation.mutate( | ||
| { | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| body: { device } as any, | ||
| params: { path: { project_id: projectId } }, | ||
| }, | ||
|
Comment on lines
+15
to
+28
|
||
| { | ||
| onSuccess: async () => { | ||
| await queryClient.invalidateQueries({ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of invalidating queries here in |
||
| queryKey: getQueryKey([ | ||
| 'get', | ||
| '/api/v1/projects/{project_id}', | ||
| { params: { path: { project_id: projectId } } }, | ||
| ]), | ||
| }); | ||
| }, | ||
| } | ||
| ); | ||
| }; | ||
|
|
||
| return { | ||
| mutate: updateDevice, | ||
| isPending: mutation.isPending, | ||
| }; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * Copyright (C) 2025 Intel Corporation | ||||||||||||||||||||||||||||
| * SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { Key, Suspense } from 'react'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { useCurrentProject } from '@/hooks'; | ||||||||||||||||||||||||||||
| import { Item, Loading, Picker } from '@geti/ui'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { useGetDevices } from './api/use-get-devices'; | ||||||||||||||||||||||||||||
| import { useUpdateProjectDevice } from './api/use-update-project-device'; | ||||||||||||||||||||||||||||
| import { DeviceOption } from './device-selector.types'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const AUTO_DEVICE: DeviceOption = { id: 'auto', name: 'Auto' }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export const DeviceSelector = () => { | ||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
| <Suspense fallback={<Loading size={'S'} />}> | ||||||||||||||||||||||||||||
| <DeviceSelectorContent /> | ||||||||||||||||||||||||||||
| </Suspense> | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const DeviceSelectorContent = () => { | ||||||||||||||||||||||||||||
| const devices = useGetDevices(); | ||||||||||||||||||||||||||||
| const { data: _project } = useCurrentProject(); | ||||||||||||||||||||||||||||
| const updateDevice = useUpdateProjectDevice(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const items: DeviceOption[] = [AUTO_DEVICE, ...devices]; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // TODO: Replace with actual project device when API is ready | ||||||||||||||||||||||||||||
| const selectedDevice = 'auto'; | ||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+33
|
||||||||||||||||||||||||||||
| const { data: _project } = useCurrentProject(); | |
| const updateDevice = useUpdateProjectDevice(); | |
| const items: DeviceOption[] = [AUTO_DEVICE, ...devices]; | |
| // TODO: Replace with actual project device when API is ready | |
| const selectedDevice = 'auto'; | |
| const { data: project } = useCurrentProject(); | |
| const updateDevice = useUpdateProjectDevice(); | |
| const items: DeviceOption[] = [AUTO_DEVICE, ...devices]; | |
| const selectedDevice = project?.device ?? 'auto'; |
Copilot
AI
Mar 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are existing UI tests for similar toolbar pickers, but this new selector has no tests covering its core behavior (rendering Auto + device options, reflecting project.device, and issuing the PUT update + invalidating the project query on change). Adding a device-selector.component.test.tsx (MSW-mocked) would prevent regressions.
Copilot
AI
Mar 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aria-label is currently set to a generic lowercase 'device'. For better screen reader output and consistency with other labels in this codebase, use a more descriptive label (e.g., "Device" or "Device selector"), or rely on the visible label prop if Picker already wires it up correctly.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| /** | ||
| * Copyright (C) 2025 Intel Corporation | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| export interface DeviceOption { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. most probably that type will come from |
||
| id: string; | ||
| name: string; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hook currently returns a hard-coded
MOCK_DEVICESlist. The issue/PR acceptance criteria requires fetching available devices fromGET /system/devicesand always including an Auto option, so this should be implemented against the real endpoint (and the UI should handle loading/error states accordingly) rather than shipping a mock.