Path: blob/master/src/packages/next/components/account/navtab.tsx
1450 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 Avatar from "components/account/avatar";14import {15menuGroup,16menuItem,17MenuItem,18MenuItems,19} from "components/antd-menu-items";20import A from "components/misc/A";21import apiPost from "lib/api/post";22import basePath from "lib/base-path";23import { useCustomize } from "lib/customize";24import useProfile from "lib/hooks/profile";25import { useRouter } from "next/router";2627const DIVIDER = {28type: "divider",29} as const;3031interface Props {32style: CSSProperties;33}3435// We make this menu fixed width in all cases, since otherwise the entire top navbar36// would flicker when profile isn't initially defined. See37// https://github.com/sagemathinc/cocalc/issues/65043839const WIDTH = "125px";4041export default function AccountNavTab({ style }: Props) {42const router = useRouter();43const { isCommercial, shareServer, siteName, sshGateway } = useCustomize();44const profile = useProfile();45if (!profile) {46return (47<div48style={{49cursor: "pointer",50...style,51width: WIDTH,52}}53>54Account55</div>56);57}5859const { first_name, last_name, name, account_id, is_admin, is_anonymous } =60profile;6162const profile_url = name ? `/${name}` : `/share/accounts/${account_id}`;6364const signedIn = menuItem(65"signed-in",66<A href={is_anonymous ? "/config/search/input" : profile_url}>67Signed into {siteName} as68<br />69<b>70{first_name} {last_name}71{name ? ` (@${name})` : ""}72</b>73</A>,74);7576const docs = menuItem(77"docs",78<A href="https://doc.cocalc.com" external>79Documentation80</A>,81"book",82);8384const configuration = menuGroup(85"configuration",86<A href="/settings">87<span style={{ color: "#a4acb3" }}>88<Icon name="wrench" /> Account89</span>90</A>,91[92menuItem(93"preferences",94<A href="/settings/account">Preferences</A>,95"address-card",96),97DIVIDER,98menuItem(99"subscriptions",100<A href="/settings/subscriptions">Subscriptions</A>,101"calendar",102),103menuItem("licenses", <A href="/settings/licenses">Licenses</A>, "key"),104menuItem(105"payg",106<A href="/settings/payg">Pay As You go</A>,107"line-chart",108),109DIVIDER,110menuItem(111"purchases",112<A href="/settings/purchases">Purchases</A>,113"money-check",114),115menuItem(116"payments",117<A href="/settings/payments">Payments</A>,118"credit-card",119),120menuItem(121"payment-methods",122<A href="/settings/payment-methods">Payment Methods</A>,123"credit-card",124),125menuItem(126"statements",127<A href="/settings/statements">Statements</A>,128"calendar-week",129),130],131);132133function profileItems() {134if (!profile) return [];135const ret: MenuItems = [];136ret.push(signedIn);137if (is_anonymous) {138ret.push(139menuItem(140"sign-up",141<A href="/config/search/input">142<b>Sign Up (save your work)!</b>143</A>,144"user",145),146);147}148ret.push(docs);149if (isCommercial) {150ret.push(menuItem("store", <A href="/store">Store</A>, "shopping-cart"));151}152ret.push(DIVIDER);153ret.push(configuration);154ret.push(DIVIDER);155return ret;156}157158function yourPages(): MenuItem[] {159const yours: MenuItem[] = [];160yours.push(161menuItem(162"projects",163<a href={join(basePath, "projects")}>164{is_anonymous ? "Project" : "Projects"}165</a>,166"edit",167),168);169170if (!is_anonymous) {171yours.push(172menuItem(173"messages",174<A href="/notifications#page=messages-inbox">Messages</A>,175"mail",176),177);178yours.push(179menuItem(180"mentions",181<A href="/notifications#page=unread">@-Mentions</A>,182"comment",183),184);185yours.push(186menuItem(187"cloud-filesystems",188<A href="/settings/cloud-filesystems">Cloud Filesystems</A>,189"user",190),191);192yours.push(193menuItem(194"support",195<A href="/settings/support">Support Tickets</A>,196"user",197),198);199if (sshGateway) {200yours.push(201menuItem(202"ssh",203<A href={join(basePath, "settings", "ssh-keys")} external>204SSH Keys205</A>,206"key",207),208);209}210211if (shareServer) {212yours.push(213menuItem(214"shared",215<A216href={217profile?.name ? `/${name}` : `/share/accounts/${account_id}`218}219external220>221Shared Files222</A>,223"bullhorn",224),225);226227yours.push(228menuItem(229"stars",230<A href="/stars">Starred Files</A>,231"star-filled",232),233);234}235}236237return [238menuGroup(239"your",240<span style={{ color: "#a4acb3" }}>241<Icon name="user" /> Your...242</span>,243yours,244),245];246}247248function admin(): MenuItem[] {249if (!is_admin) return [];250return [251DIVIDER,252menuItem(253"admin",254<a href={join(basePath, "admin")}>Site Administration</a>,255"settings",256),257];258}259260const signout: MenuItem[] = [261DIVIDER,262menuItem(263"sign-out",264<A265onClick={async () => {266await apiPost("/accounts/sign-out", { all: false });267router.push("/");268}}269>270Sign Out271</A>,272),273];274275const items: MenuProps["items"] = [276...profileItems(),277...yourPages(),278...admin(),279...signout,280];281282// NOTE: we had a dark theme before for the menu, but that's deprecated from antd283// https://github.com/ant-design/ant-design/issues/4903284return (285<div286style={{287display: "inline-block",288cursor: "pointer",289width: WIDTH,290}}291>292{/* The negative margin fixes some weird behavior that stretches header. */}293{account_id && (294<>295<Avatar account_id={account_id} style={{ margin: "-10px 0" }} />296 297</>298)}299<Dropdown menu={{ items }} trigger={["click"]}>300<span style={style}>Account ▼</span>301</Dropdown>302</div>303);304}305306307