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

Commit8c1bd32

Browse files
authored
feat(site): add basic organization management ui (#13288)
1 parent07cd9ac commit8c1bd32

File tree

32 files changed

+743
-187
lines changed

32 files changed

+743
-187
lines changed

‎codersdk/organizations.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ type OrganizationMemberWithName struct {
6666
typeCreateOrganizationRequeststruct {
6767
Namestring`json:"name" validate:"required,organization_name"`
6868
// DisplayName will default to the same value as `Name` if not provided.
69-
DisplayNamestring`json:"display_name" validate:"omitempty,organization_display_name"`
69+
DisplayNamestring`json:"display_name,omitempty" validate:"omitempty,organization_display_name"`
7070
Descriptionstring`json:"description,omitempty"`
7171
Iconstring`json:"icon,omitempty"`
7272
}

‎site/src/api/api.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,31 @@ class ApiMethods {
505505
returnresponse.data;
506506
};
507507

508+
createOrganization=async(params:TypesGen.CreateOrganizationRequest)=>{
509+
constresponse=awaitthis.axios.post<TypesGen.Organization>(
510+
"/api/v2/organizations",
511+
params,
512+
);
513+
returnresponse.data;
514+
};
515+
516+
updateOrganization=async(
517+
orgId:string,
518+
params:TypesGen.UpdateOrganizationRequest,
519+
)=>{
520+
constresponse=awaitthis.axios.patch<TypesGen.Organization>(
521+
`/api/v2/organizations/${orgId}`,
522+
params,
523+
);
524+
returnresponse.data;
525+
};
526+
527+
deleteOrganization=async(orgId:string)=>{
528+
awaitthis.axios.delete<TypesGen.Organization>(
529+
`/api/v2/organizations/${orgId}`,
530+
);
531+
};
532+
508533
getOrganization=async(
509534
organizationId:string,
510535
):Promise<TypesGen.Organization>=>{

‎site/src/api/queries/organizations.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
importtype{QueryClient}from"react-query";
2+
import{API}from"api/api";
3+
importtype{
4+
CreateOrganizationRequest,
5+
UpdateOrganizationRequest,
6+
}from"api/typesGenerated";
7+
import{meKey,myOrganizationsKey}from"./users";
8+
9+
exportconstcreateOrganization=(queryClient:QueryClient)=>{
10+
return{
11+
mutationFn:(params:CreateOrganizationRequest)=>
12+
API.createOrganization(params),
13+
14+
onSuccess:async()=>{
15+
awaitqueryClient.invalidateQueries(meKey);
16+
awaitqueryClient.invalidateQueries(myOrganizationsKey);
17+
},
18+
};
19+
};
20+
21+
interfaceUpdateOrganizationVariables{
22+
orgId:string;
23+
req:UpdateOrganizationRequest;
24+
}
25+
26+
exportconstupdateOrganization=(queryClient:QueryClient)=>{
27+
return{
28+
mutationFn:(variables:UpdateOrganizationVariables)=>
29+
API.updateOrganization(variables.orgId,variables.req),
30+
31+
onSuccess:async()=>{
32+
awaitqueryClient.invalidateQueries(myOrganizationsKey);
33+
},
34+
};
35+
};
36+
37+
exportconstdeleteOrganization=(queryClient:QueryClient)=>{
38+
return{
39+
mutationFn:(orgId:string)=>API.deleteOrganization(orgId),
40+
41+
onSuccess:async()=>{
42+
awaitqueryClient.invalidateQueries(meKey);
43+
awaitqueryClient.invalidateQueries(myOrganizationsKey);
44+
},
45+
};
46+
};

‎site/src/api/queries/users.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const authMethods = () => {
124124
};
125125
};
126126

127-
constmeKey=["me"];
127+
exportconstmeKey=["me"];
128128

129129
exportconstme=(metadata:MetadataState<User>)=>{
130130
returncachedQuery({
@@ -250,9 +250,11 @@ export const updateAppearanceSettings = (
250250
};
251251
};
252252

253+
exportconstmyOrganizationsKey=["organizations","me"]asconst;
254+
253255
exportconstmyOrganizations=()=>{
254256
return{
255-
queryKey:["organizations","me"],
257+
queryKey:myOrganizationsKey,
256258
queryFn:()=>API.getOrganizations(),
257259
};
258260
};

‎site/src/api/typesGenerated.ts

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎site/src/components/FormFooter/FormFooter.stories.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
1+
import{action}from"@storybook/addon-actions";
12
importtype{Meta,StoryObj}from"@storybook/react";
23
import{FormFooter}from"./FormFooter";
34

45
constmeta:Meta<typeofFormFooter>={
56
title:"components/FormFooter",
67
component:FormFooter,
8+
args:{
9+
isLoading:false,
10+
onCancel:action("onCancel"),
11+
},
712
};
813

914
exportdefaultmeta;
1015
typeStory=StoryObj<typeofFormFooter>;
1116

1217
exportconstReady:Story={
18+
args:{},
19+
};
20+
21+
exportconstNoCancel:Story={
1322
args:{
14-
isLoading:false,
23+
onCancel:undefined,
1524
},
1625
};
1726

1827
exportconstCustom:Story={
1928
args:{
20-
isLoading:false,
2129
submitLabel:"Create",
2230
},
2331
};

‎site/src/components/FormFooter/FormFooter.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface FormFooterStyles {
1414
}
1515

1616
exportinterfaceFormFooterProps{
17-
onCancel:()=>void;
17+
onCancel?:()=>void;
1818
isLoading:boolean;
1919
styles?:FormFooterStyles;
2020
submitLabel?:string;
@@ -45,15 +45,17 @@ export const FormFooter: FC<FormFooterProps> = ({
4545
>
4646
{submitLabel}
4747
</LoadingButton>
48-
<Button
49-
size="large"
50-
type="button"
51-
css={styles.button}
52-
onClick={onCancel}
53-
tabIndex={0}
54-
>
55-
{Language.cancelLabel}
56-
</Button>
48+
{onCancel&&(
49+
<Button
50+
size="large"
51+
type="button"
52+
css={styles.button}
53+
onClick={onCancel}
54+
tabIndex={0}
55+
>
56+
{Language.cancelLabel}
57+
</Button>
58+
)}
5759
{extraActions}
5860
</div>
5961
);

‎site/src/components/Margins/Margins.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,29 @@ const widthBySize: Record<Size, number> = {
1313
small:containerWidth/3,
1414
};
1515

16-
exportconstMargins:FC<JSX.IntrinsicElements["div"]&{size?:Size}>=({
16+
typeMarginsProps=JSX.IntrinsicElements["div"]&{
17+
size?:Size;
18+
};
19+
20+
exportconstMargins:FC<MarginsProps>=({
1721
size="regular",
22+
children,
1823
...divProps
1924
})=>{
2025
constmaxWidth=widthBySize[size];
2126
return(
2227
<div
2328
{...divProps}
2429
css={{
25-
margin:"0 auto",
30+
marginLeft:"auto",
31+
marginRight:"auto",
2632
maxWidth:maxWidth,
27-
padding:`0${sidePadding}px`,
33+
paddingLeft:sidePadding,
34+
paddingRight:sidePadding,
2835
width:"100%",
2936
}}
30-
/>
37+
>
38+
{children}
39+
</div>
3140
);
3241
};

‎site/src/modules/dashboard/Navbar/DeploymentDropdown.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,25 @@ import {
1313
import{USERS_LINK}from"modules/navigation";
1414

1515
interfaceDeploymentDropdownProps{
16-
canViewAuditLog:boolean;
1716
canViewDeployment:boolean;
17+
canViewOrganizations:boolean;
1818
canViewAllUsers:boolean;
19+
canViewAuditLog:boolean;
1920
canViewHealth:boolean;
2021
}
2122

2223
exportconstDeploymentDropdown:FC<DeploymentDropdownProps>=({
23-
canViewAuditLog,
2424
canViewDeployment,
25+
canViewOrganizations,
2526
canViewAllUsers,
27+
canViewAuditLog,
2628
canViewHealth,
2729
})=>{
2830
consttheme=useTheme();
2931

3032
if(
3133
!canViewAuditLog&&
34+
!canViewOrganizations&&
3235
!canViewDeployment&&
3336
!canViewAllUsers&&
3437
!canViewHealth
@@ -64,9 +67,10 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
6467
}}
6568
>
6669
<DeploymentDropdownContent
67-
canViewAuditLog={canViewAuditLog}
6870
canViewDeployment={canViewDeployment}
71+
canViewOrganizations={canViewOrganizations}
6972
canViewAllUsers={canViewAllUsers}
73+
canViewAuditLog={canViewAuditLog}
7074
canViewHealth={canViewHealth}
7175
/>
7276
</PopoverContent>
@@ -75,9 +79,10 @@ export const DeploymentDropdown: FC<DeploymentDropdownProps> = ({
7579
};
7680

7781
constDeploymentDropdownContent:FC<DeploymentDropdownProps>=({
78-
canViewAuditLog,
7982
canViewDeployment,
83+
canViewOrganizations,
8084
canViewAllUsers,
85+
canViewAuditLog,
8186
canViewHealth,
8287
})=>{
8388
constpopover=usePopover();
@@ -96,6 +101,16 @@ const DeploymentDropdownContent: FC<DeploymentDropdownProps> = ({
96101
Settings
97102
</MenuItem>
98103
)}
104+
{canViewOrganizations&&(
105+
<MenuItem
106+
component={NavLink}
107+
to="/organizations"
108+
css={styles.menuItem}
109+
onClick={onPopoverClose}
110+
>
111+
Organizations
112+
</MenuItem>
113+
)}
99114
{canViewAllUsers&&(
100115
<MenuItem
101116
component={NavLink}

‎site/src/modules/dashboard/Navbar/Navbar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const Navbar: FC = () => {
1212
const{ metadata}=useEmbeddedMetadata();
1313
constbuildInfoQuery=useQuery(buildInfo(metadata["build-info"]));
1414

15-
const{ appearance}=useDashboard();
15+
const{ appearance, experiments}=useDashboard();
1616
const{user:me, permissions, signOut}=useAuthenticated();
1717
constfeatureVisibility=useFeatureVisibility();
1818
constcanViewAuditLog=
@@ -29,10 +29,11 @@ export const Navbar: FC = () => {
2929
buildInfo={buildInfoQuery.data}
3030
supportLinks={appearance.support_links}
3131
onSignOut={signOut}
32-
canViewAuditLog={canViewAuditLog}
3332
canViewDeployment={canViewDeployment}
33+
canViewOrganizations={experiments.includes("multi-organization")}
3434
canViewAllUsers={canViewAllUsers}
3535
canViewHealth={canViewHealth}
36+
canViewAuditLog={canViewAuditLog}
3637
proxyContextValue={proxyContextValue}
3738
/>
3839
);

‎site/src/modules/dashboard/Navbar/NavbarView.test.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ describe("NavbarView", () => {
2828
proxyContextValue={proxyContextValue}
2929
user={MockUser}
3030
onSignOut={noop}
31-
canViewAuditLog
3231
canViewDeployment
32+
canViewOrganizations
3333
canViewAllUsers
3434
canViewHealth
35+
canViewAuditLog
3536
/>,
3637
);
3738
constworkspacesLink=awaitscreen.findByText(navLanguage.workspaces);
@@ -44,10 +45,11 @@ describe("NavbarView", () => {
4445
proxyContextValue={proxyContextValue}
4546
user={MockUser}
4647
onSignOut={noop}
47-
canViewAuditLog
4848
canViewDeployment
49+
canViewOrganizations
4950
canViewAllUsers
5051
canViewHealth
52+
canViewAuditLog
5153
/>,
5254
);
5355
consttemplatesLink=awaitscreen.findByText(navLanguage.templates);
@@ -60,10 +62,11 @@ describe("NavbarView", () => {
6062
proxyContextValue={proxyContextValue}
6163
user={MockUser}
6264
onSignOut={noop}
63-
canViewAuditLog
6465
canViewDeployment
66+
canViewOrganizations
6567
canViewAllUsers
6668
canViewHealth
69+
canViewAuditLog
6770
/>,
6871
);
6972
constdeploymentMenu=awaitscreen.findByText("Deployment");
@@ -78,10 +81,11 @@ describe("NavbarView", () => {
7881
proxyContextValue={proxyContextValue}
7982
user={MockUser}
8083
onSignOut={noop}
81-
canViewAuditLog
8284
canViewDeployment
85+
canViewOrganizations
8386
canViewAllUsers
8487
canViewHealth
88+
canViewAuditLog
8589
/>,
8690
);
8791
constdeploymentMenu=awaitscreen.findByText("Deployment");
@@ -96,10 +100,11 @@ describe("NavbarView", () => {
96100
proxyContextValue={proxyContextValue}
97101
user={MockUser}
98102
onSignOut={noop}
99-
canViewAuditLog
100103
canViewDeployment
104+
canViewOrganizations
101105
canViewAllUsers
102106
canViewHealth
107+
canViewAuditLog
103108
/>,
104109
);
105110
constdeploymentMenu=awaitscreen.findByText("Deployment");

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp