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

Commitfd75278

Browse files
committed
chore: extract Form components
1 parent1924b6a commitfd75278

File tree

4 files changed

+655
-639
lines changed

4 files changed

+655
-639
lines changed
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
importTableCellfrom"@mui/material/TableCell";
2+
importTableRowfrom"@mui/material/TableRow";
3+
importtype{
4+
Group,
5+
GroupSyncSettings,
6+
Organization,
7+
}from"api/typesGenerated";
8+
import{Button}from"components/Button/Button";
9+
import{
10+
HelpTooltip,
11+
HelpTooltipContent,
12+
HelpTooltipText,
13+
HelpTooltipTitle,
14+
HelpTooltipTrigger,
15+
}from"components/HelpTooltip/HelpTooltip";
16+
import{Input}from"components/Input/Input";
17+
import{Label}from"components/Label/Label";
18+
import{Link}from"components/Link/Link";
19+
import{
20+
MultiSelectCombobox,
21+
typeOption,
22+
}from"components/MultiSelectCombobox/MultiSelectCombobox";
23+
import{Switch}from"components/Switch/Switch";
24+
import{useFormik}from"formik";
25+
import{Plus,Trash}from"lucide-react";
26+
import{typeFC,useState}from"react";
27+
import{docs}from"utils/docs";
28+
import*asYupfrom"yup";
29+
import{ExportPolicyButton}from"./ExportPolicyButton";
30+
import{IdpMappingTable}from"./IdpMappingTable";
31+
import{IdpPillList}from"./IdpPillList";
32+
33+
interfaceIdpGroupSyncFormProps{
34+
groupSyncSettings:GroupSyncSettings;
35+
groupsMap:Map<string,string>;
36+
groups:Group[];
37+
groupMappingCount:number;
38+
legacyGroupMappingCount:number;
39+
organization:Organization;
40+
onSubmit:(data:GroupSyncSettings)=>void;
41+
}
42+
43+
constgroupSyncValidationSchema=Yup.object({
44+
field:Yup.string().trim(),
45+
regex_filter:Yup.string().trim(),
46+
auto_create_missing_groups:Yup.boolean(),
47+
mapping:Yup.object().shape({
48+
[`${String}`]:Yup.array().of(Yup.string()),
49+
}),
50+
});
51+
52+
exportconstIdpGroupSyncForm=({
53+
groupSyncSettings,
54+
groupMappingCount,
55+
legacyGroupMappingCount,
56+
groups,
57+
groupsMap,
58+
organization,
59+
onSubmit,
60+
}:IdpGroupSyncFormProps)=>{
61+
constform=useFormik<GroupSyncSettings>({
62+
initialValues:{
63+
field:groupSyncSettings?.field??"",
64+
regex_filter:groupSyncSettings?.regex_filter??"",
65+
auto_create_missing_groups:
66+
groupSyncSettings?.auto_create_missing_groups??false,
67+
mapping:groupSyncSettings?.mapping??{},
68+
},
69+
validationSchema:groupSyncValidationSchema,
70+
onSubmit,
71+
enableReinitialize:Boolean(groupSyncSettings),
72+
});
73+
const[idpGroupName,setIdpGroupName]=useState("");
74+
const[coderGroups,setCoderGroups]=useState<Option[]>([]);
75+
76+
constgetGroupNames=(groupIds:readonlystring[])=>{
77+
returngroupIds.map((groupId)=>groupsMap.get(groupId)||groupId);
78+
};
79+
80+
consthandleDelete=async(idpOrg:string)=>{
81+
constnewMapping=Object.fromEntries(
82+
Object.entries(form.values.mapping||{}).filter(
83+
([key])=>key!==idpOrg,
84+
),
85+
);
86+
constnewSyncSettings={
87+
...form.values,
88+
mapping:newMapping,
89+
};
90+
voidform.setFieldValue("mapping",newSyncSettings.mapping);
91+
form.handleSubmit();
92+
};
93+
94+
constSYNC_FIELD_ID="sync-field";
95+
constREGEX_FILTER_ID="regex-filter";
96+
constAUTO_CREATE_MISSING_GROUPS_ID="auto-create-missing-groups";
97+
constIDP_GROUP_NAME_ID="idp-group-name";
98+
99+
return(
100+
<formonSubmit={form.handleSubmit}>
101+
<fieldset
102+
disabled={form.isSubmitting}
103+
className="flex flex-col border-none gap-8 pt-2"
104+
>
105+
<divclassName="flex justify-end">
106+
<ExportPolicyButton
107+
syncSettings={groupSyncSettings}
108+
organization={organization}
109+
type="groups"
110+
/>
111+
</div>
112+
<divclassName="grid items-center gap-3">
113+
<divclassName="flex flex-row items-center gap-5">
114+
<divclassName="grid grid-cols-2 gap-2 grid-rows-[20px_auto_20px]">
115+
<LabelclassName="text-sm"htmlFor={SYNC_FIELD_ID}>
116+
Group sync field
117+
</Label>
118+
<LabelclassName="text-sm"htmlFor={SYNC_FIELD_ID}>
119+
Regex filter
120+
</Label>
121+
<Input
122+
id={SYNC_FIELD_ID}
123+
value={form.values.field}
124+
onChange={async(event)=>{
125+
voidform.setFieldValue("field",event.target.value);
126+
}}
127+
className="min-w-72 w-72"
128+
/>
129+
<divclassName="flex flex-row gap-2">
130+
<Input
131+
id={REGEX_FILTER_ID}
132+
value={form.values.regex_filter??""}
133+
onChange={async(event)=>{
134+
voidform.setFieldValue("regex_filter",event.target.value);
135+
}}
136+
className="min-w-40"
137+
/>
138+
<Button
139+
className="w-20"
140+
type="submit"
141+
disabled={form.isSubmitting||!form.dirty}
142+
onClick={(event)=>{
143+
event.preventDefault();
144+
form.handleSubmit();
145+
}}
146+
>
147+
Save
148+
</Button>
149+
</div>
150+
<pclassName="text-content-secondary text-2xs m-0">
151+
If empty, group sync is deactivated
152+
</p>
153+
</div>
154+
</div>
155+
</div>
156+
<divclassName="flex flex-row items-center gap-3">
157+
<Switch
158+
id={AUTO_CREATE_MISSING_GROUPS_ID}
159+
checked={form.values.auto_create_missing_groups}
160+
onCheckedChange={async(checked)=>{
161+
voidform.setFieldValue("auto_create_missing_groups",checked);
162+
form.handleSubmit();
163+
}}
164+
/>
165+
<spanclassName="flex flex-row items-center gap-1">
166+
<LabelhtmlFor={AUTO_CREATE_MISSING_GROUPS_ID}>
167+
Auto create missing groups
168+
</Label>
169+
<AutoCreateMissingGroupsHelpTooltip/>
170+
</span>
171+
</div>
172+
<divclassName="flex flex-row gap-2 justify-between items-start">
173+
<divclassName="grid items-center gap-1">
174+
<LabelclassName="text-sm"htmlFor={IDP_GROUP_NAME_ID}>
175+
IdP group name
176+
</Label>
177+
<Input
178+
id={IDP_GROUP_NAME_ID}
179+
value={idpGroupName}
180+
className="min-w-72 w-72"
181+
onChange={(event)=>{
182+
setIdpGroupName(event.target.value);
183+
}}
184+
/>
185+
</div>
186+
<divclassName="grid items-center gap-1 flex-1">
187+
<LabelclassName="text-sm"htmlFor=":r1d:">
188+
Coder group
189+
</Label>
190+
<MultiSelectCombobox
191+
className="min-w-60 max-w-3xl"
192+
value={coderGroups}
193+
onChange={setCoderGroups}
194+
defaultOptions={groups.map((group)=>({
195+
label:group.display_name||group.name,
196+
value:group.id,
197+
}))}
198+
hidePlaceholderWhenSelected
199+
placeholder="Select group"
200+
emptyIndicator={
201+
<pclassName="text-center text-md text-content-primary">
202+
All groups selected
203+
</p>
204+
}
205+
/>
206+
</div>
207+
<divclassName="grid grid-rows-[28px_auto]">
208+
&nbsp;
209+
<Button
210+
className="mb-px"
211+
type="submit"
212+
disabled={!idpGroupName||coderGroups.length===0}
213+
onClick={async()=>{
214+
constnewSyncSettings={
215+
...form.values,
216+
mapping:{
217+
...form.values.mapping,
218+
[idpGroupName]:coderGroups.map((role)=>role.value),
219+
},
220+
};
221+
voidform.setFieldValue("mapping",newSyncSettings.mapping);
222+
form.handleSubmit();
223+
setIdpGroupName("");
224+
setCoderGroups([]);
225+
}}
226+
>
227+
<Plussize={14}/>
228+
Add IdP group
229+
</Button>
230+
</div>
231+
</div>
232+
233+
<divclassName="flex flex-col">
234+
<IdpMappingTabletype="Group"rowCount={groupMappingCount}>
235+
{groupSyncSettings?.mapping&&
236+
Object.entries(groupSyncSettings.mapping)
237+
.sort()
238+
.map(([idpGroup,groups])=>(
239+
<GroupRow
240+
key={idpGroup}
241+
idpGroup={idpGroup}
242+
coderGroup={getGroupNames(groups)}
243+
onDelete={handleDelete}
244+
/>
245+
))}
246+
</IdpMappingTable>
247+
248+
{groupSyncSettings?.legacy_group_name_mapping&&(
249+
<div>
250+
<LegacyGroupSyncHeader/>
251+
<IdpMappingTabletype="Group"rowCount={legacyGroupMappingCount}>
252+
{Object.entries(groupSyncSettings.legacy_group_name_mapping)
253+
.sort()
254+
.map(([idpGroup,groupId])=>(
255+
<GroupRow
256+
key={idpGroup}
257+
idpGroup={idpGroup}
258+
coderGroup={getGroupNames([groupId])}
259+
onDelete={handleDelete}
260+
/>
261+
))}
262+
</IdpMappingTable>
263+
</div>
264+
)}
265+
</div>
266+
</fieldset>
267+
</form>
268+
);
269+
};
270+
271+
interfaceGroupRowProps{
272+
idpGroup:string;
273+
coderGroup:readonlystring[];
274+
onDelete:(idpOrg:string)=>void;
275+
}
276+
277+
constGroupRow:FC<GroupRowProps>=({ idpGroup, coderGroup, onDelete})=>{
278+
return(
279+
<TableRowdata-testid={`group-${idpGroup}`}>
280+
<TableCell>{idpGroup}</TableCell>
281+
<TableCell>
282+
<IdpPillListroles={coderGroup}/>
283+
</TableCell>
284+
<TableCell>
285+
<Button
286+
variant="outline"
287+
className="w-8 h-8 min-w-10 text-content-primary"
288+
aria-label="delete"
289+
onClick={()=>onDelete(idpGroup)}
290+
>
291+
<Trash/>
292+
<spanclassName="sr-only">Delete IdP mapping</span>
293+
</Button>
294+
</TableCell>
295+
</TableRow>
296+
);
297+
};
298+
299+
constAutoCreateMissingGroupsHelpTooltip:FC=()=>{
300+
return(
301+
<HelpTooltip>
302+
<HelpTooltipTrigger/>
303+
<HelpTooltipContent>
304+
<HelpTooltipText>
305+
Enabling auto create missing groups will automatically create groups
306+
returned by the OIDC provider if they do not exist in Coder.
307+
</HelpTooltipText>
308+
</HelpTooltipContent>
309+
</HelpTooltip>
310+
);
311+
};
312+
313+
constLegacyGroupSyncHeader:FC=()=>{
314+
return(
315+
<h4className="text-xl font-medium">
316+
<divclassName="flex items-end gap-2">
317+
<span>Legacy group sync settings</span>
318+
<HelpTooltip>
319+
<HelpTooltipTrigger/>
320+
<HelpTooltipContent>
321+
<HelpTooltipTitle>Legacy group sync settings</HelpTooltipTitle>
322+
<HelpTooltipText>
323+
These settings were configured using environment variables, and
324+
only apply to the default organization. It is now recommended to
325+
configure IdP sync via the CLI or the UI, which enables sync to be
326+
configured for any organization, and for those settings to be
327+
persisted without manually setting environment variables.{" "}
328+
<Linkhref={docs("/admin/users/idp-sync")}>
329+
Learn more&hellip;
330+
</Link>
331+
</HelpTooltipText>
332+
</HelpTooltipContent>
333+
</HelpTooltip>
334+
</div>
335+
</h4>
336+
);
337+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp