Path: blob/master/src/packages/next/components/account/navtab.tsx
6014 views
/*1* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/* The "Account" navigation tab in the bar at the top. */67import type { MenuProps } from "antd";8import { Dropdown } from "antd";9import { join } from "path";10import { CSSProperties } from "react";1112import { Icon } from "@cocalc/frontend/components/icon";13import {14type PreferencesSubTabType,15type SettingsPageType,16} from "@cocalc/util/types/settings";17import Avatar from "components/account/avatar";18import {19menuGroup,20menuItem,21MenuItem,22MenuItems,23} from "components/antd-menu-items";24import A from "components/misc/A";25import apiPost from "lib/api/post";26import basePath from "lib/base-path";27import { useCustomize } from "lib/customize";28import useProfile from "lib/hooks/profile";29import { useRouter } from "next/router";3031const DIVIDER = {32type: "divider",33} as const;3435// Type-safe settings link helper36type SettingsLink =37| `/settings/${SettingsPageType}`38| `/settings/preferences/${PreferencesSubTabType}`;3940// Helper function to create type-safe settings links with basePath prefix41function createSettingsLink(path: SettingsLink): string {42return join(basePath, path);43}4445interface Props {46style: CSSProperties;47}4849// We make this menu fixed width in all cases, since otherwise the entire top navbar50// would flicker when profile isn't initially defined. See51// https://github.com/sagemathinc/cocalc/issues/65045253const WIDTH = "125px";5455export default function AccountNavTab({ style }: Props) {56const router = useRouter();57const { isCommercial, shareServer, siteName, sshGateway } = useCustomize();58const profile = useProfile();59if (!profile) {60return (61<div62style={{63cursor: "pointer",64...style,65width: WIDTH,66}}67>68Account69</div>70);71}7273const { first_name, last_name, name, account_id, is_admin, is_anonymous } =74profile;7576const profile_url = name ? `/${name}` : `/share/accounts/${account_id}`;7778const signedIn = menuItem(79"signed-in",80<A href={is_anonymous ? "/config/search/input" : profile_url}>81Signed into {siteName} as82<br />83<b>84{first_name} {last_name}85{name ? ` (@${name})` : ""}86</b>87</A>,88);8990const docs = menuItem(91"docs",92<A href="https://doc.cocalc.com" external>93Documentation94</A>,95"book",96);9798const configuration = menuGroup(99"configuration",100<A href={createSettingsLink("/settings/profile")}>101<span style={{ color: "#a4acb3" }}>102<Icon name="wrench" /> Account103</span>104</A>,105[106menuItem(107"profile",108<A href={createSettingsLink("/settings/profile")}>Profile</A>,109"address-card",110),111menuItem(112"settings",113<A href={createSettingsLink("/settings/index")}>Settings</A>,114"cogs",115),116menuItem(117"appearance",118<A href={createSettingsLink("/settings/preferences/appearance")}>119Appearance120</A>,121"highlighter",122),123menuItem(124"communication",125<A href={createSettingsLink("/settings/preferences/communication")}>126Communication127</A>,128"mail",129),130menuItem(131"keys",132<A href={createSettingsLink("/settings/preferences/keys")}>133SSH & API Keys134</A>,135"key",136),137DIVIDER,138menuItem(139"subscriptions",140<A href={createSettingsLink("/settings/subscriptions")}>141Subscriptions142</A>,143"calendar",144),145menuItem(146"licenses",147<A href={createSettingsLink("/settings/licenses")}>Licenses</A>,148"key",149),150menuItem(151"payg",152<A href={createSettingsLink("/settings/payg")}>Pay As You Go</A>,153"line-chart",154),155DIVIDER,156menuItem(157"purchases",158<A href={createSettingsLink("/settings/purchases")}>Purchases</A>,159"money-check",160),161menuItem(162"payments",163<A href={createSettingsLink("/settings/payments")}>Payments</A>,164"credit-card",165),166menuItem(167"statements",168<A href={createSettingsLink("/settings/statements")}>Statements</A>,169"calendar-week",170),171],172);173174function profileItems() {175if (!profile) return [];176const ret: MenuItems = [];177ret.push(signedIn);178if (is_anonymous) {179ret.push(180menuItem(181"sign-up",182<A href="/config/search/input">183<b>Sign Up (save your work)!</b>184</A>,185"user",186),187);188}189ret.push(docs);190if (isCommercial) {191ret.push(menuItem("store", <A href="/store">Store</A>, "shopping-cart"));192}193ret.push(DIVIDER);194ret.push(configuration);195ret.push(DIVIDER);196return ret;197}198199function yourPages(): MenuItem[] {200const yours: MenuItem[] = [];201yours.push(202menuItem(203"projects",204<a href={join(basePath, "projects")}>205{is_anonymous ? "Project" : "Projects"}206</a>,207"edit",208),209);210211if (!is_anonymous) {212yours.push(213menuItem(214"messages",215<A href="/notifications#page=messages-inbox">Messages</A>,216"mail",217),218);219yours.push(220menuItem(221"mentions",222<A href="/notifications#page=unread">@-Mentions</A>,223"comment",224),225);226yours.push(227menuItem(228"support",229<A href={createSettingsLink("/settings/support")}>Support Tickets</A>,230"medkit",231),232);233234if (sshGateway) {235yours.push(236menuItem(237"ssh",238<A href={createSettingsLink("/settings/preferences/keys")}>239SSH Keys240</A>,241"key",242),243);244}245246if (shareServer) {247yours.push(248menuItem(249"shared",250<A251href={252profile?.name ? `/${name}` : `/share/accounts/${account_id}`253}254external255>256Shared Files257</A>,258"bullhorn",259),260);261262// TODO: redundant with the above?263// yours.push(264// menuItem(265// "shared",266// <A href={createSettingsLink("/settings/public-files")}>267// Published Files268// </A>,269// "share-square",270// ),271// );272273yours.push(274menuItem("stars", <A href="/stars">Starred Files</A>, "star-filled"),275);276}277}278279return [280menuGroup(281"your",282<span style={{ color: "#a4acb3" }}>283<Icon name="user" /> Your...284</span>,285yours,286),287];288}289290function admin(): MenuItem[] {291if (!is_admin) return [];292return [293DIVIDER,294menuItem(295"admin",296<a href={join(basePath, "admin")}>Site Administration</a>,297"settings",298),299];300}301302const signout: MenuItem[] = [303DIVIDER,304menuItem(305"sign-out",306<A307onClick={async () => {308await apiPost("/accounts/sign-out", { all: false });309router.push("/");310}}311>312Sign Out313</A>,314),315];316317const items: MenuProps["items"] = [318...profileItems(),319...yourPages(),320...admin(),321...signout,322];323324// NOTE: we had a dark theme before for the menu, but that's deprecated from antd325// https://github.com/ant-design/ant-design/issues/4903326return (327<div328style={{329display: "inline-block",330cursor: "pointer",331width: WIDTH,332}}333>334{/* The negative margin fixes some weird behavior that stretches header. */}335{account_id && (336<>337<Avatar account_id={account_id} style={{ margin: "-10px 0" }} />338 339</>340)}341<Dropdown menu={{ items }} trigger={["click"]}>342<span style={style}>Account ▼</span>343</Dropdown>344</div>345);346}347348349