Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/admin/users/user.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Display of basic information about a user, with link to get more information about that user.7*/89import { Icon, Gap, TimeAgo } from "@cocalc/frontend/components";10import { Component, Rendered } from "@cocalc/frontend/app-framework";11import { capitalize } from "@cocalc/util/misc";12import { Row, Col } from "@cocalc/frontend/antd-bootstrap";13import { User } from "@cocalc/frontend/frame-editors/generic/client";14import { Projects } from "./projects";15import { Impersonate } from "./impersonate";16import { PasswordReset } from "./password-reset";17import { Ban } from "./ban";18import PayAsYouGoMinBalance from "@cocalc/frontend/frame-editors/crm-editor/users/pay-as-you-go-min-balance";19import { PurchasesButton } from "@cocalc/frontend/purchases/purchases";20import { CopyToClipBoard } from "@cocalc/frontend/components";21import Money from "./money";2223interface State {24projects: boolean;25purchases: boolean;26activity: boolean;27impersonate: boolean;28password: boolean;29ban: boolean;30}3132interface HeaderProps {33header: true;34first_name: string;35last_name: string;36email_address: string;37created: string;38last_active: string;39account_id: string;40banned?: undefined;41}4243interface UserProps extends User {44header?: false;45}4647type Props = HeaderProps | UserProps;4849type More =50| "projects"51| "purchases"52| "activity"53| "impersonate"54| "password"55| "ban";5657const MORE: More[] = [58"projects",59"purchases",60"activity",61"impersonate",62"password",63"ban",64];6566export class UserResult extends Component<Props, State> {67constructor(props, state) {68super(props, state);69const x: any = {};70for (const name of MORE) {71x[name] = false;72}73this.state = x as State;74}7576render_created(): Rendered {77if (!this.props.created) {78return <span>unknown</span>;79}80return <TimeAgo date={this.props.created} />;81}8283render_last_active(): Rendered {84if (!this.props.last_active) {85return <span>unknown</span>;86}87return <TimeAgo date={this.props.last_active} />;88}8990render_purchases(): Rendered {91if (!this.state.purchases) {92return;93}94return (95<div style={{ margin: "15px 0" }}>96<Money account_id={this.props.account_id} />97<div style={{ height: "15px" }} />98<PayAsYouGoMinBalance account_id={this.props.account_id} />99<div style={{ height: "15px" }} />100<PurchasesButton account_id={this.props.account_id} />101</div>102);103}104105render_projects(): Rendered {106if (!this.state.projects) {107return;108}109return (110<Projects111account_id={this.props.account_id}112title={`Recently active projects that ${this.props.first_name} ${this.props.last_name} collaborates on`}113/>114);115}116117render_impersonate(): Rendered {118if (!this.state.impersonate) {119return;120}121return (122<Impersonate123account_id={this.props.account_id}124first_name={this.props.first_name ?? ""}125last_name={this.props.last_name ?? ""}126/>127);128}129130render_password(): Rendered {131if (!this.state.password) {132return;133}134return <PasswordReset email_address={this.props.email_address} />;135}136137render_ban(): Rendered {138if (!this.state.ban) {139return;140}141return (142<Ban143account_id={this.props.account_id}144banned={this.props.banned}145name={`${this.props.first_name} ${this.props.last_name} ${this.props.email_address}`}146/>147);148}149150render_caret(show: boolean): Rendered {151if (show) {152return <Icon name="caret-down" />;153} else {154return <Icon name="caret-right" />;155}156}157158render_more_link(name: More): Rendered {159// sorry about the any below; I could NOT get typescript to work.160return (161<a162style={{ cursor: "pointer" }}163onClick={() => (this as any).setState({ [name]: !this.state[name] })}164>165{this.render_caret(this.state[name])} {capitalize(name)}166</a>167);168}169170render_more_links(): Rendered {171return (172<div>173{this.render_more_link("projects")}174<Gap />175<Gap />176{this.render_more_link("purchases")}177<Gap />178<Gap />179{this.render_more_link("impersonate")}180<Gap />181<Gap />182{this.render_more_link("password")}183<Gap />184<Gap />185{this.render_more_link("ban")}186</div>187);188}189190render_banned(): Rendered {191if (!this.props.banned) return;192return (193<div194style={{195fontSize: "10pt",196color: "white",197paddingLeft: "5px",198background: "red",199}}200>201BANNED202</div>203);204}205206render_row(): Rendered {207return (208<div>209<Row style={{ borderTop: "1px solid #ccc" }}>210<Col md={1} style={{ overflow: "auto" }}>211{this.props.first_name}212</Col>213<Col md={1} style={{ overflow: "auto" }}>214{this.props.last_name}215</Col>216<Col md={2} style={{ overflow: "auto" }}>217{this.props.email_address}218</Col>219<Col md={2}>220{this.render_last_active()} ({this.render_created()})221</Col>222<Col md={4}>{this.render_more_links()}</Col>223<Col md={2} style={{ overflow: "auto" }}>224<div225style={{226paddingTop: "8px",227overflowX: "scroll",228}}229>230<CopyToClipBoard231before232value={this.props.account_id}233size="small"234/>235{this.render_banned()}236</div>237</Col>238</Row>239{this.render_impersonate()}240{this.render_password()}241{this.render_ban()}242{this.render_projects()}243{this.render_purchases()}244</div>245);246}247248render_row_header(): Rendered {249return (250<div style={{ color: "#666" }}>251<Row>252<Col md={1} style={{ overflow: "auto" }}>253<b>{this.props.first_name}</b>254</Col>255<Col md={1} style={{ overflow: "auto" }}>256<b>{this.props.last_name}</b>257</Col>258<Col md={2} style={{ overflow: "auto" }}>259<b>{this.props.email_address}</b>260</Col>261<Col md={2}>262<b>263{this.props.last_active} ({this.props.created}){" "}264<Icon name="caret-down" />{" "}265</b>266</Col>267<Col md={4}>268<b>More...</b>269</Col>270<Col md={2}>271<b>{this.props.account_id}</b>272</Col>273</Row>274</div>275);276}277278render(): Rendered {279if (this.props.header) {280return this.render_row_header();281} else {282return this.render_row();283}284}285}286287288