Skip to content

Commit 591a307

Browse files
committed
feat(FR-26): show error state on ProjectSelect when no accessible projects
When the current user has no accessible projects in the current domain, the project selector now indicates this with an error-colored border and an InfoCircleOutlined suffix icon. Hovering the icon shows a tooltip explaining the state. The guard suppresses the error look while the underlying select is `disabled` or `loading`, so the existing 'please select a domain first' warning in UpdateUsersModal and the project-switching spinner in the header keep their original UX.
1 parent 0af3b9a commit 591a307

23 files changed

Lines changed: 65 additions & 0 deletions

File tree

packages/backend.ai-ui/src/components/BAISelect.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,24 @@ const useStyles = createStyles(({ css, token }) => ({
4343
color: ${token.colorBgBase};
4444
}
4545
}
46+
47+
/* In ghost mode the border-color is hard-overridden with !important,
48+
which would otherwise swallow antd's status="error" red border.
49+
Restore the error treatment with a higher-specificity rule so a
50+
ghost select (e.g. the header ProjectSelect) can still surface an
51+
error state. */
52+
&.ant-select.ant-select-status-error {
53+
border-color: ${token.colorError} !important;
54+
55+
.ant-select-suffix {
56+
color: ${token.colorError};
57+
}
58+
59+
&:hover .ant-select-suffix,
60+
&:active .ant-select-suffix {
61+
color: ${token.colorError};
62+
}
63+
}
4664
`,
4765
customStyle: css`
4866
/* Hide selected value content (except search input) when the user is typing a search query.

react/src/components/ProjectSelect.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useSuspendedBackendaiClient } from '../hooks';
77
import { useCurrentUserInfo, useCurrentUserRole } from '../hooks/backendai';
88
import useControllableState_deprecated from '../hooks/useControllableState';
99
import { useCurrentUserProjectRoles } from '../hooks/useCurrentUserProjectRoles';
10+
import { InfoCircleOutlined } from '@ant-design/icons';
1011
import { theme, Tooltip } from 'antd';
1112
import { BAIFlex, BAISelect, BAISelectProps } from 'backend.ai-ui';
1213
import * as _ from 'lodash-es';
@@ -153,6 +154,13 @@ const ProjectSelect: React.FC<ProjectSelectProps> = ({
153154
},
154155
);
155156

157+
const showNoProjectError =
158+
!accessibleProjects?.length &&
159+
!selectProps.disabled &&
160+
!selectProps.loading;
161+
162+
const noAccessibleProjectsMessage = t('projectSelect.NoAccessibleProjects');
163+
156164
return (
157165
<BAISelect
158166
onChange={(value, option) => {
@@ -169,6 +177,24 @@ const ProjectSelect: React.FC<ProjectSelectProps> = ({
169177
options={
170178
_.size(groupOptions) > 1 ? groupOptions : groupOptions[0]?.options
171179
}
180+
status={showNoProjectError ? 'error' : selectProps.status}
181+
// Surface the empty-state reason on the focusable control itself,
182+
// not only on the non-focusable suffix icon. `tooltip` wraps the
183+
// whole BAISelect in an antd Tooltip (hover over the entire control,
184+
// not just the tiny icon), and `aria-label` gives keyboard /
185+
// screen-reader users a persistent accessible description that does
186+
// not depend on the tooltip being open.
187+
tooltip={
188+
showNoProjectError ? noAccessibleProjectsMessage : selectProps.tooltip
189+
}
190+
aria-label={
191+
showNoProjectError
192+
? noAccessibleProjectsMessage
193+
: selectProps['aria-label']
194+
}
195+
suffixIcon={
196+
showNoProjectError ? <InfoCircleOutlined /> : selectProps.suffixIcon
197+
}
172198
/>
173199
);
174200
};

resources/i18n/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,7 @@
21132113
"RoleMember": "Mitglied"
21142114
},
21152115
"projectSelect": {
2116+
"NoAccessibleProjects": "Keine zugänglichen Projekte.",
21162117
"ProjectAdminBadge": "Projektadministrator"
21172118
},
21182119
"prometheusQueryPreset": {

resources/i18n/el.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,7 @@
21112111
"RoleMember": "Μέλος"
21122112
},
21132113
"projectSelect": {
2114+
"NoAccessibleProjects": "Δεν υπάρχουν προσβάσιμα έργα.",
21142115
"ProjectAdminBadge": "Διαχειριστής έργου"
21152116
},
21162117
"prometheusQueryPreset": {

resources/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,7 @@
21302130
"RoleMember": "Member"
21312131
},
21322132
"projectSelect": {
2133+
"NoAccessibleProjects": "No accessible projects.",
21332134
"ProjectAdminBadge": "Project Admin"
21342135
},
21352136
"prometheusQueryPreset": {

resources/i18n/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,7 @@
21112111
"RoleMember": "Miembro"
21122112
},
21132113
"projectSelect": {
2114+
"NoAccessibleProjects": "No hay proyectos accesibles.",
21142115
"ProjectAdminBadge": "Administrador del proyecto"
21152116
},
21162117
"prometheusQueryPreset": {

resources/i18n/fi.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,7 @@
21112111
"RoleMember": "Jäsen"
21122112
},
21132113
"projectSelect": {
2114+
"NoAccessibleProjects": "Ei saatavilla olevia projekteja.",
21142115
"ProjectAdminBadge": "Projektin ylläpitäjä"
21152116
},
21162117
"prometheusQueryPreset": {

resources/i18n/fr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,7 @@
21132113
"RoleMember": "Membre"
21142114
},
21152115
"projectSelect": {
2116+
"NoAccessibleProjects": "Aucun projet accessible.",
21162117
"ProjectAdminBadge": "Administrateur de projet"
21172118
},
21182119
"prometheusQueryPreset": {

resources/i18n/id.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,7 @@
21142114
"RoleMember": "Anggota"
21152115
},
21162116
"projectSelect": {
2117+
"NoAccessibleProjects": "Tidak ada proyek yang dapat diakses.",
21172118
"ProjectAdminBadge": "Admin Proyek"
21182119
},
21192120
"prometheusQueryPreset": {

resources/i18n/it.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2111,6 +2111,7 @@
21112111
"RoleMember": "Membro"
21122112
},
21132113
"projectSelect": {
2114+
"NoAccessibleProjects": "Nessun progetto accessibile.",
21142115
"ProjectAdminBadge": "Amministratore del progetto"
21152116
},
21162117
"prometheusQueryPreset": {

0 commit comments

Comments
 (0)