Skip to content

Commit 17dae76

Browse files
committed
feat: add dropdown to select claim field value when sync field is set
1 parent 3897ea4 commit 17dae76

File tree

7 files changed

+117
-17
lines changed

7 files changed

+117
-17
lines changed

site/src/api/api.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,23 @@ class ApiMethods {
787787
return response.data;
788788
};
789789

790+
getIdpSyncClaimFieldValues = async (claimField: string) => {
791+
const response = await this.axios.get<string[]>(
792+
`/api/v2/settings/idpsync/field-values?claimField=${claimField}`,
793+
);
794+
return response.data;
795+
};
796+
797+
getIdpSyncClaimFieldValuesByOrganization = async (
798+
organization: string,
799+
claimField: string,
800+
) => {
801+
const response = await this.axios.get<TypesGen.Response>(
802+
`/api/v2/organizations/${organization}/settings/idpsync/field-values?claimField=${claimField}`,
803+
);
804+
return response.data;
805+
};
806+
790807
getTemplate = async (templateId: string): Promise<TypesGen.Template> => {
791808
const response = await this.axios.get<TypesGen.Template>(
792809
`/api/v2/templates/${templateId}`,

site/src/api/queries/organizations.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,3 +338,35 @@ export const organizationsPermissions = (
338338
},
339339
};
340340
};
341+
342+
export const getOrganizationIdpSyncClaimFieldValuesKey = (
343+
organization: string,
344+
claimField: string,
345+
) => [organization, claimField, "organizationIdpSyncClaimFieldValues"];
346+
347+
export const organizationIdpSyncClaimFieldValues = (
348+
organization: string,
349+
claimField: string,
350+
) => {
351+
return {
352+
queryKey: getOrganizationIdpSyncClaimFieldValuesKey(
353+
organization,
354+
claimField,
355+
),
356+
queryFn: () =>
357+
API.getIdpSyncClaimFieldValuesByOrganization(organization, claimField),
358+
};
359+
};
360+
361+
export const getIdpSyncClaimFieldValuesKey = (claimField: string) => [
362+
claimField,
363+
"idpSyncClaimFieldValues",
364+
];
365+
366+
export const idpSyncClaimFieldValues = (claimField: string) => {
367+
return {
368+
queryKey: getIdpSyncClaimFieldValuesKey(claimField),
369+
queryFn: () => API.getIdpSyncClaimFieldValues(claimField),
370+
enabled: !!claimField,
371+
};
372+
};

site/src/components/MultiSelectCombobox/MultiSelectCombobox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ export const MultiSelectCombobox = forwardRef<
572572
>
573573
<X className="h-5 w-5" />
574574
</button>
575-
<ChevronDown className="h-5 w-5 cursor-pointer text-content-secondary hover:text-content-primary" />
575+
<ChevronDown className="size-icon-sm cursor-pointer text-content-secondary hover:text-content-primary" />
576576
</div>
577577
</div>
578578
</div>

site/src/components/Select/Select.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@ export const SelectTrigger = React.forwardRef<
2020
<SelectPrimitive.Trigger
2121
ref={ref}
2222
className={cn(
23-
"flex h-10 w-full font-medium items-center justify-between whitespace-nowrap rounded-md ",
24-
"border border-border border-solid bg-transparent px-3 py-2 text-sm shadow-sm ",
25-
"ring-offset-background text-content-secondary placeholder:text-content-secondary focus:outline-none ",
26-
"focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
23+
`flex h-10 w-full font-medium items-center justify-between whitespace-nowrap rounded-md
24+
border border-border border-solid bg-transparent px-3 py-2 text-sm shadow-sm
25+
ring-offset-background text-content-secondary placeholder:text-content-secondary focus:outline-none,
26+
focus:ring-2 focus:ring-content-link disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1
27+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-content-link`,
2728
className,
2829
)}
2930
{...props}
3031
>
3132
{children}
3233
<SelectPrimitive.Icon asChild>
33-
<ChevronDown className="size-icon-sm opacity-50" />
34+
<ChevronDown className="size-icon-sm cursor-pointer text-content-secondary hover:text-content-primary" />
3435
</SelectPrimitive.Icon>
3536
</SelectPrimitive.Trigger>
3637
));
@@ -65,7 +66,7 @@ export const SelectScrollDownButton = React.forwardRef<
6566
)}
6667
{...props}
6768
>
68-
<ChevronDown className="size-icon-sm" />
69+
<ChevronDown className="size-icon-sm cursor-pointer text-content-secondary hover:text-content-primary" />
6970
</SelectPrimitive.ScrollDownButton>
7071
));
7172
SelectScrollDownButton.displayName =

site/src/modules/management/OrganizationSidebarView.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
SettingsSidebarNavItem,
1919
} from "components/Sidebar/Sidebar";
2020
import type { Permissions } from "contexts/auth/permissions";
21-
import { ChevronDown, Plus } from "lucide-react";
21+
import { Check, ChevronDown, Plus } from "lucide-react";
2222
import { useDashboard } from "modules/dashboard/useDashboard";
2323
import { type FC, useState } from "react";
2424
import { useNavigate } from "react-router-dom";
@@ -145,6 +145,13 @@ const OrganizationsSettingsNavigation: FC<
145145
<span className="truncate">
146146
{organization?.display_name || organization?.name}
147147
</span>
148+
{activeOrganization.name === organization.name && (
149+
<Check
150+
size={16}
151+
strokeWidth={2}
152+
className="ml-auto"
153+
/>
154+
)}
148155
</CommandItem>
149156
))}
150157
</div>

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
organizationIdpSyncSettings,
44
patchOrganizationSyncSettings,
55
} from "api/queries/idpsync";
6+
import { idpSyncClaimFieldValues } from "api/queries/organizations";
67
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne";
78
import { displayError } from "components/GlobalSnackbar/utils";
89
import { displaySuccess } from "components/GlobalSnackbar/utils";
@@ -11,7 +12,7 @@ import { Loader } from "components/Loader/Loader";
1112
import { Paywall } from "components/Paywall/Paywall";
1213
import { useDashboard } from "modules/dashboard/useDashboard";
1314
import { useFeatureVisibility } from "modules/dashboard/useFeatureVisibility";
14-
import { type FC, useEffect } from "react";
15+
import { type FC, useEffect, useState } from "react";
1516
import { Helmet } from "react-helmet-async";
1617
import { useMutation, useQuery, useQueryClient } from "react-query";
1718
import { docs } from "utils/docs";
@@ -29,6 +30,11 @@ export const IdpOrgSyncPage: FC = () => {
2930
isLoading,
3031
error,
3132
} = useQuery(organizationIdpSyncSettings(isIdpSyncEnabled));
33+
const [claimField, setClaimField] = useState("");
34+
35+
const { data: claimFieldValues } = useQuery(
36+
idpSyncClaimFieldValues(claimField),
37+
);
3238

3339
const patchOrganizationSyncSettingsMutation = useMutation(
3440
patchOrganizationSyncSettings(queryClient),
@@ -49,6 +55,10 @@ export const IdpOrgSyncPage: FC = () => {
4955
return <Loader />;
5056
}
5157

58+
const handleSyncFieldChange = (value: string) => {
59+
setClaimField(value);
60+
};
61+
5262
return (
5363
<>
5464
<Helmet>
@@ -94,6 +104,8 @@ export const IdpOrgSyncPage: FC = () => {
94104
);
95105
}
96106
}}
107+
onSyncFieldChange={handleSyncFieldChange}
108+
claimFieldValues={claimFieldValues}
97109
error={error || patchOrganizationSyncSettingsMutation.error}
98110
/>
99111
</Cond>

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

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ import {
3333
MultiSelectCombobox,
3434
type Option,
3535
} from "components/MultiSelectCombobox/MultiSelectCombobox";
36+
import {
37+
Select,
38+
SelectContent,
39+
SelectItem,
40+
SelectTrigger,
41+
SelectValue,
42+
} from "components/Select/Select";
3643
import { Spinner } from "components/Spinner/Spinner";
3744
import { Switch } from "components/Switch/Switch";
3845
import { useFormik } from "formik";
@@ -47,6 +54,8 @@ interface IdpSyncPageViewProps {
4754
organizationSyncSettings: OrganizationSyncSettings | undefined;
4855
organizations: readonly Organization[];
4956
onSubmit: (data: OrganizationSyncSettings) => void;
57+
onSyncFieldChange: (value: string) => void;
58+
claimFieldValues: string[] | undefined;
5059
error?: unknown;
5160
}
5261

@@ -76,6 +85,8 @@ export const IdpOrgSyncPageView: FC<IdpSyncPageViewProps> = ({
7685
organizationSyncSettings,
7786
organizations,
7887
onSubmit,
88+
onSyncFieldChange,
89+
claimFieldValues,
7990
error,
8091
}) => {
8192
const form = useFormik<OrganizationSyncSettings>({
@@ -135,6 +146,7 @@ export const IdpOrgSyncPageView: FC<IdpSyncPageViewProps> = ({
135146
value={form.values.field}
136147
onChange={(event) => {
137148
void form.setFieldValue("field", event.target.value);
149+
onSyncFieldChange(event.target.value);
138150
}}
139151
/>
140152
<Button
@@ -190,14 +202,33 @@ export const IdpOrgSyncPageView: FC<IdpSyncPageViewProps> = ({
190202
<Label className="text-sm" htmlFor={`${id}-idp-org-name`}>
191203
IdP organization name
192204
</Label>
193-
<Input
194-
id={`${id}-idp-org-name`}
195-
value={idpOrgName}
196-
className="min-w-72 w-72"
197-
onChange={(event) => {
198-
setIdpOrgName(event.target.value);
199-
}}
200-
/>
205+
206+
{claimFieldValues ? (
207+
<Select
208+
onValueChange={(event) => setIdpOrgName(event)}
209+
value={idpOrgName}
210+
>
211+
<SelectTrigger id={`${id}-idp-org-name`} className="w-72">
212+
<SelectValue placeholder="Select IdP organization" />
213+
</SelectTrigger>
214+
<SelectContent className="[&_*[role=option]>span]:end-2 [&_*[role=option]>span]:start-auto [&_*[role=option]]:pe-8 [&_*[role=option]]:ps-2">
215+
{claimFieldValues.map((value) => (
216+
<SelectItem key={value} value={value}>
217+
{value}
218+
</SelectItem>
219+
))}
220+
</SelectContent>
221+
</Select>
222+
) : (
223+
<Input
224+
id={`${id}-idp-org-name`}
225+
value={idpOrgName}
226+
className="w-72"
227+
onChange={(event) => {
228+
setIdpOrgName(event.target.value);
229+
}}
230+
/>
231+
)}
201232
</div>
202233
<div className="grid items-center gap-1 flex-1">
203234
<Label className="text-sm" htmlFor={`${id}-coder-org`}>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy