Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7076c4e

Browse files
authored
feat: add combobox for selecting claim field value for group/role idp sync (#16459)
contributes tocoder/internal#330This is a followup to#16335This adds a combobox that swaps with the input for the idp group and idprole names when the sync field returns claim values from the claim fieldvalues endpoint. If no claim field values are returned then the inputremains instead of the combobox.<img width="806" alt="Screenshot 2025-02-05 at 20 50 48"src="https://github.com/user-attachments/assets/bff839f5-1a2f-4cc1-8933-73fde942bc75"/><img width="803" alt="Screenshot 2025-02-05 at 20 51 00"src="https://github.com/user-attachments/assets/e9c7ca33-136a-4962-b924-770a64658dc8"/>
1 parentd5595f8 commit7076c4e

File tree

7 files changed

+172
-76
lines changed

7 files changed

+172
-76
lines changed

‎site/src/components/Table/Table.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export const TableRow = React.forwardRef<
6868
ref={ref}
6969
className={cn(
7070
"border-0 border-b border-solid border-border transition-colors",
71-
"hover:bg-muted/50data-[state=selected]:bg-muted",
71+
"data-[state=selected]:bg-muted",
7272
className,
7373
)}
7474
{...props}

‎site/src/pages/DeploymentSettingsPage/IdpOrgSyncPage/IdpOrgSyncPageView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ const IdpMappingTable: FC<IdpMappingTableProps> = ({ isEmpty, children }) => {
367367
<TableRow>
368368
<TableCellwidth="45%">IdP organization</TableCell>
369369
<TableCellwidth="55%">Coder organization</TableCell>
370-
<TableCellwidth="10%"/>
370+
<TableCellwidth="5%"/>
371371
</TableRow>
372372
</TableHeader>
373373
<TableBody>

‎site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpGroupSyncForm.tsx

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
importTableCellfrom"@mui/material/TableCell";
2-
importTableRowfrom"@mui/material/TableRow";
31
importtype{
42
Group,
53
GroupSyncSettings,
64
Organization,
75
}from"api/typesGenerated";
86
import{Button}from"components/Button/Button";
7+
import{Combobox}from"components/Combobox/Combobox";
98
import{
109
HelpTooltip,
1110
HelpTooltipContent,
@@ -22,6 +21,7 @@ import {
2221
}from"components/MultiSelectCombobox/MultiSelectCombobox";
2322
import{Spinner}from"components/Spinner/Spinner";
2423
import{Switch}from"components/Switch/Switch";
24+
import{TableCell,TableRow}from"components/Table/Table";
2525
import{
2626
Tooltip,
2727
TooltipContent,
@@ -30,7 +30,7 @@ import {
3030
}from"components/Tooltip/Tooltip";
3131
import{useFormik}from"formik";
3232
import{Plus,Trash,TriangleAlert}from"lucide-react";
33-
import{typeFC,useId,useState}from"react";
33+
import{typeFC,typeKeyboardEventHandler,useId,useState}from"react";
3434
import{docs}from"utils/docs";
3535
import{isUUID}from"utils/uuid";
3636
import*asYupfrom"yup";
@@ -70,6 +70,7 @@ interface IdpGroupSyncFormProps {
7070
legacyGroupMappingCount:number;
7171
organization:Organization;
7272
onSubmit:(data:GroupSyncSettings)=>void;
73+
onSyncFieldChange:(value:string)=>void;
7374
}
7475

7576
exportconstIdpGroupSyncForm:FC<IdpGroupSyncFormProps>=({
@@ -81,6 +82,7 @@ export const IdpGroupSyncForm: FC<IdpGroupSyncFormProps> = ({
8182
groupsMap,
8283
organization,
8384
onSubmit,
85+
onSyncFieldChange,
8486
})=>{
8587
constform=useFormik<GroupSyncSettings>({
8688
initialValues:{
@@ -97,6 +99,8 @@ export const IdpGroupSyncForm: FC<IdpGroupSyncFormProps> = ({
9799
const[idpGroupName,setIdpGroupName]=useState("");
98100
const[coderGroups,setCoderGroups]=useState<Option[]>([]);
99101
constid=useId();
102+
const[comboInputValue,setComboInputValue]=useState("");
103+
const[open,setOpen]=useState(false);
100104

101105
constgetGroupNames=(groupIds:readonlystring[])=>{
102106
returngroupIds.map((groupId)=>groupsMap.get(groupId)||groupId);
@@ -116,6 +120,21 @@ export const IdpGroupSyncForm: FC<IdpGroupSyncFormProps> = ({
116120
form.handleSubmit();
117121
};
118122

123+
consthandleKeyDown:KeyboardEventHandler<HTMLInputElement>=(event)=>{
124+
if(
125+
event.key==="Enter"&&
126+
comboInputValue&&
127+
!claimFieldValues?.some(
128+
(value)=>value===comboInputValue.toLowerCase(),
129+
)
130+
){
131+
event.preventDefault();
132+
setIdpGroupName(comboInputValue);
133+
setComboInputValue("");
134+
setOpen(false);
135+
}
136+
};
137+
119138
return(
120139
<formonSubmit={form.handleSubmit}>
121140
<fieldset
@@ -143,6 +162,7 @@ export const IdpGroupSyncForm: FC<IdpGroupSyncFormProps> = ({
143162
value={form.values.field}
144163
onChange={(event)=>{
145164
voidform.setFieldValue("field",event.target.value);
165+
onSyncFieldChange(event.target.value);
146166
}}
147167
className="w-72"
148168
/>
@@ -202,14 +222,31 @@ export const IdpGroupSyncForm: FC<IdpGroupSyncFormProps> = ({
202222
<LabelclassName="text-sm"htmlFor={`${id}-idp-group-name`}>
203223
IdP group name
204224
</Label>
205-
<Input
206-
id={`${id}-idp-group-name`}
207-
value={idpGroupName}
208-
className="w-72"
209-
onChange={(event)=>{
210-
setIdpGroupName(event.target.value);
211-
}}
212-
/>
225+
{claimFieldValues ?(
226+
<Combobox
227+
value={idpGroupName}
228+
options={claimFieldValues}
229+
placeholder="Select IdP group"
230+
open={open}
231+
onOpenChange={setOpen}
232+
inputValue={comboInputValue}
233+
onInputChange={setComboInputValue}
234+
onKeyDown={handleKeyDown}
235+
onSelect={(value)=>{
236+
setIdpGroupName(value);
237+
setOpen(false);
238+
}}
239+
/>
240+
) :(
241+
<Input
242+
id={`${id}-idp-group-name`}
243+
value={idpGroupName}
244+
className="w-72"
245+
onChange={(event)=>{
246+
setIdpGroupName(event.target.value);
247+
}}
248+
/>
249+
)}
213250
</div>
214251
<divclassName="grid items-center gap-1 flex-1">
215252
<LabelclassName="text-sm"htmlFor={`${id}-coder-group`}>

‎site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpMappingTable.tsx

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
importTablefrom"@mui/material/Table";
2-
importTableBodyfrom"@mui/material/TableBody";
3-
importTableCellfrom"@mui/material/TableCell";
4-
importTableContainerfrom"@mui/material/TableContainer";
5-
importTableHeadfrom"@mui/material/TableHead";
6-
importTableRowfrom"@mui/material/TableRow";
71
import{ChooseOne,Cond}from"components/Conditionals/ChooseOne";
82
import{EmptyState}from"components/EmptyState/EmptyState";
93
import{Link}from"components/Link/Link";
4+
import{
5+
Table,
6+
TableBody,
7+
TableCell,
8+
TableHeader,
9+
TableRow,
10+
}from"components/Table/Table";
1011
importtype{FC}from"react";
1112
import{docs}from"utils/docs";
1213

@@ -22,48 +23,45 @@ export const IdpMappingTable: FC<IdpMappingTableProps> = ({
2223
children,
2324
})=>{
2425
return(
25-
<divclassName="flex flex-col w-full gap-2">
26-
<TableContainer>
27-
<Table>
28-
<TableHead>
29-
<TableRow>
30-
<TableCellwidth="45%">IdP{type.toLocaleLowerCase()}</TableCell>
31-
<TableCellwidth="55%">
32-
Coder{type.toLocaleLowerCase()}
33-
</TableCell>
34-
<TableCellwidth="10%"/>
35-
</TableRow>
36-
</TableHead>
37-
<TableBody>
38-
<ChooseOne>
39-
<Condcondition={rowCount===0}>
40-
<TableRow>
41-
<TableCellcolSpan={999}>
42-
<EmptyState
43-
message={`No${type.toLocaleLowerCase()} mappings`}
44-
isCompact
45-
cta={
46-
<Link
47-
href={docs(
48-
`/admin/users/idp-sync#${type.toLocaleLowerCase()}-sync`,
49-
)}
50-
>
51-
How to setup IdP{type.toLocaleLowerCase()} sync
52-
</Link>
53-
}
54-
/>
55-
</TableCell>
56-
</TableRow>
57-
</Cond>
58-
<Cond>{children}</Cond>
59-
</ChooseOne>
60-
</TableBody>
61-
</Table>
62-
</TableContainer>
26+
<divclassName="flex flex-col gap-2">
27+
<Table>
28+
<TableHeader>
29+
<TableRow>
30+
<TableCellwidth="45%">IdP{type.toLocaleLowerCase()}</TableCell>
31+
<TableCellwidth="55%">Coder{type.toLocaleLowerCase()}</TableCell>
32+
<TableCellwidth="5%"/>
33+
</TableRow>
34+
</TableHeader>
35+
<TableBody>
36+
<ChooseOne>
37+
<Condcondition={rowCount===0}>
38+
<TableRow>
39+
<TableCellcolSpan={999}>
40+
<EmptyState
41+
message={`No${type.toLocaleLowerCase()} mappings`}
42+
isCompact
43+
cta={
44+
<Link
45+
href={docs(
46+
`/admin/users/idp-sync#${type.toLocaleLowerCase()}-sync`,
47+
)}
48+
>
49+
How to setup IdP{type.toLocaleLowerCase()} sync
50+
</Link>
51+
}
52+
/>
53+
</TableCell>
54+
</TableRow>
55+
</Cond>
56+
<Cond>{children}</Cond>
57+
</ChooseOne>
58+
</TableBody>
59+
</Table>
6360
<divclassName="flex justify-end">
6461
<divclassName="text-content-secondary text-xs">
6562
Showing<strongclassName="text-content-primary">{rowCount}</strong>{" "}
66-
groups
63+
{type.toLocaleLowerCase()}
64+
{(rowCount===0||rowCount>1)&&"s"}
6765
</div>
6866
</div>
6967
</div>

‎site/src/pages/OrganizationSettingsPage/IdpSyncPage/IdpRoleSyncForm.tsx

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
importTableCellfrom"@mui/material/TableCell";
2-
importTableRowfrom"@mui/material/TableRow";
31
importtype{Organization,Role,RoleSyncSettings}from"api/typesGenerated";
42
import{Button}from"components/Button/Button";
3+
import{Combobox}from"components/Combobox/Combobox";
54
import{Input}from"components/Input/Input";
65
import{Label}from"components/Label/Label";
76
import{
87
MultiSelectCombobox,
98
typeOption,
109
}from"components/MultiSelectCombobox/MultiSelectCombobox";
1110
import{Spinner}from"components/Spinner/Spinner";
11+
import{TableCell,TableRow}from"components/Table/Table";
1212
import{
1313
Tooltip,
1414
TooltipContent,
@@ -17,7 +17,7 @@ import {
1717
}from"components/Tooltip/Tooltip";
1818
import{useFormik}from"formik";
1919
import{Plus,Trash,TriangleAlert}from"lucide-react";
20-
import{typeFC,useId,useState}from"react";
20+
import{typeFC,typeKeyboardEventHandler,useId,useState}from"react";
2121
import*asYupfrom"yup";
2222
import{ExportPolicyButton}from"./ExportPolicyButton";
2323
import{IdpMappingTable}from"./IdpMappingTable";
@@ -53,6 +53,7 @@ interface IdpRoleSyncFormProps {
5353
organization:Organization;
5454
roles:Role[];
5555
onSubmit:(data:RoleSyncSettings)=>void;
56+
onSyncFieldChange:(value:string)=>void;
5657
}
5758

5859
exportconstIdpRoleSyncForm:FC<IdpRoleSyncFormProps>=({
@@ -62,6 +63,7 @@ export const IdpRoleSyncForm: FC<IdpRoleSyncFormProps> = ({
6263
organization,
6364
roles,
6465
onSubmit,
66+
onSyncFieldChange,
6567
})=>{
6668
constform=useFormik<RoleSyncSettings>({
6769
initialValues:{
@@ -75,6 +77,8 @@ export const IdpRoleSyncForm: FC<IdpRoleSyncFormProps> = ({
7577
const[idpRoleName,setIdpRoleName]=useState("");
7678
const[coderRoles,setCoderRoles]=useState<Option[]>([]);
7779
constid=useId();
80+
const[comboInputValue,setComboInputValue]=useState("");
81+
const[open,setOpen]=useState(false);
7882

7983
consthandleDelete=async(idpOrg:string)=>{
8084
constnewMapping=Object.fromEntries(
@@ -90,6 +94,21 @@ export const IdpRoleSyncForm: FC<IdpRoleSyncFormProps> = ({
9094
form.handleSubmit();
9195
};
9296

97+
consthandleKeyDown:KeyboardEventHandler<HTMLInputElement>=(event)=>{
98+
if(
99+
event.key==="Enter"&&
100+
comboInputValue&&
101+
!claimFieldValues?.some(
102+
(value)=>value===comboInputValue.toLowerCase(),
103+
)
104+
){
105+
event.preventDefault();
106+
setIdpRoleName(comboInputValue);
107+
setComboInputValue("");
108+
setOpen(false);
109+
}
110+
};
111+
93112
return(
94113
<formonSubmit={form.handleSubmit}>
95114
<fieldset
@@ -114,6 +133,7 @@ export const IdpRoleSyncForm: FC<IdpRoleSyncFormProps> = ({
114133
value={form.values.field}
115134
onChange={(event)=>{
116135
voidform.setFieldValue("field",event.target.value);
136+
onSyncFieldChange(event.target.value);
117137
}}
118138
className="w-72"
119139
/>
@@ -143,14 +163,31 @@ export const IdpRoleSyncForm: FC<IdpRoleSyncFormProps> = ({
143163
<LabelclassName="text-sm"htmlFor={`${id}-idp-role-name`}>
144164
IdP role name
145165
</Label>
146-
<Input
147-
id={`${id}-idp-role-name`}
148-
value={idpRoleName}
149-
className="w-72"
150-
onChange={(event)=>{
151-
setIdpRoleName(event.target.value);
152-
}}
153-
/>
166+
{claimFieldValues ?(
167+
<Combobox
168+
value={idpRoleName}
169+
options={claimFieldValues}
170+
placeholder="Select IdP role"
171+
open={open}
172+
onOpenChange={setOpen}
173+
inputValue={comboInputValue}
174+
onInputChange={setComboInputValue}
175+
onKeyDown={handleKeyDown}
176+
onSelect={(value)=>{
177+
setIdpRoleName(value);
178+
setOpen(false);
179+
}}
180+
/>
181+
) :(
182+
<Input
183+
id={`${id}-idp-role-name`}
184+
value={idpRoleName}
185+
className="w-72"
186+
onChange={(event)=>{
187+
setIdpRoleName(event.target.value);
188+
}}
189+
/>
190+
)}
154191
</div>
155192
<divclassName="grid items-center gap-1 flex-1">
156193
<LabelclassName="text-sm"htmlFor={`${id}-coder-role`}>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp