From bcc66367ae089620db0f54c83bbcad913e248aef Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 9 Aug 2024 17:04:19 +0000 Subject: [PATCH 1/3] feat: show organization information on templates page --- .../modules/dashboard/DashboardProvider.tsx | 8 ++++ site/src/modules/navigation.ts | 11 +---- .../ManagementSettingsLayout.tsx | 2 +- .../src/pages/TemplatesPage/TemplatesPage.tsx | 3 ++ .../pages/TemplatesPage/TemplatesPageView.tsx | 48 ++++++++++++++++--- site/src/testHelpers/storybook.tsx | 1 + 6 files changed, 56 insertions(+), 17 deletions(-) diff --git a/site/src/modules/dashboard/DashboardProvider.tsx b/site/src/modules/dashboard/DashboardProvider.tsx index f8cf6eca1a1ba..0a6ede104747c 100644 --- a/site/src/modules/dashboard/DashboardProvider.tsx +++ b/site/src/modules/dashboard/DashboardProvider.tsx @@ -13,12 +13,14 @@ import type { import { ErrorAlert } from "components/Alert/ErrorAlert"; import { Loader } from "components/Loader/Loader"; import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata"; +import { selectFeatureVisibility } from "./entitlements"; export interface DashboardValue { entitlements: Entitlements; experiments: Experiments; appearance: AppearanceConfig; organizations: Organization[]; + showOrganizations: boolean; } export const DashboardContext = createContext( @@ -52,6 +54,11 @@ export const DashboardProvider: FC = ({ children }) => { return ; } + const hasMultipleOrganizations = organizationsQuery.data.length > 1; + const organizationsEnabled = + experimentsQuery.data.includes("multi-organization") && + selectFeatureVisibility(entitlementsQuery.data).multiple_organizations; + return ( = ({ children }) => { experiments: experimentsQuery.data, appearance: appearanceQuery.data, organizations: organizationsQuery.data, + showOrganizations: hasMultipleOrganizations || organizationsEnabled, }} > {children} diff --git a/site/src/modules/navigation.ts b/site/src/modules/navigation.ts index d319d5972d1ea..9d6773b73e4f6 100644 --- a/site/src/modules/navigation.ts +++ b/site/src/modules/navigation.ts @@ -4,7 +4,6 @@ import { useEffectEvent } from "hooks/hookPolyfills"; import type { DashboardValue } from "./dashboard/DashboardProvider"; -import { selectFeatureVisibility } from "./dashboard/entitlements"; import { useDashboard } from "./dashboard/useDashboard"; type LinkThunk = (state: DashboardValue) => string; @@ -27,13 +26,7 @@ export const linkToUsers = withFilter("/users", "status:active"); export const linkToTemplate = (organizationName: string, templateName: string): LinkThunk => - (dashboard) => { - const hasMultipleOrganizations = dashboard.organizations.length > 1; - const organizationsEnabled = - dashboard.experiments.includes("multi-organization") && - selectFeatureVisibility(dashboard.entitlements).multiple_organizations; - - return hasMultipleOrganizations || organizationsEnabled + (dashboard) => + dashboard.showOrganizations ? `/templates/${organizationName}/${templateName}` : `/templates/${templateName}`; - }; diff --git a/site/src/pages/ManagementSettingsPage/ManagementSettingsLayout.tsx b/site/src/pages/ManagementSettingsPage/ManagementSettingsLayout.tsx index c31bbfe2b54c7..283b5a714560a 100644 --- a/site/src/pages/ManagementSettingsPage/ManagementSettingsLayout.tsx +++ b/site/src/pages/ManagementSettingsPage/ManagementSettingsLayout.tsx @@ -13,7 +13,7 @@ import { DeploySettingsContext } from "../DeploySettingsPage/DeploySettingsLayou import { Sidebar } from "./Sidebar"; type OrganizationSettingsValue = { - organizations: Organization[] | undefined; + organizations: Organization[]; }; export const useOrganizationSettings = (): OrganizationSettingsValue => { diff --git a/site/src/pages/TemplatesPage/TemplatesPage.tsx b/site/src/pages/TemplatesPage/TemplatesPage.tsx index fa9c5bb167669..9e7b0b1e48e2b 100644 --- a/site/src/pages/TemplatesPage/TemplatesPage.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPage.tsx @@ -5,9 +5,11 @@ import { templateExamples, templates } from "api/queries/templates"; import { useAuthenticated } from "contexts/auth/RequireAuth"; import { pageTitle } from "utils/page"; import { TemplatesPageView } from "./TemplatesPageView"; +import { useDashboard } from "modules/dashboard/useDashboard"; export const TemplatesPage: FC = () => { const { permissions } = useAuthenticated(); + const { showOrganizations } = useDashboard(); const templatesQuery = useQuery(templates()); const examplesQuery = useQuery({ @@ -23,6 +25,7 @@ export const TemplatesPage: FC = () => { { }; interface TemplateRowProps { + showOrganizations: boolean; template: Template; } -const TemplateRow: FC = ({ template }) => { +const TemplateRow: FC = ({ showOrganizations, template }) => { const getLink = useLinks(); const templatePageLink = getLink( linkToTemplate(template.organization_name, template.name), @@ -120,7 +121,23 @@ const TemplateRow: FC = ({ template }) => { - {Language.developerCount(template.active_user_count)} + {showOrganizations ? ( + + + {template.organization_display_name} + + + Used by {Language.developerCount(template.active_user_count)} + + + ) : ( + Language.developerCount(template.active_user_count) + )} @@ -156,16 +173,18 @@ const TemplateRow: FC = ({ template }) => { export interface TemplatesPageViewProps { error?: unknown; + showOrganizations: boolean; + canCreateTemplates: boolean; examples: TemplateExample[] | undefined; templates: Template[] | undefined; - canCreateTemplates: boolean; } export const TemplatesPageView: FC = ({ - templates, error, - examples, + showOrganizations, canCreateTemplates, + examples, + templates, }) => { const isLoading = !templates; const isEmpty = templates && templates.length === 0; @@ -209,7 +228,9 @@ export const TemplatesPageView: FC = ({ {Language.nameLabel} - {Language.usedByLabel} + + {showOrganizations ? "Organization" : Language.usedByLabel} + {Language.buildTimeLabel} {Language.lastUpdatedLabel} @@ -225,7 +246,11 @@ export const TemplatesPageView: FC = ({ /> ) : ( templates?.map((template) => ( - + )) )} @@ -276,6 +301,15 @@ const styles = { actionCell: { whiteSpace: "nowrap", }, + cellPrimaryLine: (theme) => ({ + color: theme.palette.text.primary, + fontWeight: 600, + }), + cellSecondaryLine: (theme) => ({ + fontSize: 13, + color: theme.palette.text.secondary, + lineHeight: "150%", + }), secondary: (theme) => ({ color: theme.palette.text.secondary, }), diff --git a/site/src/testHelpers/storybook.tsx b/site/src/testHelpers/storybook.tsx index 30fbc28d0fccc..2cb8036f87a98 100644 --- a/site/src/testHelpers/storybook.tsx +++ b/site/src/testHelpers/storybook.tsx @@ -34,6 +34,7 @@ export const withDashboardProvider = ( experiments, appearance: MockAppearanceConfig, organizations: [MockDefaultOrganization], + showOrganizations: false, }} > From 4fc07cbed2e4047ce4278424372620fbfa686692 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 9 Aug 2024 17:07:20 +0000 Subject: [PATCH 2/3] add story --- site/src/pages/TemplatesPage/TemplatesPageView.stories.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.stories.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.stories.tsx index 6267f899df651..63707e67a3c0f 100644 --- a/site/src/pages/TemplatesPage/TemplatesPageView.stories.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPageView.stories.tsx @@ -69,6 +69,13 @@ export const WithTemplates: Story = { }, }; +export const MultipleOrganizations: Story = { + args: { + ...WithTemplates.args, + showOrganizations: true, + }, +}; + export const EmptyCanCreate: Story = { args: { canCreateTemplates: true, From 54901e38d0210120b5dfb31d522c64d95b514bc1 Mon Sep 17 00:00:00 2001 From: McKayla Washburn Date: Fri, 9 Aug 2024 17:09:09 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/src/pages/TemplatesPage/TemplatesPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/pages/TemplatesPage/TemplatesPage.tsx b/site/src/pages/TemplatesPage/TemplatesPage.tsx index 9e7b0b1e48e2b..0002198bc9acb 100644 --- a/site/src/pages/TemplatesPage/TemplatesPage.tsx +++ b/site/src/pages/TemplatesPage/TemplatesPage.tsx @@ -3,9 +3,9 @@ import { Helmet } from "react-helmet-async"; import { useQuery } from "react-query"; import { templateExamples, templates } from "api/queries/templates"; import { useAuthenticated } from "contexts/auth/RequireAuth"; +import { useDashboard } from "modules/dashboard/useDashboard"; import { pageTitle } from "utils/page"; import { TemplatesPageView } from "./TemplatesPageView"; -import { useDashboard } from "modules/dashboard/useDashboard"; export const TemplatesPage: FC = () => { const { permissions } = useAuthenticated();