mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: show organization information on templates page (#14224)
This commit is contained in:
committed by
GitHub
parent
9715ae5932
commit
59a80d70dc
@@ -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<DashboardValue | undefined>(
|
||||
@@ -52,6 +54,11 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
return <Loader fullscreen />;
|
||||
}
|
||||
|
||||
const hasMultipleOrganizations = organizationsQuery.data.length > 1;
|
||||
const organizationsEnabled =
|
||||
experimentsQuery.data.includes("multi-organization") &&
|
||||
selectFeatureVisibility(entitlementsQuery.data).multiple_organizations;
|
||||
|
||||
return (
|
||||
<DashboardContext.Provider
|
||||
value={{
|
||||
@@ -59,6 +66,7 @@ export const DashboardProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
experiments: experimentsQuery.data,
|
||||
appearance: appearanceQuery.data,
|
||||
organizations: organizationsQuery.data,
|
||||
showOrganizations: hasMultipleOrganizations || organizationsEnabled,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -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}`;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ import { DeploySettingsContext } from "../DeploySettingsPage/DeploySettingsLayou
|
||||
import { Sidebar } from "./Sidebar";
|
||||
|
||||
type OrganizationSettingsValue = {
|
||||
organizations: Organization[] | undefined;
|
||||
organizations: Organization[];
|
||||
};
|
||||
|
||||
export const useOrganizationSettings = (): OrganizationSettingsValue => {
|
||||
|
||||
@@ -3,11 +3,13 @@ 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";
|
||||
|
||||
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 = () => {
|
||||
</Helmet>
|
||||
<TemplatesPageView
|
||||
error={error}
|
||||
showOrganizations={showOrganizations}
|
||||
canCreateTemplates={permissions.createTemplates}
|
||||
examples={examplesQuery.data}
|
||||
templates={templatesQuery.data}
|
||||
|
||||
@@ -69,6 +69,13 @@ export const WithTemplates: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const MultipleOrganizations: Story = {
|
||||
args: {
|
||||
...WithTemplates.args,
|
||||
showOrganizations: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const EmptyCanCreate: Story = {
|
||||
args: {
|
||||
canCreateTemplates: true,
|
||||
|
||||
@@ -81,10 +81,11 @@ const TemplateHelpTooltip: FC = () => {
|
||||
};
|
||||
|
||||
interface TemplateRowProps {
|
||||
showOrganizations: boolean;
|
||||
template: Template;
|
||||
}
|
||||
|
||||
const TemplateRow: FC<TemplateRowProps> = ({ template }) => {
|
||||
const TemplateRow: FC<TemplateRowProps> = ({ showOrganizations, template }) => {
|
||||
const getLink = useLinks();
|
||||
const templatePageLink = getLink(
|
||||
linkToTemplate(template.organization_name, template.name),
|
||||
@@ -120,7 +121,23 @@ const TemplateRow: FC<TemplateRowProps> = ({ template }) => {
|
||||
</TableCell>
|
||||
|
||||
<TableCell css={styles.secondary}>
|
||||
{Language.developerCount(template.active_user_count)}
|
||||
{showOrganizations ? (
|
||||
<Stack
|
||||
spacing={0}
|
||||
css={{
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<span css={styles.cellPrimaryLine}>
|
||||
{template.organization_display_name}
|
||||
</span>
|
||||
<span css={styles.cellSecondaryLine}>
|
||||
Used by {Language.developerCount(template.active_user_count)}
|
||||
</span>
|
||||
</Stack>
|
||||
) : (
|
||||
Language.developerCount(template.active_user_count)
|
||||
)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell css={styles.secondary}>
|
||||
@@ -156,16 +173,18 @@ const TemplateRow: FC<TemplateRowProps> = ({ template }) => {
|
||||
|
||||
export interface TemplatesPageViewProps {
|
||||
error?: unknown;
|
||||
showOrganizations: boolean;
|
||||
canCreateTemplates: boolean;
|
||||
examples: TemplateExample[] | undefined;
|
||||
templates: Template[] | undefined;
|
||||
canCreateTemplates: boolean;
|
||||
}
|
||||
|
||||
export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
|
||||
templates,
|
||||
error,
|
||||
examples,
|
||||
showOrganizations,
|
||||
canCreateTemplates,
|
||||
examples,
|
||||
templates,
|
||||
}) => {
|
||||
const isLoading = !templates;
|
||||
const isEmpty = templates && templates.length === 0;
|
||||
@@ -209,7 +228,9 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell width="35%">{Language.nameLabel}</TableCell>
|
||||
<TableCell width="15%">{Language.usedByLabel}</TableCell>
|
||||
<TableCell width="15%">
|
||||
{showOrganizations ? "Organization" : Language.usedByLabel}
|
||||
</TableCell>
|
||||
<TableCell width="10%">{Language.buildTimeLabel}</TableCell>
|
||||
<TableCell width="15%">{Language.lastUpdatedLabel}</TableCell>
|
||||
<TableCell width="1%"></TableCell>
|
||||
@@ -225,7 +246,11 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
|
||||
/>
|
||||
) : (
|
||||
templates?.map((template) => (
|
||||
<TemplateRow key={template.id} template={template} />
|
||||
<TemplateRow
|
||||
key={template.id}
|
||||
showOrganizations={showOrganizations}
|
||||
template={template}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
@@ -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,
|
||||
}),
|
||||
|
||||
@@ -41,6 +41,7 @@ export const withDashboardProvider = (
|
||||
experiments,
|
||||
appearance: MockAppearanceConfig,
|
||||
organizations: [MockDefaultOrganization],
|
||||
showOrganizations: false,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
|
||||
Reference in New Issue
Block a user