From 0b80f60401b561d2dd9a9f9560a6af5a26a56cf1 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Tue, 14 Mar 2023 20:29:48 +0000 Subject: [PATCH 1/4] wip --- .../dashboard/src/teams/git-integrations/GitIntegrations.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/components/dashboard/src/teams/git-integrations/GitIntegrations.tsx b/components/dashboard/src/teams/git-integrations/GitIntegrations.tsx index 3557b8a09e838e..4bcbe90018d5d0 100644 --- a/components/dashboard/src/teams/git-integrations/GitIntegrations.tsx +++ b/components/dashboard/src/teams/git-integrations/GitIntegrations.tsx @@ -18,6 +18,7 @@ export const GitIntegrations: FunctionComponent = () => {
Git Integrations + Manage Git integrations for self-managed instances of GitLab, GitHub, or Bitbucket. From cf98571f97a45df93d6f750524adec739f54a5eb Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Tue, 14 Mar 2023 23:29:22 +0000 Subject: [PATCH 2/4] Adding error and warning support to Modal --- components/dashboard/src/components/Alert.tsx | 21 ++++++-- components/dashboard/src/components/Modal.tsx | 48 +++++++++++++++++-- .../git-integrations/GitIntegrationModal.tsx | 27 ++++------- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/components/dashboard/src/components/Alert.tsx b/components/dashboard/src/components/Alert.tsx index fa06fdc7686d9c..20d46aed4cd986 100644 --- a/components/dashboard/src/components/Alert.tsx +++ b/components/dashboard/src/components/Alert.tsx @@ -10,6 +10,7 @@ import { ReactComponent as Exclamation2 } from "../images/exclamation2.svg"; import { ReactComponent as InfoSvg } from "../images/info.svg"; import { ReactComponent as XSvg } from "../images/x.svg"; import { ReactComponent as Check } from "../images/check-circle.svg"; +import classNames from "classnames"; export type AlertType = // Green @@ -20,6 +21,8 @@ export type AlertType = | "info" // Red | "error" + // Dark Red + | "danger" // Blue | "message"; @@ -32,6 +35,7 @@ export interface AlertProps { onClose?: () => void; showIcon?: boolean; icon?: React.ReactNode; + rounded?: boolean; children?: React.ReactNode; } @@ -73,6 +77,12 @@ const infoMap: Record = { icon: , iconColor: "text-red-400", }, + danger: { + bgCls: "bg-red-600 dark:bg-red-600", + txtCls: "text-white", + icon: , + iconColor: "filter-brightness-10", + }, }; export default function Alert(props: AlertProps) { @@ -84,11 +94,16 @@ export default function Alert(props: AlertProps) { const info = infoMap[type]; const showIcon = props.showIcon ?? true; const light = props.light ?? false; + const rounded = props.rounded ?? true; return (
{showIcon && {props.icon ?? info.icon}} {props.children} diff --git a/components/dashboard/src/components/Modal.tsx b/components/dashboard/src/components/Modal.tsx index 6e8b7ae8e1975f..8b9caa668c9e4b 100644 --- a/components/dashboard/src/components/Modal.tsx +++ b/components/dashboard/src/components/Modal.tsx @@ -4,10 +4,11 @@ * See License.AGPL.txt in the project root for license information. */ -import { ReactNode, useEffect } from "react"; +import { FC, ReactNode, useEffect, useMemo } from "react"; import cn from "classnames"; import { getGitpodService } from "../service/service"; import { Heading2 } from "./typography/headings"; +import Alert from "./Alert"; type CloseModalManner = "esc" | "enter" | "x"; @@ -125,7 +126,7 @@ type ModalBodyProps = { export const ModalBody = ({ children, hideDivider = false, noScroll = false }: ModalBodyProps) => { return (
{ - return
{children}
; +export const ModalFooter: FC = ({ error, warning, children }) => { + // Inlining these to ensure error band covers modal left/right borders + const alertStyles = useMemo(() => ({ marginLeft: "-25px", marginRight: "-25px" }), []); + + const hasAlert = error || warning; + + return ( +
+ {hasAlert && ( +
+ +
+ )} +
{children}
+
+ ); +}; + +type ModalFooterAlertProps = { + error?: string; + warning?: string; +}; +const ModalFooterAlert: FC = ({ error, warning }) => { + if (error) { + return ( + + {error} + + ); + } + if (warning) { + return ( + + {warning} + + ); + } + + return null; }; diff --git a/components/dashboard/src/teams/git-integrations/GitIntegrationModal.tsx b/components/dashboard/src/teams/git-integrations/GitIntegrationModal.tsx index f4ff6596bbe706..485f4fa1d7fa75 100644 --- a/components/dashboard/src/teams/git-integrations/GitIntegrationModal.tsx +++ b/components/dashboard/src/teams/git-integrations/GitIntegrationModal.tsx @@ -6,7 +6,7 @@ import { AuthProviderEntry } from "@gitpod/gitpod-protocol"; import { FunctionComponent, useCallback, useMemo, useState } from "react"; -import Alert from "../../components/Alert"; +import { Button } from "../../components/Button"; import { InputField } from "../../components/forms/InputField"; import { SelectInputField } from "../../components/forms/SelectInputField"; import { TextInputField } from "../../components/forms/TextInputField"; @@ -16,7 +16,6 @@ import { useInvalidateOrgAuthProvidersQuery } from "../../data/auth-providers/or import { useUpsertOrgAuthProviderMutation } from "../../data/auth-providers/upsert-org-auth-provider-mutation"; import { useCurrentOrg } from "../../data/organizations/orgs-query"; import { useOnBlurError } from "../../hooks/use-onblur-error"; -import exclamation from "../../images/exclamation.svg"; import { openAuthorizeWindow } from "../../provider-utils"; import { getGitpodService, gitpodHostUrl } from "../../service/service"; @@ -104,6 +103,7 @@ export const GitIntegrationModal: FunctionComponent = (props) => { console.error("no current team selected"); return; } + // Set a saving state and clear any error message setSavingProvider(true); setErrorMessage(undefined); @@ -193,9 +193,6 @@ export const GitIntegrationModal: FunctionComponent = (props) => { > {isNew ? "New Git Integration" : "Git Integration"} - {!isNew && savedProvider?.status !== "verified" && ( - You need to activate this integration. - )}
Configure an integration with a self-managed instance of GitLab, GitHub, or Bitbucket.
@@ -239,22 +236,14 @@ export const GitIntegrationModal: FunctionComponent = (props) => { onBlur={clientSecretOnBlur} />
- - {errorMessage && ( -
- exclamation mark - {errorMessage} -
- )} - - + ); From 448ffbd3c8e4e3423131882bae45b120fb409857 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 15 Mar 2023 00:29:09 +0000 Subject: [PATCH 3/4] Updating headings for team settings pages --- .../dashboard/src/components/EmptyMessage.tsx | 37 +++++++++++++++++++ .../components/UsageBasedBillingConfig.tsx | 15 +++++--- .../src/teams/GitIntegrationsPage.tsx | 11 +++++- .../dashboard/src/teams/OrgSettingsPage.tsx | 7 ++-- components/dashboard/src/teams/SSO.tsx | 32 ++++++++-------- .../dashboard/src/teams/TeamBilling.tsx | 2 +- .../dashboard/src/teams/TeamSettings.tsx | 2 +- .../src/teams/TeamUsageBasedBilling.tsx | 7 ++-- .../GitIntegrationListItem.tsx | 8 ++-- .../git-integrations/GitIntegrations.tsx | 17 +-------- .../git-integrations/GitIntegrationsList.tsx | 32 ++++++++-------- 11 files changed, 99 insertions(+), 71 deletions(-) create mode 100644 components/dashboard/src/components/EmptyMessage.tsx diff --git a/components/dashboard/src/components/EmptyMessage.tsx b/components/dashboard/src/components/EmptyMessage.tsx new file mode 100644 index 00000000000000..f482e0d53a9e7e --- /dev/null +++ b/components/dashboard/src/components/EmptyMessage.tsx @@ -0,0 +1,37 @@ +/** + * 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 classNames from "classnames"; +import { FC, useCallback } from "react"; +import { Button } from "./Button"; +import { Heading2, Subheading } from "./typography/headings"; + +type Props = { + title: string; + subtitle?: string; + buttonText?: string; + onClick?: () => void; + className?: string; +}; +export const EmptyMessage: FC = ({ title, subtitle, buttonText, onClick, className }) => { + const handleClick = useCallback(() => { + onClick && onClick(); + }, [onClick]); + return ( +
+
+ {title} + {subtitle} + {buttonText && } +
+
+ ); +}; diff --git a/components/dashboard/src/components/UsageBasedBillingConfig.tsx b/components/dashboard/src/components/UsageBasedBillingConfig.tsx index 864eb3705521a7..f368749a5b0427 100644 --- a/components/dashboard/src/components/UsageBasedBillingConfig.tsx +++ b/components/dashboard/src/components/UsageBasedBillingConfig.tsx @@ -29,9 +29,10 @@ type PendingStripeSubscription = { pendingSince: number }; interface Props { attributionId?: string; + hideSubheading?: boolean; } -export default function UsageBasedBillingConfig({ attributionId }: Props) { +export default function UsageBasedBillingConfig({ attributionId, hideSubheading = false }: Props) { const location = useLocation(); const currentOrg = useCurrentOrg().data; const attrId = attributionId ? AttributionId.parse(attributionId) : undefined; @@ -170,11 +171,13 @@ export default function UsageBasedBillingConfig({ attributionId }: Props) { return (
- - {attributionId && AttributionId.parse(attributionId)?.kind === "user" - ? "Manage billing for your personal account." - : "Manage billing for your organization."} - + {!hideSubheading && ( + + {attributionId && AttributionId.parse(attributionId)?.kind === "user" + ? "Manage billing for your personal account." + : "Manage billing for your organization."} + + )}
{errorMessage && ( diff --git a/components/dashboard/src/teams/GitIntegrationsPage.tsx b/components/dashboard/src/teams/GitIntegrationsPage.tsx index b14cabc570e39e..8eb1b0a9a7fe45 100644 --- a/components/dashboard/src/teams/GitIntegrationsPage.tsx +++ b/components/dashboard/src/teams/GitIntegrationsPage.tsx @@ -24,11 +24,14 @@ export default function GitAuth() { export const OrgSettingsPageWrapper: FunctionComponent = ({ children }) => { const currentOrg = useCurrentOrg(); + const title = "Git Integrations"; + const subtitle = "Configure Git integrations for self-managed instances of GitLab, GitHub, or Bitbucket."; + // Render as much of the page as we can in a loading state to avoid content shift if (currentOrg.isLoading) { return (
-
+
@@ -40,5 +43,9 @@ export const OrgSettingsPageWrapper: FunctionComponent = ({ children }) => { return ; } - return {children}; + return ( + + {children} + + ); }; diff --git a/components/dashboard/src/teams/OrgSettingsPage.tsx b/components/dashboard/src/teams/OrgSettingsPage.tsx index 52239da28158c1..4ce86742ff0416 100644 --- a/components/dashboard/src/teams/OrgSettingsPage.tsx +++ b/components/dashboard/src/teams/OrgSettingsPage.tsx @@ -14,10 +14,12 @@ import { useCurrentOrg } from "../data/organizations/orgs-query"; import { getTeamSettingsMenu } from "./TeamSettings"; export interface OrgSettingsPageProps { + title: string; + subtitle: string; children: React.ReactNode; } -export function OrgSettingsPage({ children }: OrgSettingsPageProps) { +export function OrgSettingsPage({ title, subtitle, children }: OrgSettingsPageProps) { const org = useCurrentOrg(); const { oidcServiceEnabled, orgGitAuthProviders } = useFeatureFlags(); @@ -32,9 +34,6 @@ export function OrgSettingsPage({ children }: OrgSettingsPageProps) { [oidcServiceEnabled, orgGitAuthProviders, org.data], ); - const title = "Organization Settings"; - const subtitle = "Manage your organization's settings."; - // Render as much of the page as we can in a loading state to avoid content shift if (org.isLoading) { return ( diff --git a/components/dashboard/src/teams/SSO.tsx b/components/dashboard/src/teams/SSO.tsx index 231cdcdcc6a926..0d1649c693038c 100644 --- a/components/dashboard/src/teams/SSO.tsx +++ b/components/dashboard/src/teams/SSO.tsx @@ -17,11 +17,16 @@ import copy from "../images/copy.svg"; import exclamation from "../images/exclamation.svg"; import { OrgSettingsPage } from "./OrgSettingsPage"; import { Heading2, Subheading } from "../components/typography/headings"; +import { EmptyMessage } from "../components/EmptyMessage"; export default function SSO() { const currentOrg = useCurrentOrg(); - return {currentOrg.data && }; + return ( + + {currentOrg.data && } + + ); } function OIDCClients(props: { organizationId: string }) { @@ -85,11 +90,10 @@ function OIDCClients(props: { organizationId: string }) { {modal?.mode === "edit" && <>} {modal?.mode === "delete" && <>} + OpenID Connect providers + Configure single sign-on for your organization. +
-
- Single sign sign-on with OIDC - Setup SSO for your organization. -
{clientConfigs.length !== 0 ? (
{clientConfigs.length === 0 && ( -
-
-

No OIDC Clients

-
- Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor - invidunt ut labore et dolore magna aliquyam -
- -
-
+ setModal({ mode: "new" })} + /> )} diff --git a/components/dashboard/src/teams/TeamBilling.tsx b/components/dashboard/src/teams/TeamBilling.tsx index e47f1635d50392..3160514ac166b5 100644 --- a/components/dashboard/src/teams/TeamBilling.tsx +++ b/components/dashboard/src/teams/TeamBilling.tsx @@ -27,7 +27,7 @@ type PendingPlan = Plan & { pendingSince: number }; export default function TeamBillingPage() { return ( - + ); diff --git a/components/dashboard/src/teams/TeamSettings.tsx b/components/dashboard/src/teams/TeamSettings.tsx index ac51b45412d447..503cf44f77086f 100644 --- a/components/dashboard/src/teams/TeamSettings.tsx +++ b/components/dashboard/src/teams/TeamSettings.tsx @@ -109,7 +109,7 @@ export default function TeamSettings() { return ( <> - + Organization Name This is your organization's visible name within Gitpod. For example, the name of your company. diff --git a/components/dashboard/src/teams/TeamUsageBasedBilling.tsx b/components/dashboard/src/teams/TeamUsageBasedBilling.tsx index 89c94710ccdb9c..a3f66b195dfef1 100644 --- a/components/dashboard/src/teams/TeamUsageBasedBilling.tsx +++ b/components/dashboard/src/teams/TeamUsageBasedBilling.tsx @@ -5,7 +5,6 @@ */ import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution"; -import { Heading2 } from "../components/typography/headings"; import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode"; import UsageBasedBillingConfig from "../components/UsageBasedBillingConfig"; import { useCurrentOrg } from "../data/organizations/orgs-query"; @@ -19,8 +18,10 @@ export default function TeamUsageBasedBilling() { return ( <> - Organization Billing - + ); } diff --git a/components/dashboard/src/teams/git-integrations/GitIntegrationListItem.tsx b/components/dashboard/src/teams/git-integrations/GitIntegrationListItem.tsx index b2f364a436c782..cda38a56f6136a 100644 --- a/components/dashboard/src/teams/git-integrations/GitIntegrationListItem.tsx +++ b/components/dashboard/src/teams/git-integrations/GitIntegrationListItem.tsx @@ -47,7 +47,7 @@ export const GitIntegrationListItem: FunctionComponent = ({ provider }) = return ( <> - +
= ({ provider }) =  
- + {provider.type} - + {provider.host} - +
{showDeleteConfirmation && ( { const { data, isLoading } = useOrgAuthProvidersQuery(); - return ( -
-
-
- Git Integrations - - - Manage Git integrations for self-managed instances of GitLab, GitHub, or Bitbucket. - -
-
- - {isLoading ? : } -
- ); + return
{isLoading ? : }
; }; diff --git a/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx b/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx index e6f51a3943f769..7c8070a1964d09 100644 --- a/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx +++ b/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx @@ -6,8 +6,9 @@ import { AuthProviderEntry } from "@gitpod/gitpod-protocol"; import { FunctionComponent, useCallback, useState } from "react"; -import { ItemsList } from "../../components/ItemsList"; -import { Heading2 } from "../../components/typography/headings"; +import { Button } from "../../components/Button"; +import { EmptyMessage } from "../../components/EmptyMessage"; +import { Item, ItemField, ItemsList } from "../../components/ItemsList"; import { GitIntegrationListItem } from "./GitIntegrationListItem"; import { GitIntegrationModal } from "./GitIntegrationModal"; @@ -23,27 +24,24 @@ export const GitIntegrationsList: FunctionComponent = ({ providers }) => return ( <> {providers.length === 0 ? ( -
-
- - No Git Integrations - -
- In addition to the default Git Providers you can authorize with a self-hosted instance of a - provider. -
- -
-
+ ) : ( <>
- +
+ + + Provider Type + Host Name + {providers.map((p) => ( ))} From cd042bb2168587ff5bf95ac041f50aa8316d9b28 Mon Sep 17 00:00:00 2001 From: Brad Harris Date: Wed, 15 Mar 2023 15:53:11 +0000 Subject: [PATCH 4/4] copy updates --- components/dashboard/src/teams/GitIntegrationsPage.tsx | 4 ++-- components/dashboard/src/teams/SSO.tsx | 6 +++--- components/dashboard/src/teams/TeamSettings.tsx | 2 +- .../src/teams/git-integrations/GitIntegrationsList.tsx | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components/dashboard/src/teams/GitIntegrationsPage.tsx b/components/dashboard/src/teams/GitIntegrationsPage.tsx index 8eb1b0a9a7fe45..9d24ec9e35bdf0 100644 --- a/components/dashboard/src/teams/GitIntegrationsPage.tsx +++ b/components/dashboard/src/teams/GitIntegrationsPage.tsx @@ -24,8 +24,8 @@ export default function GitAuth() { export const OrgSettingsPageWrapper: FunctionComponent = ({ children }) => { const currentOrg = useCurrentOrg(); - const title = "Git Integrations"; - const subtitle = "Configure Git integrations for self-managed instances of GitLab, GitHub, or Bitbucket."; + const title = "Git Auth"; + const subtitle = "Configure Git Auth for GitLab, or Github."; // Render as much of the page as we can in a loading state to avoid content shift if (currentOrg.isLoading) { diff --git a/components/dashboard/src/teams/SSO.tsx b/components/dashboard/src/teams/SSO.tsx index 0d1649c693038c..5e4e0602248ccc 100644 --- a/components/dashboard/src/teams/SSO.tsx +++ b/components/dashboard/src/teams/SSO.tsx @@ -23,7 +23,7 @@ export default function SSO() { const currentOrg = useCurrentOrg(); return ( - + {currentOrg.data && } ); @@ -90,7 +90,7 @@ function OIDCClients(props: { organizationId: string }) { {modal?.mode === "edit" && <>} {modal?.mode === "delete" && <>} - OpenID Connect providers + OpenID Connect clients Configure single sign-on for your organization.
@@ -106,7 +106,7 @@ function OIDCClients(props: { organizationId: string }) { {clientConfigs.length === 0 && ( setModal({ mode: "new" })} /> diff --git a/components/dashboard/src/teams/TeamSettings.tsx b/components/dashboard/src/teams/TeamSettings.tsx index 503cf44f77086f..71450fd45835fb 100644 --- a/components/dashboard/src/teams/TeamSettings.tsx +++ b/components/dashboard/src/teams/TeamSettings.tsx @@ -37,7 +37,7 @@ export function getTeamSettingsMenu(params: { } if (orgGitAuthProviders) { result.push({ - title: "Git Integrations", + title: "Git Auth", link: [`/settings/git`], }); } diff --git a/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx b/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx index 7c8070a1964d09..7aa20efb9cfb98 100644 --- a/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx +++ b/components/dashboard/src/teams/git-integrations/GitIntegrationsList.tsx @@ -25,8 +25,8 @@ export const GitIntegrationsList: FunctionComponent = ({ providers }) => <> {providers.length === 0 ? (