Skip to content

Commit

Permalink
[dashboard] use organization v2 shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
svenefftinge committed Nov 6, 2023
1 parent 18e04fb commit 0fbe813
Show file tree
Hide file tree
Showing 53 changed files with 2,457 additions and 2,021 deletions.
6 changes: 3 additions & 3 deletions components/dashboard/src/components/AuthorizeGit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { AuthProviderInfo } from "@gitpod/gitpod-protocol";
import { FC, useCallback, useContext } from "react";
import { Link } from "react-router-dom";
import { useAuthProviders } from "../data/auth-providers/auth-provider-query";
import { useCurrentOrg } from "../data/organizations/orgs-query";
import { openAuthorizeWindow } from "../provider-utils";
import { getGitpodService } from "../service/service";
import { UserContext, useCurrentUser } from "../user-context";
import { Button } from "./Button";
import { Heading2, Heading3, Subheading } from "./typography/headings";
import classNames from "classnames";
import { iconForAuthProvider, simplifyProviderName } from "../provider-utils";
import { useIsOwner } from "../data/organizations/members-query";

export function useNeedsGitAuthorization() {
const authProviders = useAuthProviders();
Expand All @@ -28,7 +28,7 @@ export function useNeedsGitAuthorization() {

export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
const { setUser } = useContext(UserContext);
const org = useCurrentOrg();
const owner = useIsOwner();
const authProviders = useAuthProviders();
const updateUser = useCallback(() => {
getGitpodService().server.getLoggedInUser().then(setUser);
Expand Down Expand Up @@ -57,7 +57,7 @@ export const AuthorizeGit: FC<{ className?: string }> = ({ className }) => {
{verifiedProviders.length === 0 ? (
<>
<Heading3 className="pb-2">No Git integrations</Heading3>
{!!org.data?.isOwner ? (
{!!owner ? (
<div className="px-6">
<Subheading>You need to configure at least one Git integration.</Subheading>
<Link to="/settings/git">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import ContextMenu, { ContextMenuEntry } from "./ContextMenu";
import CaretDown from "../icons/CaretDown.svg";
import { WorkspaceGitStatus } from "@gitpod/public-api/lib/gitpod/experimental/v2/workspace_pb";
import { WorkspaceGitStatus } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";

export default function PendingChangesDropdown({ gitStatus }: { gitStatus?: WorkspaceGitStatus }) {
const headingStyle = "text-gray-500 dark:text-gray-400 text-left";
Expand Down
2 changes: 1 addition & 1 deletion components/dashboard/src/components/PrebuildLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { getGitpodService } from "../service/service";
import { PrebuildStatus } from "../projects/Prebuilds";
import { converter, workspaceClient } from "../service/public-api";
import { GetWorkspaceRequest, WorkspacePhase_Phase } from "@gitpod/public-api/lib/gitpod/experimental/v2/workspace_pb";
import { GetWorkspaceRequest, WorkspacePhase_Phase } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";

const WorkspaceLogs = React.lazy(() => import("./WorkspaceLogs"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Button } from "./Button";
import { useCreateHoldPaymentIntentMutation } from "../data/billing/create-hold-payment-intent-mutation";
import { useToast } from "./toasts/Toasts";
import { ProgressBar } from "./ProgressBar";
import { useListOrganizationMembers } from "../data/organizations/members-query";

const BASE_USAGE_LIMIT_FOR_STRIPE_USERS = 1000;

Expand All @@ -33,8 +34,9 @@ let didAlreadyCallSubscribe = false;

export default function UsageBasedBillingConfig({ hideSubheading = false }: Props) {
const currentOrg = useCurrentOrg().data;
const attrId = currentOrg ? AttributionId.create(currentOrg) : undefined;
const attrId = currentOrg ? AttributionId.createFromOrganizationId(currentOrg.id) : undefined;
const attributionId = attrId && AttributionId.render(attrId);
const members = useListOrganizationMembers().data;
const [showUpdateLimitModal, setShowUpdateLimitModal] = useState<boolean>(false);
const [stripeSubscriptionId, setStripeSubscriptionId] = useState<string | undefined>();
const [isLoadingStripeSubscription, setIsLoadingStripeSubscription] = useState<boolean>(true);
Expand Down Expand Up @@ -155,7 +157,7 @@ export default function UsageBasedBillingConfig({ hideSubheading = false }: Prop
// FIXME: Should we ask the customer to confirm or edit this default limit?
let limit = BASE_USAGE_LIMIT_FOR_STRIPE_USERS;
if (attrId?.kind === "team" && currentOrg) {
limit = BASE_USAGE_LIMIT_FOR_STRIPE_USERS * currentOrg.members.length;
limit = BASE_USAGE_LIMIT_FOR_STRIPE_USERS * (members?.length || 0);
}
const newLimit = await getGitpodService().server.subscribeToStripe(
attributionId,
Expand Down Expand Up @@ -190,7 +192,7 @@ export default function UsageBasedBillingConfig({ hideSubheading = false }: Prop
);
}
},
[attrId?.kind, attributionId, currentOrg, location.pathname, refreshSubscriptionDetails],
[members, attrId?.kind, attributionId, currentOrg, location.pathname, refreshSubscriptionDetails],
);

const showSpinner = !attributionId || isLoadingStripeSubscription || isCreatingSubscription;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* See License.AGPL.txt in the project root for license information.
*/

import { Organization } from "@gitpod/gitpod-protocol";
import { useMutation } from "@tanstack/react-query";
import { useOrganizationsInvalidator } from "./orgs-query";
import { publicApiTeamToProtocol, teamsService } from "../../service/public-api";
import { organizationClient } from "../../service/public-api";
import { Organization } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";

type CreateOrgArgs = Pick<Organization, "name">;

Expand All @@ -16,13 +16,12 @@ export const useCreateOrgMutation = () => {

return useMutation<Organization, Error, CreateOrgArgs>({
mutationFn: async ({ name }) => {
const { team } = await teamsService.createTeam({ name });
if (!team) {
throw new Error("Error creating team");
const { organization } = await organizationClient.createOrganization({ name });
if (!organization) {
throw new Error("Error creating organization");
}

const org = publicApiTeamToProtocol(team);
return org;
return organization;
},
onSuccess(newOrg) {
invalidateOrgs();
Expand Down
58 changes: 58 additions & 0 deletions components/dashboard/src/data/organizations/invite-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import { organizationClient } from "../../service/public-api";
import { useCurrentOrg } from "./orgs-query";

export function useInviteInvalidator() {
const organizationId = useCurrentOrg().data?.id;
const queryClient = useQueryClient();
return useCallback(() => {
queryClient.invalidateQueries(getQueryKey(organizationId));
}, [organizationId, queryClient]);
}

export function useInvitationId() {
const organizationId = useCurrentOrg().data?.id;
const query = useQuery<string, Error>(
getQueryKey(organizationId),
async () => {
const response = await organizationClient.getOrganizationInvitation({
organizationId,
});
return response.invitationId;
},
{
enabled: !!organizationId,
},
);
return query;
}

export function useResetInvitationId() {
const invalidate = useInviteInvalidator();
return useMutation<void, Error, string>({
mutationFn: async (orgId) => {
if (!orgId) {
throw new Error("No current organization selected");
}

await organizationClient.resetOrganizationInvitation({
organizationId: orgId,
});
//TODO update useInvitation Query
},
onSuccess(updatedOrg) {
invalidate();
},
});
}

function getQueryKey(organizationId: string | undefined) {
return ["invitationId", organizationId || "undefined"];
}
54 changes: 54 additions & 0 deletions components/dashboard/src/data/organizations/members-query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2023 Gitpod GmbH. All rights reserved.
* Licensed under the GNU Affero General Public License (AGPL).
* See License.AGPL.txt in the project root for license information.
*/

import { OrganizationMember, OrganizationRole } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import { organizationClient } from "../../service/public-api";
import { useCurrentUser } from "../../user-context";
import { useCurrentOrg } from "./orgs-query";

export function useOrganizationMembersInvalidator() {
const organizationId = useCurrentOrg().data?.id;
const queryClient = useQueryClient();
return useCallback(() => {
queryClient.invalidateQueries(getQueryKey(organizationId));
}, [organizationId, queryClient]);
}

export function useListOrganizationMembers() {
const organizationId = useCurrentOrg().data?.id;
const query = useQuery<OrganizationMember[], Error>(
getQueryKey(organizationId),
async () => {
const response = await organizationClient.listOrganizationMembers({
organizationId,
pagination: {
pageSize: 1000,
},
});
return response.members;
},
{
staleTime: 1000 * 60 * 5, // 5 minutes
enabled: !!organizationId,
},
);
return query;
}

export function useIsOwner(): boolean {
const user = useCurrentUser();
const members = useListOrganizationMembers();
const isOwner = useMemo(() => {
return members?.data?.some((m) => m.userId === user?.id && m.role === OrganizationRole.OWNER);
}, [members?.data, user?.id]);
return !!isOwner;
}

function getQueryKey(organizationId: string | undefined) {
return ["listOrganizationMembers", organizationId || "undefined"];
}
45 changes: 27 additions & 18 deletions components/dashboard/src/data/organizations/org-settings-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,38 @@
* See License.AGPL.txt in the project root for license information.
*/

import { OrganizationSettings } from "@gitpod/gitpod-protocol";
import { useQuery } from "@tanstack/react-query";
import { getGitpodService } from "../../service/service";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { organizationClient } from "../../service/public-api";
import { OrganizationSettings } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
import { useCallback } from "react";
import { useCurrentOrg } from "./orgs-query";

export type OrgSettingsResult = OrganizationSettings;
export function useOrgSettingsQueryInvalidator() {
const organizationId = useCurrentOrg().data?.id;
const queryClient = useQueryClient();
return useCallback(() => {
queryClient.invalidateQueries(getQueryKey(organizationId));
}, [organizationId, queryClient]);
}

export const useOrgSettingsQuery = () => {
const org = useCurrentOrg().data;

return useQuery<OrgSettingsResult>({
queryKey: getOrgSettingsQueryKey(org?.id ?? ""),
staleTime: 1000 * 60 * 1, // 1 minute
queryFn: async () => {
if (!org) {
export function useOrgSettingsQuery() {
const organizationId = useCurrentOrg().data?.id;
return useQuery<OrganizationSettings, Error>(
getQueryKey(organizationId),
async () => {
if (!organizationId) {
throw new Error("No org selected.");
}

const settings = await getGitpodService().server.getOrgSettings(org.id);
return settings || null;
const settings = await organizationClient.getOrganizationSettings({ organizationId });
return settings.settings || new OrganizationSettings();
},
{
enabled: !!organizationId,
},
enabled: !!org,
});
};
);
}

export const getOrgSettingsQueryKey = (teamId: string) => ["org-settings", { teamId }];
function getQueryKey(organizationId?: string) {
return ["getOrganizationSettings", organizationId || "undefined"];
}
30 changes: 7 additions & 23 deletions components/dashboard/src/data/organizations/orgs-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,14 @@
* See License.AGPL.txt in the project root for license information.
*/

import { Organization, OrgMemberInfo, User } from "@gitpod/gitpod-protocol";
import { User } from "@gitpod/gitpod-protocol";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback } from "react";
import { useLocation } from "react-router";
import { publicApiTeamMembersToProtocol, publicApiTeamToProtocol, teamsService } from "../../service/public-api";
import { organizationClient } from "../../service/public-api";
import { useCurrentUser } from "../../user-context";
import { noPersistence } from "../setup";

export interface OrganizationInfo extends Organization {
members: OrgMemberInfo[];
isOwner: boolean;
invitationId?: string;
}
import { Organization } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";

export function useOrganizationsInvalidator() {
const user = useCurrentUser();
Expand All @@ -29,7 +24,7 @@ export function useOrganizationsInvalidator() {

export function useOrganizations() {
const user = useCurrentUser();
const query = useQuery<OrganizationInfo[], Error>(
const query = useQuery<Organization[], Error>(
getQueryKey(user),
async () => {
console.log("Fetching orgs... " + JSON.stringify(getQueryKey(user)));
Expand All @@ -38,19 +33,8 @@ export function useOrganizations() {
return [];
}

const response = await teamsService.listTeams({});
const result: OrganizationInfo[] = [];
for (const org of response.teams) {
const members = publicApiTeamMembersToProtocol(org.members || []);
const isOwner = members.some((m) => m.role === "owner" && m.userId === user?.id);
result.push({
...publicApiTeamToProtocol(org),
members,
isOwner,
invitationId: org.teamInvitation?.id,
});
}
return result;
const response = await organizationClient.listOrganizations({});
return response.organizations;
},
{
enabled: !!user,
Expand All @@ -68,7 +52,7 @@ function getQueryKey(user?: User) {
}

// Custom hook to return the current org if one is selected
export function useCurrentOrg(): { data?: OrganizationInfo; isLoading: boolean } {
export function useCurrentOrg(): { data?: Organization; isLoading: boolean } {
const location = useLocation();
const orgs = useOrganizations();
const user = useCurrentUser();
Expand Down
Loading

0 comments on commit 0fbe813

Please sign in to comment.