diff --git a/changelog/v0.0.36/app-details-api-keys-section.yaml b/changelog/v0.0.36/app-details-api-keys-section.yaml new file mode 100644 index 00000000..214a08fd --- /dev/null +++ b/changelog/v0.0.36/app-details-api-keys-section.yaml @@ -0,0 +1,5 @@ +changelog: + - type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6881 + description: >- + Adds an API keys section to the App Details page. diff --git a/changelog/v0.0.36/apps-docs-loading-fixes.yaml b/changelog/v0.0.36/apps-docs-loading-fixes.yaml new file mode 100644 index 00000000..e6a5eaa2 --- /dev/null +++ b/changelog/v0.0.36/apps-docs-loading-fixes.yaml @@ -0,0 +1,17 @@ +changelog: + - type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6853 + description: >- + Fixes flickering loading state on landing pages. + - type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6852 + description: >- + Adds docs setup information to API details page. + - type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6851 + description: >- + Removes placeholder images for apps. + - type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6854 + description: >- + Adds information when in a logged out empty state on private Portals. diff --git a/changelog/v0.0.36/custom-pages.yaml b/changelog/v0.0.36/custom-pages.yaml new file mode 100644 index 00000000..504cc325 --- /dev/null +++ b/changelog/v0.0.36/custom-pages.yaml @@ -0,0 +1,5 @@ +changelog: + - type: FIX + issueLink: https://github.com/solo-io/solo-projects/issues/6860 + description: >- + Adds the ability for users to create custom pages that show up in the UI. diff --git a/changelog/v0.0.36/teams-page.yaml b/changelog/v0.0.36/teams-page.yaml new file mode 100644 index 00000000..ee5cb1d1 --- /dev/null +++ b/changelog/v0.0.36/teams-page.yaml @@ -0,0 +1,30 @@ +changelog: + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6808 + description: >- + Adds the Teams page in to the GG version of the UI. + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6809 + description: >- + Adds the Team Details page in to the GG version of the UI. + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6810 + description: >- + Adds the Apps page in to the GG version of the UI. + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6811 + description: >- + Adds the App Details page in to the GG version of the UI. + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6812 + description: >- + Adds the Admin Subscriptions page in to the GG version of the UI. + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6813 + description: >- + Adds the Admin Teams page in to the GG version of the UI. + - type: NEW_FEATURE + issueLink: https://github.com/solo-io/solo-projects/issues/6814 + description: >- + Adds in an edit button + modal to the Apps + Teams Details pages + (for both admins + non-admins) on the GG version of the UI. diff --git a/projects/ui/src/Apis/api-types.ts b/projects/ui/src/Apis/api-types.ts index f342a2f6..b3cc6376 100644 --- a/projects/ui/src/Apis/api-types.ts +++ b/projects/ui/src/Apis/api-types.ts @@ -108,6 +108,16 @@ export type App = { teamId: string; }; +export type ApiKey = { + id: string; + createdAt: string; + updatedAt: string; + deletedAt: string; + apiKey: string; + name: string; + metadata: Record; +}; + export type Team = { createdAt: string; description: string; diff --git a/projects/ui/src/Apis/gg_hooks.ts b/projects/ui/src/Apis/gg_hooks.ts index 9a83abf7..5b6b3143 100644 --- a/projects/ui/src/Apis/gg_hooks.ts +++ b/projects/ui/src/Apis/gg_hooks.ts @@ -4,6 +4,7 @@ import useSWRMutation from "swr/mutation"; import { AuthContext } from "../Context/AuthContext"; import { omitErrorMessageResponse } from "../Utility/utility"; import { + ApiKey, ApiProductDetails, ApiProductSummary, ApiVersion, @@ -19,15 +20,15 @@ import { import { fetchJSON, useMultiSwrWithAuth, useSwrWithAuth } from "./utility"; // -// Queries +// region Queries // -// User +// region User export function useGetCurrentUser() { return useSwrWithAuth("/me"); } -// Apps +// region Apps + API Keys export function useListAppsForTeam(team: Team) { return useSwrWithAuth(`/teams/${team.id}/apps`); } @@ -55,8 +56,11 @@ export function useListFlatAppsForTeamsOmitErrors(teams: Team[]) { export function useGetAppDetails(id?: string) { return useSwrWithAuth(`/apps/${id}`); } +export function useListApiKeysForApp(appId: string) { + return useSwrWithAuth(`/apps/${appId}/api-keys`); +} -// Teams +// region Teams const TEAMS_SWR_KEY = "teams"; export function useListTeams() { return useSwrWithAuth(`/teams`); @@ -68,7 +72,7 @@ export function useGetTeamDetails(id?: string) { return useSwrWithAuth(`/teams/${id}`); } -// Api Products +// region API Products export function useListApiProducts() { return useSwrWithAuth("/api-products"); } @@ -79,7 +83,7 @@ export function useGetApiProductVersions(id?: string) { return useSwrWithAuth(`/api-products/${id}/versions`); } -// Subscriptions +// region Subscriptions // this is an admin endpoint export function useListSubscriptionsForStatus(status: SubscriptionStatus) { const swrResponse = useSwrWithAuth( @@ -115,7 +119,7 @@ export function useListSubscriptionsForApps(apps: App[]) { } // -// Mutations +// region Mutations // const getLatestAuthHeaders = (latestAccessToken: string | undefined) => { @@ -129,7 +133,7 @@ const getLatestAuthHeaders = (latestAccessToken: string | undefined) => { type MutationWithArgs = { arg: T }; // ------------------------ // -// Create Team +// region Create Team type CreateTeamParams = MutationWithArgs<{ name: string; description: string }>; @@ -149,7 +153,7 @@ export function useCreateTeamMutation() { } // ------------------------ // -// Create Team Member +// region Create Team Member type AddTeamMemberParams = MutationWithArgs<{ email: string; teamId: string }>; @@ -169,7 +173,7 @@ export function useAddTeamMemberMutation() { } // ------------------------ // -// Remove Team Member +// region Remove Team Member type AdminRemoveTeamMemberParams = MutationWithArgs<{ teamId: string; @@ -194,7 +198,7 @@ export function useRemoveTeamMemberMutation() { } // ------------------------ // -// Create App +// region Create App type CreateAppParams = MutationWithArgs<{ name: string; description: string }>; @@ -220,7 +224,7 @@ export function useCreateAppMutation(teamId: string | undefined) { } // ------------------------ // -// Update App +// region Update App type UpdateAppParams = MutationWithArgs<{ appId: string; @@ -247,7 +251,7 @@ export function useUpdateAppMutation() { } // ------------------------ // -// Update Team +// region Update Team type UpdateTeamParams = MutationWithArgs<{ teamId: string; @@ -272,7 +276,7 @@ export function useUpdateTeamMutation() { } // ------------------------ // -// Create App and Subscription +// region Create App and Subscription type CreateAppAndSubscriptionParams = MutationWithArgs<{ appName: string; @@ -315,7 +319,7 @@ export function useCreateAppAndSubscriptionMutation() { } // ------------------------ // -// Create Subscription +// region Create Subscription type CreateSubscriptionParams = MutationWithArgs<{ apiProductId: string; @@ -344,7 +348,7 @@ export function useCreateSubscriptionMutation(appId: string) { } // -------------------------------- // -// (Admin) Approve/Reject Subscription +// region (Admin) Approve/Reject Subscription type UpdateSubscriptionParams = MutationWithArgs<{ subscription: Subscription; @@ -387,7 +391,7 @@ export function useAdminRejectSubscriptionMutation() { } // -------------------------------- // -// Delete Subscription +// region Delete Subscription export function useDeleteSubscriptionMutation() { const { latestAccessToken } = useContext(AuthContext); @@ -406,7 +410,7 @@ export function useDeleteSubscriptionMutation() { } // -------------------------------- // -// Delete Team +// region Delete Team type DeleteTeamParams = MutationWithArgs<{ teamId: string }>; @@ -424,7 +428,7 @@ export function useDeleteTeamMutation() { } // -------------------------------- // -// Delete App +// region Delete App type DeleteAppParams = MutationWithArgs<{ appId: string }>; @@ -440,3 +444,39 @@ export function useDeleteAppMutation() { }; return useSWRMutation(`delete-team`, deleteApp); } + +// -------------------------------- // +// region Create API Key + +type CreateApiKeyParams = MutationWithArgs<{ apiKeyName: string }>; + +export function useCreateApiKeyMutation(appId: string) { + const { latestAccessToken } = useContext(AuthContext); + const createApiKey = async (_: string, { arg }: CreateApiKeyParams) => { + return await fetchJSON(`/apps/${appId}/api-keys`, { + method: "POST", + headers: getLatestAuthHeaders(latestAccessToken), + body: JSON.stringify(arg), + }); + }; + return useSWRMutation( + `/apps/${appId}/api-keys`, + createApiKey + ); +} + +// -------------------------------- // +// region Delete API Key + +type DeleteApiKeyParams = MutationWithArgs<{ apiKeyId: string }>; + +export function useDeleteApiKeyMutation(appId: string) { + const { latestAccessToken } = useContext(AuthContext); + const deleteApiKey = async (_: string, { arg }: DeleteApiKeyParams) => { + await fetchJSON(`/api-keys/${arg.apiKeyId}`, { + method: "DELETE", + headers: getLatestAuthHeaders(latestAccessToken), + }); + }; + return useSWRMutation(`/apps/${appId}/api-keys`, deleteApiKey); +} diff --git a/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageBody.tsx b/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageBody.tsx index bdabf8e8..0ea6fdd2 100644 --- a/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageBody.tsx +++ b/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageBody.tsx @@ -1,4 +1,4 @@ -import { Tabs } from "@mantine/core"; +import { Code, Tabs } from "@mantine/core"; import { useEffect, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { @@ -7,6 +7,7 @@ import { ApiVersionSchema, } from "../../../Apis/api-types"; import { ContentWidthDiv } from "../../../Styles/ContentWidthHelpers"; +import { EmptyData } from "../../Common/EmptyData"; import DocsTabContent from "./DocsTab/DocsTabContent"; import SchemaTabContent from "./SchemaTab/SchemaTabContent"; @@ -62,9 +63,7 @@ export function ApiProductDetailsPageBody({ */} Spec - {includesDocumentation && ( - Docs - )} + Docs {/* @@ -72,17 +71,27 @@ export function ApiProductDetailsPageBody({ */} - {includesDocumentation && ( - + + {includesDocumentation ? ( - - )} + ) : ( + + + You may add documentation for this API in the{" "} + + spec.versions[your-version].openapiMetadata.description + {" "} + field of this ApiProduct resource. Markdown is + supported. + + + )} + ); diff --git a/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageHeading.tsx b/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageHeading.tsx index 8a2b832e..ba2c831a 100644 --- a/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageHeading.tsx +++ b/projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageHeading.tsx @@ -1,4 +1,5 @@ import { Box, Flex, Select } from "@mantine/core"; +import { useContext, useState } from "react"; import toast from "react-hot-toast"; import { ApiProductSummary, @@ -6,12 +7,14 @@ import { ApiVersionSchema, } from "../../../Apis/api-types"; import { Icon } from "../../../Assets/Icons"; +import { AuthContext } from "../../../Context/AuthContext"; import { FormModalStyles } from "../../../Styles/shared/FormModalStyles"; import { useGetImageURL } from "../../../Utility/custom-image-utility"; import { downloadFile, filterMetadataToDisplay, } from "../../../Utility/utility"; +import NewSubscriptionModal from "../../Apps/Details/Modals/NewSubscriptionModal"; import { BannerHeading } from "../../Common/Banner/BannerHeading"; import { BannerHeadingTitle } from "../../Common/Banner/BannerHeadingTitle"; import { Button } from "../../Common/Button"; @@ -31,8 +34,8 @@ const ApiProductDetailsPageHeading = ({ onSelectedApiVersionChange: (newVersionId: string | null) => void; apiVersionSpec: ApiVersionSchema | undefined; }) => { - // const { isLoggedIn } = useContext(AuthContext); - // const [showSubscribeModal, setShowSubscribeModal] = useState(false); + const { isLoggedIn } = useContext(AuthContext); + const [showSubscribeModal, setShowSubscribeModal] = useState(false); const downloadApiSpec = () => { if (!selectedApiVersion?.apiSpec) { @@ -102,14 +105,12 @@ const ApiProductDetailsPageHeading = ({ /> )} - {/* - // Note: Removing sections for GGv2 demo. {isLoggedIn && ( - )} */} + )} + + + + setCreatedApiKey("")} + /> + + ); +}; + +export default AddApiKeysSubSection; diff --git a/projects/ui/src/Components/Apps/Details/ApiKeysSection/AppApiKeysSection.tsx b/projects/ui/src/Components/Apps/Details/ApiKeysSection/AppApiKeysSection.tsx new file mode 100644 index 00000000..5a000b9d --- /dev/null +++ b/projects/ui/src/Components/Apps/Details/ApiKeysSection/AppApiKeysSection.tsx @@ -0,0 +1,135 @@ +import { Box, Flex } from "@mantine/core"; +import { useContext, useMemo, useState } from "react"; +import { di } from "react-magnetic-di"; +import { APIKey, App } from "../../../../Apis/api-types"; +import { + useListApiKeysForApp, + useListAppsForTeam, +} from "../../../../Apis/gg_hooks"; +import { AuthContext } from "../../../../Context/AuthContext"; +import { DetailsPageStyles } from "../../../../Styles/shared/DetailsPageStyles"; +import { GridCardStyles } from "../../../../Styles/shared/GridCard.style"; +import { UtilityStyles } from "../../../../Styles/shared/Utility.style"; +import { formatDateToMMDDYYYY } from "../../../../Utility/utility"; +import { Button } from "../../../Common/Button"; +import CustomPagination, { + pageOptions, + useCustomPagination, +} from "../../../Common/CustomPagination"; +import { EmptyData } from "../../../Common/EmptyData"; +import { Loading } from "../../../Common/Loading"; +import Table from "../../../Common/Table"; +import ToggleAddButton from "../../../Common/ToggleAddButton"; +import ConfirmDeleteApiKeyModal from "../Modals/ConfirmDeleteApiKeyModal"; +import AddApiKeysSubSection from "./AddApiKeysSubSection"; + +const AppApiKeysSection = ({ app }: { app: App }) => { + di(useListAppsForTeam); + const { isAdmin } = useContext(AuthContext); + const { isLoading, data: apiKeys } = useListApiKeysForApp(app.id); + const [showAddApiKeySubSection, setShowAddApiKeySubSection] = useState(false); + + const customPaginationData = useCustomPagination( + apiKeys ?? [], + pageOptions.table + ); + const { paginatedData } = customPaginationData; + + const [confirmDeleteApiKey, setConfirmDeleteApiKey] = useState(); + + const rows = useMemo(() => { + return paginatedData?.map((apiKey) => { + return ( + ( + + {apiKey.name} + {formatDateToMMDDYYYY(new Date(apiKey.createdAt))} + {/* {JSON.stringify(apiKey.metadata)} */} + + + + + + + ) ?? [] + ); + }); + }, [paginatedData]); + + if (isLoading) { + return ; + } + return ( + + + API Keys + {!isAdmin && ( + + setShowAddApiKeySubSection(!showAddApiKeySubSection) + } + /> + )} + + setShowAddApiKeySubSection(false)} + /> + {!apiKeys?.length ? ( + + + + ) : ( + + + + + + + + + + + + {rows} + + + + + +
NameCreated + + Delete + +
+ + + +
+
+
+
+ )} + setConfirmDeleteApiKey(undefined)} + /> +
+ ); +}; + +export default AppApiKeysSection; diff --git a/projects/ui/src/Components/Apps/Details/ApiSubscriptionsSection/AppApiSubscriptionsSection.tsx b/projects/ui/src/Components/Apps/Details/ApiSubscriptionsSection/AppApiSubscriptionsSection.tsx index 0dc43e45..ee2caa1e 100644 --- a/projects/ui/src/Components/Apps/Details/ApiSubscriptionsSection/AppApiSubscriptionsSection.tsx +++ b/projects/ui/src/Components/Apps/Details/ApiSubscriptionsSection/AppApiSubscriptionsSection.tsx @@ -43,8 +43,8 @@ const AppApiSubscriptionsSection = ({ /> {subscriptions.length === 0 && ( - - + + )} diff --git a/projects/ui/src/Components/Apps/Details/AppDetailsPageContent.tsx b/projects/ui/src/Components/Apps/Details/AppDetailsPageContent.tsx index 11575485..a4b2a4a2 100644 --- a/projects/ui/src/Components/Apps/Details/AppDetailsPageContent.tsx +++ b/projects/ui/src/Components/Apps/Details/AppDetailsPageContent.tsx @@ -1,19 +1,34 @@ -import { Box, Flex, Loader } from "@mantine/core"; +import { Box, Flex, Loader, Tooltip } from "@mantine/core"; +import { useMemo } from "react"; import { di } from "react-magnetic-di"; +import { NavLink } from "react-router-dom"; import { App } from "../../../Apis/api-types"; -import { useListSubscriptionsForApp } from "../../../Apis/gg_hooks"; +import { + useListSubscriptionsForApp, + useListTeams, +} from "../../../Apis/gg_hooks"; +import { Icon } from "../../../Assets/Icons"; +import { UtilityStyles } from "../../../Styles/shared/Utility.style"; +import { getTeamDetailsLink } from "../../../Utility/link-builders"; import { BannerHeading } from "../../Common/Banner/BannerHeading"; import { BannerHeadingTitle } from "../../Common/Banner/BannerHeadingTitle"; import { PageContainer } from "../../Common/PageContainer"; +import AppApiKeysSection from "./ApiKeysSection/AppApiKeysSection"; import AppApiSubscriptionsSection from "./ApiSubscriptionsSection/AppApiSubscriptionsSection"; import AppAuthenticationSection from "./AuthenticationSection/AppAuthenticationSection"; import EditAppButtonWithModal from "./EditAppButtonWithModal"; export const AppDetailsPageContent = ({ app }: { app: App }) => { - di(useListSubscriptionsForApp); + di(useListSubscriptionsForApp, useListTeams); const { isLoading: isLoadingSubscriptions, data: subscriptions } = useListSubscriptionsForApp(app.id); + const { data: teams } = useListTeams(); + const team = useMemo( + () => teams?.find((t) => t.id === app.teamId), + [teams, app] + ); + // Mock data for testing // app.idpClientId = "4df81266-f855-466d-8ded-699056780850"; // app.idpClientName = "test-idp"; @@ -27,6 +42,7 @@ export const AppDetailsPageContent = ({ app }: { app: App }) => { title={ } stylingTweaks={{ fontSize: "32px", lineHeight: "36px", @@ -34,7 +50,31 @@ export const AppDetailsPageContent = ({ app }: { app: App }) => { additionalInfo={} /> } - description={app.description} + description={ + <> + {!!team && !!team.name && ( + + + + + + {team?.name} + + + + + )} + {app.description} + + } breadcrumbItems={[ { label: "Home", link: "/" }, { label: "Apps", link: "/apps" }, @@ -44,6 +84,9 @@ export const AppDetailsPageContent = ({ app }: { app: App }) => { {appHasOAuthClient && } + + + {isLoadingSubscriptions || subscriptions === undefined ? ( ) : ( diff --git a/projects/ui/src/Components/Apps/Details/Modals/ConfirmDeleteApiKeyModal.tsx b/projects/ui/src/Components/Apps/Details/Modals/ConfirmDeleteApiKeyModal.tsx new file mode 100644 index 00000000..4d01a8e8 --- /dev/null +++ b/projects/ui/src/Components/Apps/Details/Modals/ConfirmDeleteApiKeyModal.tsx @@ -0,0 +1,61 @@ +import { Box, CloseButton, Flex } from "@mantine/core"; +import { FormEvent } from "react"; +import toast from "react-hot-toast"; +import { di } from "react-magnetic-di"; +import { useDeleteApiKeyMutation } from "../../../../Apis/gg_hooks"; +import { FormModalStyles } from "../../../../Styles/shared/FormModalStyles"; +import { Button } from "../../../Common/Button"; + +const ConfirmDeleteApiKeyModal = ({ + apiKeyId, + appId, + open, + onClose, +}: { + apiKeyId: string; + appId: string; + open: boolean; + onClose: () => void; +}) => { + di(useDeleteApiKeyMutation); + const { trigger: deleteApiKey } = useDeleteApiKeyMutation(appId); + const onConfirm = async (e?: FormEvent) => { + e?.preventDefault(); + await toast.promise(deleteApiKey({ apiKeyId }), { + error: (e) => "There was an error deleting the API Key. " + e, + loading: "Deleting the API Key...", + success: "Deleted the API Key!", + }); + onClose(); + }; + + // + // Render + // + return ( + + +
+ Delete API Key + + Are you sure that you want to delete this API Key? + +
+ +
+ + + + + + + +
+ ); +}; + +export default ConfirmDeleteApiKeyModal; diff --git a/projects/ui/src/Components/Apps/Details/Modals/NewSubscriptionModal.tsx b/projects/ui/src/Components/Apps/Details/Modals/NewSubscriptionModal.tsx index 0d29da86..0cf9646a 100644 --- a/projects/ui/src/Components/Apps/Details/Modals/NewSubscriptionModal.tsx +++ b/projects/ui/src/Components/Apps/Details/Modals/NewSubscriptionModal.tsx @@ -119,7 +119,10 @@ const NewSubscriptionModal = ({ await toast.promise( createSubscription({ apiProductId: formApiProductId }), { - error: "There was an error creating the subscription.", + error: (e) => + "message" in e + ? "Error: " + e.message + : "There was an error creating the subscription.", loading: "Creating the subscription...", success: "Created the subscription!", } diff --git a/projects/ui/src/Components/Apps/Details/Modals/ViewCreatedApiKeyModal.tsx b/projects/ui/src/Components/Apps/Details/Modals/ViewCreatedApiKeyModal.tsx new file mode 100644 index 00000000..7cedc78a --- /dev/null +++ b/projects/ui/src/Components/Apps/Details/Modals/ViewCreatedApiKeyModal.tsx @@ -0,0 +1,100 @@ +import { Alert, Box, CloseButton, Flex } from "@mantine/core"; +import { useEffect, useState } from "react"; +import toast from "react-hot-toast"; +import { Icon } from "../../../../Assets/Icons"; +import { FormModalStyles } from "../../../../Styles/shared/FormModalStyles"; +import { copyToClipboard } from "../../../../Utility/utility"; +import { Button } from "../../../Common/Button"; + +const ViewCreatedApiKeyModal = ({ + apiKey, + open, + onCloseModal, +}: { + apiKey: string; + open: boolean; + onCloseModal: () => void; +}) => { + const [hasCopiedKey, setHasCopiedKey] = useState(false); + + useEffect(() => { + // Reset state on close. + if (!open) { + setHasCopiedKey(false); + } + }, [open]); + + const handleOnClose = () => { + if (!hasCopiedKey) { + return; + } + onCloseModal(); + }; + + // + // Render + // + return ( + + + + Created API Key + + {hasCopiedKey && ( + + )} + + + + } + title="Warning!" + color="orange" + > + This API Key value will not be available later. Please click the API + Key value to copy and secure this value now. + + + + + + + + + + ); +}; + +export default ViewCreatedApiKeyModal; diff --git a/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryGridCard.tsx b/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryGridCard.tsx index decacafb..1765259b 100644 --- a/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryGridCard.tsx +++ b/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryGridCard.tsx @@ -1,44 +1,49 @@ -import { Box } from "@mantine/core"; -import { useMemo } from "react"; +import { Box, Flex, Tooltip } from "@mantine/core"; +import { NavLink } from "react-router-dom"; import { Icon } from "../../../../Assets/Icons"; import { CardStyles } from "../../../../Styles/shared/Card.style"; import { GridCardStyles } from "../../../../Styles/shared/GridCard.style"; -import { getAppDetailsLink } from "../../../../Utility/link-builders"; +import { UtilityStyles } from "../../../../Styles/shared/Utility.style"; +import { + getAppDetailsLink, + getTeamDetailsLink, +} from "../../../../Utility/link-builders"; +import { SubscriptionInfoCardStyles } from "../../../Common/SubscriptionsList/SubscriptionInfoCard/SubscriptionInfoCard.style"; import { AppWithTeam } from "../AppsList"; /** * MAIN COMPONENT **/ export function AppSummaryGridCard({ app }: { app: AppWithTeam }) { - // In the future banner images may come through API data. - // Even when that is the case, a default image may be desired - // for when no image is available. - // Further, you may have some clever trick for setting one of - // many default images. - const defaultCardImage = useMemo( - () => { - return "https://img.huffingtonpost.com/asset/57f2730f170000f70aac9059.jpeg?ops=scalefit_960_noupscale"; - }, - // Currently we don't need to change images unless the api itself has changed. - // Depending on the function within the memo, this may not always be the case. - [app.id] - ); - return ( - - - placeholder - - - {app.name} - {app.description} - - - - - {app.team.name} - - - + // + +
+ + + {app.name} + + + + + + {app.team.name} + + + + + {app.description} + + +
+ + + DETAILS + + +
); } diff --git a/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryListCard.tsx b/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryListCard.tsx deleted file mode 100644 index f2c0fd9e..00000000 --- a/projects/ui/src/Components/Apps/PageContent/AppSummaryCards/AppSummaryListCard.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { Box, Flex } from "@mantine/core"; -import { NavLink } from "react-router-dom"; -import { Icon } from "../../../../Assets/Icons"; -import { CardStyles } from "../../../../Styles/shared/Card.style"; -import { ListCardStyles } from "../../../../Styles/shared/ListCard.style"; -import { getAppDetailsLink } from "../../../../Utility/link-builders"; -import { AppWithTeam } from "../AppsList"; - -/** - * MAIN COMPONENT - **/ -export function AppSummaryListCard({ app }: { app: AppWithTeam }) { - return ( - - - - - - - - {app.name} - {app.description} - - - - - - {app.team.name} - - - - - ); -} diff --git a/projects/ui/src/Components/Apps/PageContent/AppsFilter.tsx b/projects/ui/src/Components/Apps/PageContent/AppsFilter.tsx index 80e560ee..5ac6b624 100644 --- a/projects/ui/src/Components/Apps/PageContent/AppsFilter.tsx +++ b/projects/ui/src/Components/Apps/PageContent/AppsFilter.tsx @@ -1,15 +1,13 @@ import { Select, TextInput } from "@mantine/core"; -import { useContext, useMemo } from "react"; +import { useMemo } from "react"; import { Team } from "../../../Apis/api-types"; import { Icon } from "../../../Assets/Icons"; -import { AppContext } from "../../../Context/AppContext"; import { FilterStyles as Styles } from "../../../Styles/shared/Filters.style"; import { FilterType } from "../../../Utility/filter-utility"; import { AppliedFiltersSection, FiltrationProp, } from "../../Common/Filters/AppliedFiltersSection"; -import GridListToggle from "../../Common/GridListToggle"; export function AppsFilter({ filters, @@ -18,8 +16,6 @@ export function AppsFilter({ filters: FiltrationProp; teams: Team[]; }) { - const { preferGridView, setPreferGridView } = useContext(AppContext); - const addNameFilter = (evt: { target: { value: string } }) => { const displayName = evt.target.value; // Check for duplicate filters. @@ -98,11 +94,6 @@ export function AppsFilter({ placeholder="Team" /> - - setPreferGridView(!newIsList)} - isList={!preferGridView} - /> diff --git a/projects/ui/src/Components/Apps/PageContent/AppsList.tsx b/projects/ui/src/Components/Apps/PageContent/AppsList.tsx index 15e7ffed..18135daa 100644 --- a/projects/ui/src/Components/Apps/PageContent/AppsList.tsx +++ b/projects/ui/src/Components/Apps/PageContent/AppsList.tsx @@ -1,15 +1,13 @@ -import { useContext, useMemo } from "react"; +import { useMemo } from "react"; import { di } from "react-magnetic-di"; import { App, Team } from "../../../Apis/api-types"; import { useListAppsForTeams } from "../../../Apis/gg_hooks"; -import { AppContext } from "../../../Context/AppContext"; import { FilterPair, FilterType } from "../../../Utility/filter-utility"; import { omitErrorMessageResponse } from "../../../Utility/utility"; import { EmptyData } from "../../Common/EmptyData"; import { Loading } from "../../Common/Loading"; import { AppsPageStyles } from "../AppsPage.style"; import { AppSummaryGridCard } from "./AppSummaryCards/AppSummaryGridCard"; -import { AppSummaryListCard } from "./AppSummaryCards/AppSummaryListCard"; export type AppWithTeam = App & { team: Team }; @@ -23,7 +21,6 @@ export function AppsList({ nameFilter: string; }) { di(useListAppsForTeams); - const { preferGridView } = useContext(AppContext); // This is the App[][] of apps per team. const { isLoading, data: appsListPerTeam } = useListAppsForTeams(teams); // This is the flattened AppWithTeam[] that includes team information. @@ -88,23 +85,17 @@ export function AppsList({ if (isLoading) { return ; } - if (!filteredAppsList.length) { - return ; + if (!appsList?.length) { + return ; } - if (preferGridView) { - return ( - - {filteredAppsList.map((api) => ( - - ))} - - ); + if (!filteredAppsList.length) { + return ; } return ( -
- {filteredAppsList.map((app) => ( - + + {filteredAppsList.map((api) => ( + ))} -
+ ); } diff --git a/projects/ui/src/Components/Common/EmptyData.tsx b/projects/ui/src/Components/Common/EmptyData.tsx index d637102d..bde84dbe 100644 --- a/projects/ui/src/Components/Common/EmptyData.tsx +++ b/projects/ui/src/Components/Common/EmptyData.tsx @@ -1,28 +1,46 @@ -import { Flex, Text } from "@mantine/core"; +import { css } from "@emotion/react"; +import styled from "@emotion/styled"; +import { Box } from "@mantine/core"; +import { borderRadiusConstants } from "../../Styles/constants"; -type EmptyDataProps = - | { - topic: string; - message?: string; - } - | { - topicMessageOverride: string; - }; -export function EmptyData(props: EmptyDataProps) { +const StyledEmptyContentOuter = styled.div( + ({ theme }) => css` + display: flex; + justify-content: center; + text-align: center; + line-height: 2rem; + background-color: white; + box-shadow: 1px 1px 5px ${theme.splashBlue}; + border: 1px solid ${theme.splashBlue}; + border-radius: ${borderRadiusConstants.small}; + margin-bottom: 30px; + padding: 30px; + ` +); + +export const EmptyData = (props: { + children?: React.ReactNode; + title?: React.ReactNode; +}) => { return ( - - - {"topicMessageOverride" in props ? ( - <>{props.topicMessageOverride} - ) : ( - <>No {props.topic} results were found + + + {props.title && ( + + {props.title} + + )} + {!!props.children && ( + + {props.children} + )} - - {"message" in props && !!props.message && ( - - {props.message} - - )} - +
+ ); -} +}; diff --git a/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionInfoCard/SubscriptionInfoCardFooter.tsx b/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionInfoCard/SubscriptionInfoCardFooter.tsx index 02dfaa1c..a0c4faa7 100644 --- a/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionInfoCard/SubscriptionInfoCardFooter.tsx +++ b/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionInfoCard/SubscriptionInfoCardFooter.tsx @@ -22,9 +22,12 @@ const SubscriptionInfoCardFooter = ({ subscriptionState: SubscriptionState; }) => { const [showDeleteSubModal, setShowDeleteSubModal] = useState(false); - const canDeleteSubscription = - subscriptionState === SubscriptionState.PENDING || - subscriptionState === SubscriptionState.ACCEPTED; + const canDeleteSubscription = true; + // We could limit what's able to be deleted like this. + // But it's possible that an admin accidentally rejects a subscription and a user wants to recreate it. + // const canDeleteSubscription = + // subscriptionState === SubscriptionState.PENDING || + // subscriptionState === SubscriptionState.APPROVED; // // Render diff --git a/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsList.tsx b/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsList.tsx index 14328fb1..b401c7eb 100644 --- a/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsList.tsx +++ b/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsList.tsx @@ -35,7 +35,7 @@ const SubscriptionsList = ({ return null; } if (subscriptions.length === 0) { - return ; + return ; } return ( diff --git a/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsUtility.ts b/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsUtility.ts index b4f1838d..c9c2651a 100644 --- a/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsUtility.ts +++ b/projects/ui/src/Components/Common/SubscriptionsList/SubscriptionsUtility.ts @@ -3,7 +3,7 @@ import { colors } from "../../../Styles"; export enum SubscriptionState { PENDING, - ACCEPTED, + APPROVED, REJECTED, DELETED, } @@ -14,9 +14,9 @@ export const subscriptionStateMap = { accentColor: colors.seaBlue, borderColor: colors.splashBlue, }, - [SubscriptionState.ACCEPTED]: { - subscriptionState: SubscriptionState.ACCEPTED, - label: "ACCEPTED", + [SubscriptionState.APPROVED]: { + subscriptionState: SubscriptionState.APPROVED, + label: "APPROVED", accentColor: colors.midGreen, borderColor: colors.splashBlue, }, @@ -40,7 +40,7 @@ const dateHasValue = (dateString: string | undefined) => { export const GetSubscriptionState = (subscription: Subscription) => { if (!!subscription.approved) { - return SubscriptionState.ACCEPTED; + return SubscriptionState.APPROVED; } if (dateHasValue(subscription.deletedAt)) { return SubscriptionState.DELETED; diff --git a/projects/ui/src/Components/Structure/BasicAuthVariant/Header.tsx b/projects/ui/src/Components/Structure/BasicAuthVariant/Header.tsx new file mode 100644 index 00000000..0842edc4 --- /dev/null +++ b/projects/ui/src/Components/Structure/BasicAuthVariant/Header.tsx @@ -0,0 +1,154 @@ +import { useContext, useMemo } from "react"; +import { Link, NavLink, useLocation } from "react-router-dom"; +import { ReactComponent as Logo } from "../../../Assets/logo.svg"; +import { AppContext } from "../../../Context/AppContext"; +import { AuthContext } from "../../../Context/AuthContext"; +import { logoImageURL } from "../../../user_variables.tmplr"; +import { ErrorBoundary } from "../../Common/ErrorBoundary"; +import { HeaderStyles } from "../Header.style"; +import HeaderSectionLoggedIn from "./HeaderSectionLoggedIn"; +import HeaderSectionLoggedOut from "./HeaderSectionLoggedOut"; + +if (!window.isSecureContext) { + // eslint-disable-next-line no-console + console.error( + "This page is not being delivered in a secure context (see https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts), " + + "so login will not work." + ); +} + +/** + * MAIN COMPONENT + **/ +export function Header() { + const { isAdmin } = useContext(AuthContext); + const routerLocation = useLocation(); + const { isLoggedIn } = useContext(AuthContext); + + const inArea = (paths: string[]) => { + return paths.some((s) => routerLocation.pathname.includes(s)); + }; + + const inAdminTeamsArea = useMemo( + () => inArea(["/admin/teams"]), + [routerLocation.pathname] + ); + + const inAdminSubscriptionsArea = useMemo( + () => inArea(["/admin/subscriptions"]), + [routerLocation.pathname] + ); + + const inAPIsArea = useMemo( + () => inArea(["/apis", "/api-details/"]), + [routerLocation.pathname] + ); + + const inAppsArea = useMemo( + () => inArea(["/apps", "/app-details/"]), + [routerLocation.pathname] + ); + + const inTeamsArea = useMemo( + () => inArea(["/teams", "/team-details/"]), + [routerLocation.pathname] + ); + + const { pageContentIsWide } = useContext(AppContext); + + return ( + + +
+ + {!!logoImageURL ? ( + + ) : ( + + )} + +
+
+ + Home + + + {!isLoggedIn ? ( + // + // Logged-out view + // + <> + + APIs + + + ) : isAdmin ? ( + // + // Logged-in, admin view + // + <> + + Teams + + + Subscriptions + + + ) : ( + // + // Logged-in, non-admin view + // + <> + {/* + // If we allow admins to access the APIs page, things get a bit + // more confusing, since we will have to consider the behavior + // of the API details pages and pending subscriptions tabs. + // For example, a user can create an App from the API details page, + // so it would be strange for the admin not to have access to + // the Apps page in that case. + */} + + APIs + + + Teams + + + Apps + + + )} + +
+ + {isLoggedIn ? ( + + ) : ( + + )} + +
+ + + ); +} diff --git a/projects/ui/src/Components/Structure/HeaderSectionLoggedIn.tsx b/projects/ui/src/Components/Structure/BasicAuthVariant/HeaderSectionLoggedIn.tsx similarity index 91% rename from projects/ui/src/Components/Structure/HeaderSectionLoggedIn.tsx rename to projects/ui/src/Components/Structure/BasicAuthVariant/HeaderSectionLoggedIn.tsx index 011a0c5f..9e390931 100644 --- a/projects/ui/src/Components/Structure/HeaderSectionLoggedIn.tsx +++ b/projects/ui/src/Components/Structure/BasicAuthVariant/HeaderSectionLoggedIn.tsx @@ -4,11 +4,11 @@ import { Popover } from "@mantine/core"; import { useContext, useEffect, useMemo, useState } from "react"; import { di } from "react-magnetic-di"; import { NavLink, useLocation, useSearchParams } from "react-router-dom"; -import { useGetCurrentUser } from "../../Apis/gg_hooks"; -import { Icon } from "../../Assets/Icons"; -import { AppContext } from "../../Context/AppContext"; -import { AuthContext } from "../../Context/AuthContext"; -import { logoutEndpoint } from "../../user_variables.tmplr"; +import { useGetCurrentUser } from "../../../Apis/gg_hooks"; +import { Icon } from "../../../Assets/Icons"; +import { AppContext } from "../../../Context/AppContext"; +import { AuthContext } from "../../../Context/AuthContext"; +import { logoutEndpoint } from "../../../user_variables.tmplr"; export const StyledUserDropdown = styled(Popover.Dropdown)( ({ theme }) => css` diff --git a/projects/ui/src/Components/Structure/HeaderSectionLoggedOut.tsx b/projects/ui/src/Components/Structure/BasicAuthVariant/HeaderSectionLoggedOut.tsx similarity index 92% rename from projects/ui/src/Components/Structure/HeaderSectionLoggedOut.tsx rename to projects/ui/src/Components/Structure/BasicAuthVariant/HeaderSectionLoggedOut.tsx index e1a5ebad..623fa4df 100644 --- a/projects/ui/src/Components/Structure/HeaderSectionLoggedOut.tsx +++ b/projects/ui/src/Components/Structure/BasicAuthVariant/HeaderSectionLoggedOut.tsx @@ -4,10 +4,14 @@ import { AuthContext, LOCAL_STORAGE_AUTH_STATE, LOCAL_STORAGE_AUTH_VERIFIER, -} from "../../Context/AuthContext"; -import { doAccessTokenRequest } from "../../Utility/accessTokenRequest"; -import { audience, authEndpoint, clientId } from "../../user_variables.tmplr"; -import { Button } from "../Common/Button"; +} from "../../../Context/AuthContext"; +import { doAccessTokenRequest } from "../../../Utility/accessTokenRequest"; +import { + audience, + authEndpoint, + clientId, +} from "../../../user_variables.tmplr"; +import { Button } from "../../Common/Button"; // // From https://stackoverflow.com/a/63336562 @@ -71,9 +75,8 @@ const AuthFlowStarter = () => { const [codeChallenge, setCodeChallenge] = useState(); useEffect(() => { (async () => { - const newCodeChallenge = await generateCodeChallengeFromVerifier( - verifier - ); + const newCodeChallenge = + await generateCodeChallengeFromVerifier(verifier); setCodeChallenge(newCodeChallenge); })(); }, [setCodeChallenge]); diff --git a/projects/ui/src/Components/Structure/Header.tsx b/projects/ui/src/Components/Structure/Header.tsx deleted file mode 100644 index b5a1d2ef..00000000 --- a/projects/ui/src/Components/Structure/Header.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useContext, useMemo } from "react"; -import { Link, NavLink, useLocation } from "react-router-dom"; -import { ReactComponent as Logo } from "../../Assets/logo.svg"; -import { AppContext } from "../../Context/AppContext"; -import { AuthContext } from "../../Context/AuthContext"; -import { logoImageURL } from "../../user_variables.tmplr"; -import { ErrorBoundary } from "../Common/ErrorBoundary"; -import { HeaderStyles } from "./Header.style"; -import HeaderSectionLoggedIn from "./HeaderSectionLoggedIn"; -import HeaderSectionLoggedOut from "./HeaderSectionLoggedOut"; - -if (!window.isSecureContext) { - // eslint-disable-next-line no-console - console.error( - "This page is not being delivered in a secure context (see https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts), " + - "so login will not work." - ); -} - -/** - * MAIN COMPONENT - **/ -export function Header() { - const routerLocation = useLocation(); - const { isLoggedIn } = useContext(AuthContext); - - const inArea = (paths: string[]) => { - return paths.some((s) => routerLocation.pathname.includes(s)); - }; - - // Note: Removing sections for GGv2 demo. - - // const inAdminTeamsArea = useMemo( - // () => inArea(["/admin/teams"]), - // [routerLocation.pathname] - // ); - - // const inAdminSubscriptionsArea = useMemo( - // () => inArea(["/admin/subscriptions"]), - // [routerLocation.pathname] - // ); - - const inAPIsArea = useMemo( - () => inArea(["/apis", "/api-details/"]), - [routerLocation.pathname] - ); - - // Note: Removing sections for GGv2 demo. - - // const inAppsArea = useMemo( - // () => inArea(["/apps", "/app-details/"]), - // [routerLocation.pathname] - // ); - - // const inTeamsArea = useMemo( - // () => inArea(["/teams", "/team-details/"]), - // [routerLocation.pathname] - // ); - - const { pageContentIsWide } = useContext(AppContext); - - return ( - - -
- - {!!logoImageURL ? ( - - ) : ( - - )} - -
-
- - Home - - - {/* - // Note: Removing sections for GGv2 demo. - - {!isAdmin && ( - // If we allow admins to access the APIs page, things get a bit - // more confusing, since we will have to consider the behavior - // of the API details pages and pending subscriptions tabs. - // For example, a user can create an App from the API details page, - // so it would be strange for the admin not to have access to - // the Apps page in that case. - */} - - APIs - - {/* )} */} - - {/* - - // Note: Removing sections for GGv2 demo. - - {isLoggedIn && - (isAdmin ? ( - // - // Logged-in, admin view - // - <> - - Teams - - - Subscriptions - - - ) : ( - // - // Logged-in, non-admin view - // - <> - - Teams - - - Apps - - - ))} - */} - -
- - {isLoggedIn ? ( - - ) : ( - - )} - -
- - - ); -} diff --git a/projects/ui/src/Components/Structure/OidcAuthCodeHeaderVariant/OidcAuthCodeHeaderDropdown.tsx b/projects/ui/src/Components/Structure/OidcAuthCodeHeaderVariant/OidcAuthCodeHeaderDropdown.tsx index fae765b2..2b2f1088 100644 --- a/projects/ui/src/Components/Structure/OidcAuthCodeHeaderVariant/OidcAuthCodeHeaderDropdown.tsx +++ b/projects/ui/src/Components/Structure/OidcAuthCodeHeaderVariant/OidcAuthCodeHeaderDropdown.tsx @@ -9,7 +9,7 @@ import { oidcAuthCodeConfigCallbackPath, oidcAuthCodeConfigLogoutPath, } from "../../../user_variables.tmplr"; -import { StyledUserDropdown } from "../HeaderSectionLoggedIn"; +import { StyledUserDropdown } from "../BasicAuthVariant/HeaderSectionLoggedIn"; /** * MAIN COMPONENT diff --git a/projects/ui/src/Components/Teams/Details/AppsSection/TeamAppsSection.tsx b/projects/ui/src/Components/Teams/Details/AppsSection/TeamAppsSection.tsx index 39515fd9..e7407a2a 100644 --- a/projects/ui/src/Components/Teams/Details/AppsSection/TeamAppsSection.tsx +++ b/projects/ui/src/Components/Teams/Details/AppsSection/TeamAppsSection.tsx @@ -86,7 +86,7 @@ const TeamAppsSection = ({ team }: { team: Team }) => { )} {!apps?.length ? ( - + ) : ( diff --git a/projects/ui/src/Components/Teams/Details/TeamDetailsPageContent.tsx b/projects/ui/src/Components/Teams/Details/TeamDetailsPageContent.tsx index e1494227..8c600b02 100644 --- a/projects/ui/src/Components/Teams/Details/TeamDetailsPageContent.tsx +++ b/projects/ui/src/Components/Teams/Details/TeamDetailsPageContent.tsx @@ -1,6 +1,7 @@ import { Box, Flex } from "@mantine/core"; import { useContext } from "react"; import { Team } from "../../../Apis/api-types"; +import { Icon } from "../../../Assets/Icons"; import { AuthContext } from "../../../Context/AuthContext"; import { BannerHeading } from "../../Common/Banner/BannerHeading"; import { BannerHeadingTitle } from "../../Common/Banner/BannerHeadingTitle"; @@ -17,6 +18,7 @@ const TeamDetailsPageContent = ({ team }: { team: Team }) => { title={ } stylingTweaks={{ fontSize: "32px", lineHeight: "36px", diff --git a/projects/ui/src/Components/Teams/Details/UsersSection/TeamUsersSection.tsx b/projects/ui/src/Components/Teams/Details/UsersSection/TeamUsersSection.tsx index 12bf55a9..fa1559a6 100644 --- a/projects/ui/src/Components/Teams/Details/UsersSection/TeamUsersSection.tsx +++ b/projects/ui/src/Components/Teams/Details/UsersSection/TeamUsersSection.tsx @@ -100,7 +100,8 @@ const TeamUsersSection = ({ team }: { team: Team }) => { /> {!members?.length ? ( - + {/* We never should get here, since the user must be a member. */} + ) : ( diff --git a/projects/ui/src/Components/Teams/TeamsList/TeamSummaryCards/TeamSummaryGridCard.tsx b/projects/ui/src/Components/Teams/TeamsList/TeamSummaryCards/TeamSummaryGridCard.tsx index 743fbe9d..855f3299 100644 --- a/projects/ui/src/Components/Teams/TeamsList/TeamSummaryCards/TeamSummaryGridCard.tsx +++ b/projects/ui/src/Components/Teams/TeamsList/TeamSummaryCards/TeamSummaryGridCard.tsx @@ -57,7 +57,7 @@ export function TeamSummaryGridCard({ team }: { team: Team }) {
- MANAGE + DETAILS diff --git a/projects/ui/src/Components/Teams/TeamsList/TeamsList.tsx b/projects/ui/src/Components/Teams/TeamsList/TeamsList.tsx index 49e2d3c4..0b711ce4 100644 --- a/projects/ui/src/Components/Teams/TeamsList/TeamsList.tsx +++ b/projects/ui/src/Components/Teams/TeamsList/TeamsList.tsx @@ -12,11 +12,11 @@ export function TeamsList() { // // Render // - if (isLoading) { + if (teamsList === undefined || isLoading) { return ; } if (!teamsList?.length) { - return ; + return ; } return ( diff --git a/projects/ui/src/Components/UsagePlans/UsagePlanList/APIUsagePlansList.tsx b/projects/ui/src/Components/UsagePlans/UsagePlanList/APIUsagePlansList.tsx index 0cd03c09..0808d446 100644 --- a/projects/ui/src/Components/UsagePlans/UsagePlanList/APIUsagePlansList.tsx +++ b/projects/ui/src/Components/UsagePlans/UsagePlanList/APIUsagePlansList.tsx @@ -46,7 +46,7 @@ export function APIUsagePlansList() { )) ) : ( - + )} ); diff --git a/projects/ui/src/Styles/colors.ts b/projects/ui/src/Styles/colors.ts index 06513481..d8c35710 100644 --- a/projects/ui/src/Styles/colors.ts +++ b/projects/ui/src/Styles/colors.ts @@ -43,6 +43,8 @@ const baseColors = { const colorMap = { ...baseColors, + januaryGreyDark1: Color(baseColors.januaryGrey).darken(0.01).hex(), + marchGreyDark3: Color(baseColors.marchGrey).darken(0.03).hex(), marchGreyDark5: Color(baseColors.marchGrey).darken(0.05).hex(), marchGreyDark10: Color(baseColors.marchGrey).darken(0.1).hex(), diff --git a/projects/ui/src/Styles/global-styles/mantine-overrides.style.ts b/projects/ui/src/Styles/global-styles/mantine-overrides.style.ts index e020250e..bacc83a9 100644 --- a/projects/ui/src/Styles/global-styles/mantine-overrides.style.ts +++ b/projects/ui/src/Styles/global-styles/mantine-overrides.style.ts @@ -63,4 +63,9 @@ export const mantineGlobalStyles = css` } } } + + code.mantine-Code-root { + background-color: ${colors.januaryGreyDark1}; + white-space: nowrap; + } `; diff --git a/projects/ui/src/Styles/shared/Utility.style.tsx b/projects/ui/src/Styles/shared/Utility.style.tsx index 16420c16..d3bd3731 100644 --- a/projects/ui/src/Styles/shared/Utility.style.tsx +++ b/projects/ui/src/Styles/shared/Utility.style.tsx @@ -20,13 +20,22 @@ export namespace UtilityStyles { justify-content: center; `; - export const NavLinkContainer = styled.div( - ({ theme }) => css` + export const NavLinkContainer = styled.div<{ + withArrow?: boolean; + flexCenter?: boolean; + }>( + ({ theme, withArrow = true, flexCenter = false }) => css` font-size: 0.95rem; a { position: relative; font-weight: 500; padding-right: 5px; + ${flexCenter && + css` + display: flex; + align-items: center; + gap: 8px; + `} :hover { color: ${theme.seaBlue}; text-decoration: underline; @@ -34,16 +43,19 @@ export namespace UtilityStyles { :active { color: ${theme.oceanBlue}; } - :after { - content: ""; - border-top: 2px solid; - border-right: 2px solid; - border-color: currentColor; - width: 10px; - height: 10px; - display: inline-block; - transform: translateX(2px) rotate(45deg); - } + ${!!withArrow && + css` + :after { + content: ""; + border-top: 2px solid; + border-right: 2px solid; + border-color: currentColor; + width: 10px; + height: 10px; + display: inline-block; + transform: translateX(2px) rotate(45deg); + } + `} } ` );