CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/next/components/account/navtab.tsx
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2021 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/* The "Account" navigation tab in the bar at the top. */
7
8
import type { MenuProps } from "antd";
9
import { Dropdown } from "antd";
10
import { join } from "path";
11
import { CSSProperties } from "react";
12
13
import { Icon } from "@cocalc/frontend/components/icon";
14
import Avatar from "components/account/avatar";
15
import {
16
menuGroup,
17
menuItem,
18
MenuItem,
19
MenuItems,
20
} from "components/antd-menu-items";
21
import A from "components/misc/A";
22
import apiPost from "lib/api/post";
23
import basePath from "lib/base-path";
24
import { useCustomize } from "lib/customize";
25
import useProfile from "lib/hooks/profile";
26
import { useRouter } from "next/router";
27
28
const DIVIDER = {
29
type: "divider",
30
} as const;
31
32
interface Props {
33
style: CSSProperties;
34
}
35
36
// We make this menu fixed width in all cases, since otherwise the entire top navbar
37
// would flicker when profile isn't initially defined. See
38
// https://github.com/sagemathinc/cocalc/issues/6504
39
40
const WIDTH = "125px";
41
42
export default function AccountNavTab({ style }: Props) {
43
const router = useRouter();
44
const { isCommercial, shareServer, siteName, sshGateway } = useCustomize();
45
const profile = useProfile();
46
if (!profile) {
47
return (
48
<div
49
style={{
50
cursor: "pointer",
51
...style,
52
width: WIDTH,
53
}}
54
>
55
Account
56
</div>
57
);
58
}
59
60
const { first_name, last_name, name, account_id, is_admin, is_anonymous } =
61
profile;
62
63
const profile_url = name ? `/${name}` : `/share/accounts/${account_id}`;
64
65
const signedIn = menuItem(
66
"signed-in",
67
<A href={is_anonymous ? "/config/search/input" : profile_url}>
68
Signed into {siteName} as
69
<br />
70
<b>
71
{first_name} {last_name}
72
{name ? ` (@${name})` : ""}
73
</b>
74
</A>
75
);
76
77
const docs = menuItem(
78
"docs",
79
<A href="https://doc.cocalc.com" external>
80
Documentation
81
</A>,
82
"book"
83
);
84
85
const configuration = menuGroup(
86
"configuration",
87
<A href="/config/search/input">
88
<span style={{ color: "#a4acb3" }}>
89
<Icon name="wrench" /> Configuration
90
</span>
91
</A>,
92
[
93
menuItem("account", <A href="/config/account/name">Account</A>, "user"),
94
menuItem(
95
"editor",
96
<A href="/config/editor/appearance">Editor</A>,
97
"edit"
98
),
99
menuItem(
100
"system",
101
<A href="/config/system/appearance">System</A>,
102
"gear"
103
),
104
]
105
);
106
107
function profileItems() {
108
if (!profile) return [];
109
const ret: MenuItems = [];
110
ret.push(signedIn);
111
if (is_anonymous) {
112
ret.push(
113
menuItem(
114
"sign-up",
115
<A href="/config/search/input">
116
<b>Sign Up (save your work)!</b>
117
</A>,
118
"user"
119
)
120
);
121
}
122
ret.push(docs);
123
if (isCommercial) {
124
ret.push(menuItem("store", <A href="/store">Store</A>, "shopping-cart"));
125
}
126
ret.push(DIVIDER);
127
ret.push(configuration);
128
ret.push(DIVIDER);
129
return ret;
130
}
131
132
function yourPages(): MenuItem[] {
133
const yours: MenuItem[] = [];
134
yours.push(
135
menuItem(
136
"projects",
137
<a href={join(basePath, "projects")}>
138
{is_anonymous ? "Project" : "Projects"}
139
</a>,
140
"edit"
141
)
142
);
143
144
if (!is_anonymous) {
145
yours.push(menuItem("licenses", <A href="/licenses">Licenses</A>, "key"));
146
147
if (isCommercial) {
148
yours.push(
149
menuItem(
150
"billing",
151
<A href="/billing">Billing Management</A>,
152
"credit-card"
153
)
154
);
155
}
156
if (sshGateway) {
157
yours.push(
158
menuItem(
159
"ssh",
160
<A href={join(basePath, "settings", "ssh-keys")} external>
161
SSH Keys
162
</A>,
163
"key"
164
)
165
);
166
}
167
168
if (shareServer) {
169
yours.push(
170
menuItem(
171
"shared",
172
<A
173
href={
174
profile?.name ? `/${name}` : `/share/accounts/${account_id}`
175
}
176
external
177
>
178
Shared Files
179
</A>,
180
"bullhorn"
181
)
182
);
183
184
yours.push(
185
menuItem("stars", <A href="/stars">Stars</A>, "star-filled")
186
);
187
}
188
}
189
190
return [
191
menuGroup(
192
"your",
193
<span style={{ color: "#a4acb3" }}>
194
<Icon name="user" /> Your...
195
</span>,
196
yours
197
),
198
];
199
}
200
201
function admin(): MenuItem[] {
202
if (!is_admin) return [];
203
return [
204
DIVIDER,
205
menuItem(
206
"admin",
207
<a href={join(basePath, "admin")}>Site Administration</a>,
208
"settings"
209
),
210
];
211
}
212
213
const signout: MenuItem[] = [
214
DIVIDER,
215
menuItem(
216
"sign-out",
217
<A
218
onClick={async () => {
219
await apiPost("/accounts/sign-out", { all: false });
220
router.push("/");
221
}}
222
>
223
Sign Out
224
</A>
225
),
226
];
227
228
const items: MenuProps["items"] = [
229
...profileItems(),
230
...yourPages(),
231
...admin(),
232
...signout,
233
];
234
235
// NOTE: we had a dark theme before for the menu, but that's deprecated from antd
236
// https://github.com/ant-design/ant-design/issues/4903
237
return (
238
<div
239
style={{
240
display: "inline-block",
241
cursor: "pointer",
242
width: WIDTH,
243
}}
244
>
245
{/* The negative margin fixes some weird behavior that stretches header. */}
246
{account_id && (
247
<>
248
<Avatar account_id={account_id} style={{ margin: "-10px 0" }} />
249
&nbsp;&nbsp;
250
</>
251
)}
252
<Dropdown menu={{ items }} trigger={["click"]}>
253
<span style={style}>Account ▼</span>
254
</Dropdown>
255
</div>
256
);
257
}
258
259