-
Notifications
You must be signed in to change notification settings - Fork 943
feat: implement CRUD UI for IDP organization sync settings #15503
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
Changes from 1 commit
d0baa8c
6463076
f714986
072d775
f6e286c
acf8cbe
ad1aa84
b01588d
6203d04
bc37998
2c2246d
6971f91
6e87130
a1b6e79
36c8706
a4e66c2
f7be49f
90a65fe
c282765
40155bc
a129820
8edd4b4
e54e5ca
bb16bd1
678a91d
a13d9b2
1b8efd1
137c25c
46a4646
85a455c
a9e06f1
d921b08
ba665b4
8a0e789
234c0fb
7d88a38
c6c9b16
5322a4e
eab8a0d
721d5d4
8986cbd
89a7b12
148dca7
0c338b0
0cadc8b
1d7153b
a914c54
bd307c0
95bfd17
82bd850
6eb6987
3822b2e
fe17b1c
e75e179
c6e07ff
a3b9b27
0601164
b01e5d3
1c5b6ef
6e2ee08
ae30db0
ac74cdc
a99dca5
4c00959
568a5a9
c313439
506223d
f223ab7
94ff4ca
9101e0e
5ca2c0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | |
}, | ||
// validationSchema, | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
onSubmit, | ||
enableReinitialize: true, | ||
}); | ||
const [coderOrgs, setCoderOrgs] = useState<Option[]>([]); | ||
const [idpOrgName, setIdpOrgName] = useState(""); | ||
|
@@ -108,130 +109,135 @@ export const IdpSyncPageView: FC<IdpSyncPageViewProps> = ({ | |
<> | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<Stack spacing={2}> | ||
<form onSubmit={form.handleSubmit}> | ||
<div className="flex flex-row"> | ||
<div className="grid items-center gap-1"> | ||
<Label className="text-sm" htmlFor="sync-field"> | ||
Organization sync field | ||
</Label> | ||
<div className="flex flex-row items-center gap-4"> | ||
<Input | ||
id="sync-field" | ||
value={syncSettings.field} | ||
className="w-72" | ||
onChange={async ( | ||
event: React.ChangeEvent<HTMLInputElement>, | ||
) => { | ||
setSyncSettings({ | ||
...(syncSettings as OrganizationSyncSettings), | ||
field: event.target.value, | ||
}); | ||
await form.setFieldValue("field", event.target.value); | ||
}} | ||
/> | ||
<div className="flex flex-row items-center gap-3"> | ||
<Switch | ||
id="organization-assign-default" | ||
checked={syncSettings.organization_assign_default} | ||
onCheckedChange={async (checked) => { | ||
<fieldset disabled={form.isSubmitting}> | ||
<div className="flex flex-row"> | ||
<div className="grid items-center gap-1"> | ||
<Label className="text-sm" htmlFor="sync-field"> | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Organization sync field 8000 | ||
</Label> | ||
<div className="flex flex-row items-center gap-4"> | ||
<Input | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
id="sync-field" | ||
value={syncSettings.field} | ||
className="w-72" | ||
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. When do we want to start using Tailwind's percentage-/proportion-based width values? Do they make sense here? 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. The current design lays out the UI elements very specifically. I think when we start to consider mobile and other screen sizes we could start considering this. |
||
onChange={async ( | ||
event: React.ChangeEvent<HTMLInputElement>, | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) => { | ||
setSyncSettings({ | ||
...(syncSettings as OrganizationSyncSettings), | ||
organization_assign_default: checked, | ||
field: event.target.value, | ||
}); | ||
await form.setFieldValue( | ||
"organization_assign_default", | ||
checked, | ||
); | ||
await form.setFieldValue("field", event.target.value); | ||
}} | ||
/> | ||
<Label htmlFor="organization-assign-default"> | ||
Assign Default Organization | ||
</Label> | ||
<div className="flex flex-row items-center gap-3"> | ||
<Switch | ||
id="organization-assign-default" | ||
checked={syncSettings.organization_assign_default} | ||
onCheckedChange={async (checked) => { | ||
setSyncSettings({ | ||
...(syncSettings as OrganizationSyncSettings), | ||
organization_assign_default: checked, | ||
}); | ||
await form.setFieldValue( | ||
"organization_assign_default", | ||
checked, | ||
); | ||
}} | ||
/> | ||
<Label htmlFor="organization-assign-default"> | ||
Assign Default Organization | ||
</Label> | ||
</div> | ||
</div> | ||
<p className="text-content-secondary text-2xs m-0"> | ||
If empty, organization sync is deactivated | ||
</p> | ||
</div> | ||
<p className="text-content-secondary text-2xs m-0"> | ||
If empty, organization sync is deactivated | ||
</p> | ||
</div> | ||
</div> | ||
|
||
<div className="flex flex-col gap-4"> | ||
<div className="flex flex-row pt-8 gap-2 justify-between"> | ||
<div className="grid items-center gap-1"> | ||
<Label className="text-sm" htmlFor="idp-organization-name"> | ||
IdP organization name | ||
</Label> | ||
<Input | ||
id="idp-organization-name" | ||
value={idpOrgName} | ||
className="min-w-72 w-72" | ||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { | ||
setIdpOrgName(event.target.value); | ||
<div className="flex flex-col gap-4"> | ||
<div className="flex flex-row pt-8 gap-2 justify-between"> | ||
<div className="grid items-center gap-1"> | ||
<Label className="text-sm" htmlFor="idp-organization-name"> | ||
IdP organization name | ||
</Label> | ||
<Input | ||
id="idp-organization-name" | ||
value={idpOrgName} | ||
className="min-w-72 w-72" | ||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => { | ||
setIdpOrgName(event.target.value); | ||
}} | ||
/> | ||
</div> | ||
<div className="grid items-center gap-1 flex-1"> | ||
<Label className="text-sm" htmlFor="idp-organization-name"> | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Coder organization | ||
</Label> | ||
<MultipleSelector | ||
className="min-w-96 max-w-3xl" | ||
value={coderOrgs} | ||
onChange={setCoderOrgs} | ||
defaultOptions={OPTIONS} | ||
hidePlaceholderWhenSelected | ||
placeholder="Select organization" | ||
emptyIndicator={ | ||
<p className="text-center text-lg leading-10 text-content-primary"> | ||
jaaydenh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
no results found. | ||
</p> | ||
} | ||
/> | ||
</div> | ||
<Button | ||
className="mb-px self-end" | ||
disabled={!idpOrgName || coderOrgs.length === 0} | ||
onClick={async () => { | ||
const newSyncSettings = { | ||
...(syncSettings as OrganizationSyncSettings), | ||
mapping: { | ||
...syncSettings?.mapping, | ||
[idpOrgName]: coderOrgs.map((org) => org.value), | ||
}, | ||
}; | ||
setSyncSettings(newSyncSettings); | ||
await form.setFieldValue( | ||
"mapping", | ||
newSyncSettings.mapping, | ||
); | ||
setIdpOrgName(""); | ||
setCoderOrgs([]); | ||
}} | ||
/> | ||
</div> | ||
<div className="grid items-center gap-1 flex-1"> | ||
<Label className="text-sm" htmlFor="idp-organization-name"> | ||
Coder organization | ||
</Label> | ||
<MultipleSelector | ||
className="min-w-96 max-w-3xl" | ||
value={coderOrgs} | ||
onChange={setCoderOrgs} | ||
defaultOptions={OPTIONS} | ||
hidePlaceholderWhenSelected | ||
placeholder="Select organization" | ||
emptyIndicator={ | ||
<p className="text-center text-lg leading-10 text-content-primary"> | ||
no results found. | ||
</p> | ||
} | ||
/> | ||
> | ||
<Plus size={14} /> | ||
Add IdP organization | ||
</Button> | ||
</div> | ||
<IdpMappingTable isEmpty={organizationMappingCount === 0}> | ||
{syncSettings?.mapping && | ||
Object.entries(syncSettings.mapping) | ||
.sort() | ||
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. I don't think there's ever a risk of this breaking, but could you add a comment saying that using the default behavior of stringifying the entries and comparing them that way is intentional? 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. Maybe the lines of code reference changed, but where are you referring to comparing that is happening here? |
||
.map(([idpOrg, organizations]) => ( | ||
<OrganizationRow | ||
key={idpOrg} | ||
idpOrg={idpOrg} | ||
coderOrgs={getOrgNames(organizations)} | ||
onDelete={handleDelete} | ||
/> | ||
))} | ||
</IdpMappingTable> | ||
<Button | ||
className="mb-px self-end" | ||
disabled={!idpOrgName || coderOrgs.length === 0} | ||
onClick={async () => { | ||
const newSyncSettings = { | ||
...(syncSettings as OrganizationSyncSettings), | ||
mapping: { | ||
...syncSettings?.mapping, | ||
[idpOrgName]: coderOrgs.map((org) => org.value), | ||
}, | ||
}; | ||
setSyncSettings(newSyncSettings); | ||
await form.setFieldValue("mapping", newSyncSettings.mapping); | ||
setIdpOrgName(""); | ||
setCoderOrgs([]); | ||
className="w-20" | ||
disabled={form.isSubmitting || !form.dirty} | ||
onClick={(event) => { | ||
event.preventDefault(); | ||
form.handleSubmit(); | ||
}} | ||
> | ||
<Plus size={14} /> | ||
Add IdP organization | ||
Save | ||
</Button> | ||
</div> | ||
<IdpMappingTable isEmpty={organizationMappingCount === 0}> | ||
{syncSettings?.mapping && | ||
Object.entries(syncSettings.mapping) | ||
.sort() | ||
.map(([idpOrg, organizations]) => ( | ||
<OrganizationRow | ||
key={idpOrg} | ||
idpOrg={idpOrg} | ||
coderOrgs={getOrgNames(organizations)} | ||
onDelete={handleDelete} | ||
/> | ||
))} | ||
</IdpMappingTable> | ||
<Button | ||
className="w-20" | ||
disabled={form.isSubmitting || !form.dirty} | ||
onClick={(event) => { | ||
event.preventDefault(); | ||
form.handleSubmit(); | ||
}} | ||
> | ||
Save | ||
</Button> | ||
</div> | ||
</fieldset> | ||
</form> | ||
</Stack> | ||
</> | ||
|
Uh oh!
There was an error while loading. Please reload this page.