Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -983,9 +983,10 @@ func DefaultSupportLinks(docsURL string) []LinkConfig {
Icon: "bug",
},
{
Name: "Join the Coder Discord",
Target: "https://coder.com/chat?utm_source=coder&utm_medium=coder&utm_campaign=server-footer",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we could make a new coder.com/chat link? or at least add a utm_source to the discord.gg url which i think the statistics do give us (unsure, need to check)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for flagging it! We can update the link in a follow up since I don't know who owns these links.

Icon: "chat",
Name: "Join the Coder Discord",
Target: "https://discord.gg/coder",
Icon: "chat",
Location: "navbar",
},
{
Name: "Star the Repo",
Expand Down Expand Up @@ -3325,7 +3326,9 @@ type SupportConfig struct {
type LinkConfig struct {
Name string `json:"name" yaml:"name"`
Target string `json:"target" yaml:"target"`
Icon string `json:"icon" yaml:"icon" enums:"bug,chat,docs"`
Icon string `json:"icon" yaml:"icon" enums:"bug,chat,docs,star"`

Location string `json:"location,omitempty" yaml:"location,omitempty" enums:"navbar,dropdown"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just asking because it sounds like I'll be helping out more with coder/coder's backend soon: what uses the enums struct tags?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mainly swagger generator + if we enable go validation, then the field will have restricted values too.

}

// Validate checks cross-field constraints for deployment values.
Expand Down
1 change: 1 addition & 0 deletions docs/reference/api/enterprise.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/reference/api/general.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 20 additions & 10 deletions docs/reference/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions enterprise/coderd/appearance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,17 @@ func TestCustomSupportLinks(t *testing.T) {
Target: "http://second-link-2",
Icon: "bug",
},
{
Name: "First button",
Target: "http://first-button-1",
Icon: "bug",
Location: "navbar",
},
{
Name: "Third link",
Target: "http://third-link-3",
Icon: "star",
},
}
cfg := coderdtest.DeploymentValues(t)
cfg.Support.Links = serpent.Struct[[]codersdk.LinkConfig]{
Expand Down
1 change: 1 addition & 0 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion site/src/modules/dashboard/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { buildInfo } from "api/queries/buildInfo";
import type { LinkConfig } from "api/typesGenerated";
import { useProxy } from "contexts/ProxyContext";
import { useAuthenticated } from "hooks";
import { useEmbeddedMetadata } from "hooks/useEmbeddedMetadata";
Expand All @@ -25,12 +26,18 @@ export const Navbar: FC = () => {
const canViewConnectionLog =
featureVisibility.connection_log && permissions.viewAnyConnectionLog;

const uniqueLinks = new Map<string, LinkConfig>();
for (const link of appearance.support_links ?? []) {
if (!uniqueLinks.has(link.name)) {
uniqueLinks.set(link.name, link);
}
}
return (
<NavbarView
user={me}
logo_url={appearance.logo_url}
buildInfo={buildInfoQuery.data}
supportLinks={appearance.support_links}
supportLinks={Array.from(uniqueLinks.values())}
onSignOut={signOut}
canViewDeployment={canViewDeployment}
canViewOrganizations={canViewOrganizations}
Expand Down
62 changes: 62 additions & 0 deletions site/src/modules/dashboard/Navbar/NavbarView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const meta: Meta<typeof NavbarView> = {
canViewDeployment: true,
canViewHealth: true,
canViewOrganizations: true,
supportLinks: [],
},
decorators: [withDashboardProvider],
};
Expand Down Expand Up @@ -129,3 +130,64 @@ export const IdleTasks: Story = {
],
},
};

export const SupportLinks: Story = {
args: {
user: MockUserMember,
canViewAuditLog: false,
canViewDeployment: false,
canViewHealth: false,
canViewOrganizations: false,
supportLinks: [
{
name: "This is a bug",
icon: "bug",
target: "#",
},
{
name: "This is a star",
icon: "star",
target: "#",
location: "navbar",
},
{
name: "This is a chat",
icon: "chat",
target: "#",
location: "navbar",
},
{
name: "No icon here",
icon: "",
target: "#",
location: "navbar",
},
{
name: "No icon here too",
icon: "",
target: "#",
},
],
},
};

export const DefaultSupportLinks: Story = {
args: {
user: MockUserMember,
canViewAuditLog: false,
canViewDeployment: false,
canViewHealth: false,
canViewOrganizations: false,
supportLinks: [
{ icon: "docs", name: "Documentation", target: "" },
{ icon: "bug", name: "Report a bug", target: "" },
{
icon: "chat",
name: "Join the Coder Discord",
target: "",
location: "navbar",
},
{ icon: "star", name: "Star the Repo", target: "" },
],
},
};
4 changes: 4 additions & 0 deletions site/src/modules/dashboard/Navbar/NavbarView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe("NavbarView", () => {
canViewHealth
canViewAuditLog
canViewConnectionLog
supportLinks={[]}
/>,
);
const workspacesLink =
Expand All @@ -52,6 +53,7 @@ describe("NavbarView", () => {
canViewHealth
canViewAuditLog
canViewConnectionLog
supportLinks={[]}
/>,
);
const templatesLink =
Expand All @@ -70,6 +72,7 @@ describe("NavbarView", () => {
canViewHealth
canViewAuditLog
canViewConnectionLog
supportLinks={[]}
/>,
);
const deploymentMenu = await screen.findByText("Admin settings");
Expand All @@ -89,6 +92,7 @@ describe("NavbarView", () => {
canViewHealth
canViewAuditLog
canViewConnectionLog
supportLinks={[]}
/>,
);
const deploymentMenu = await screen.findByText("Admin settings");
Expand Down
48 changes: 46 additions & 2 deletions site/src/modules/dashboard/Navbar/NavbarView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import { cn } from "utils/cn";
import { DeploymentDropdown } from "./DeploymentDropdown";
import { MobileMenu } from "./MobileMenu";
import { ProxyMenu } from "./ProxyMenu";
import { SupportIcon } from "./SupportIcon";
import { UserDropdown } from "./UserDropdown/UserDropdown";

interface NavbarViewProps {
logo_url?: string;
user: TypesGen.User;
buildInfo?: TypesGen.BuildInfoResponse;
supportLinks?: readonly TypesGen.LinkConfig[];
supportLinks: readonly TypesGen.LinkConfig[];
onSignOut: () => void;
canViewDeployment: boolean;
canViewOrganizations: boolean;
Expand Down Expand Up @@ -71,6 +72,16 @@ export const NavbarView: FC<NavbarViewProps> = ({
<NavItems className="ml-4" user={user} />

<div className="flex items-center gap-3 ml-auto">
{supportLinks.filter(isNavbarLink).map((link) => (
<div key={link.name} className="hidden md:block">
<SupportButton
name={link.name}
target={link.target}
icon={link.icon}
/>
</div>
))}

{proxyContextValue && (
<div className="hidden md:block">
<ProxyMenu proxyContextValue={proxyContextValue} />
Expand Down Expand Up @@ -121,7 +132,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
<UserDropdown
user={user}
buildInfo={buildInfo}
supportLinks={supportLinks}
supportLinks={supportLinks?.filter((link) => !isNavbarLink(link))}
onSignOut={onSignOut}
/>
</div>
Expand Down Expand Up @@ -240,3 +251,36 @@ const TasksNavItem: FC<TasksNavItemProps> = ({ user }) => {
function idleTasksLabel(count: number) {
return `You have ${count} ${count === 1 ? "task" : "tasks"} waiting for input`;
}

function isNavbarLink(link: TypesGen.LinkConfig): boolean {
return link.location === "navbar";
}
Comment thread
buenos-nachos marked this conversation as resolved.

interface SupportButtonProps {
name: string;
target: string;
icon: string;
location?: string;
}

const SupportButton: FC<SupportButtonProps> = ({ name, target, icon }) => {
return (
<Button asChild variant="outline">
<a
href={target}
target="_blank"
rel="noreferrer"
className="inline-block"
>
{icon && (
<SupportIcon
icon={icon}
className={"size-5 text-content-secondary"}
/>
)}
{name}
<span className="sr-only"> (link opens in new tab)</span>
</a>
</Button>
);
};
Loading
Loading