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/next/components/licenses/license.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2022 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Alert, Popover, Progress } from "antd";6import { CSSProperties } from "react";78import { describe_quota } from "@cocalc/util/licenses/describe-quota";9import { capitalize, stripeAmount } from "@cocalc/util/misc";10import { Paragraph } from "components/misc";11import A from "components/misc/A";12import Copyable from "components/misc/copyable";13import Timestamp from "components/misc/timestamp";14import Loading from "components/share/loading";15import useAPI from "lib/hooks/api";16import { EditableDescription, EditableTitle } from "./editable-license";1718interface Props {19license_id: string;20contrib?: { [project_id: string]: object };21style?: CSSProperties;22}2324export default function License({ license_id, style }: Props) {25// TODO: do something with contrib26return (27<Popover28content={() => <Details license_id={license_id} />}29title={license_id}30mouseEnterDelay={0.5}31>32<A33style={{ cursor: "pointer", fontFamily: "monospace", ...style }}34href={`/licenses/how-used?license_id=${license_id}`}35>36{license_id}37</A>38</Popover>39);40}4142/*43{44"id": "cad2fe88-29d7-4f4e-987c-a3ae6143a25b",45"title": "different business license for specific time",46"description": "",47"expires": 1642799517638,48"activates": 1640121117638,49"last_used": null,50"managers": [51"93620c6e-324a-4217-a60e-2ac436953174"52],53"upgrades": null,54"quota": {55"cpu": 1,56"ram": 1,57"disk": 1,58"user": "business",59"member": true,60"dedicated_cpu": 0,61"dedicated_ram": 0,62"always_running": true63},64"run_limit": 3,65is_manager:true,66number_running:267}68*/6970interface DetailsProps {71license_id: string; // the license id72style?: CSSProperties; // style for the outer div73condensed?: boolean; // if true, only show a brief run_limit x quota description74type?: "cost" | "all";75plan?: { amount: number; currency: string };76}7778export function Details(props: DetailsProps) {79const { license_id, style, type = "all", condensed = false, plan } = props;80const { result, error } = useAPI("licenses/get-license", { license_id }, 3); // 3s cache81if (error) {82return <Alert type="error" message={error} />;83}84if (!result) {85return <Loading />;86}8788function title() {89if (condensed) return;90return (91(result.title || result.is_manager) && (92<Paragraph style={{ fontWeight: "bold", fontSize: "13pt" }}>93{result.is_manager ? (94<EditableTitle license_id={license_id} title={result.title} />95) : (96"Title: " + result.title97)}98</Paragraph>99)100);101}102103function description() {104if (condensed) return;105return (106(result.description || result.is_manager) && (107<Paragraph>108{result.is_manager ? (109<EditableDescription110license_id={license_id}111description={result.description}112/>113) : (114"Description: " + result.description115)}116</Paragraph>117)118);119}120121function managers() {122if (condensed) return;123return (124result.managers != null && (125<Paragraph>You are a manager of this license.</Paragraph>126)127);128}129130function date() {131if (condensed) return;132return (133<Paragraph>134<DateRange {...result} />135</Paragraph>136);137}138139function lastUsed() {140if (condensed) return;141return (142result.last_used != null && (143<Paragraph>144Last used: <Timestamp epoch={result.last_used} />145</Paragraph>146)147);148}149150function quota() {151return (152<Paragraph>153{condensed ? `${result.run_limit ?? 1} x` : "Quota:"}{" "}154<Quota quota={result.quota} upgrades={result.upgrades} />155</Paragraph>156);157}158159function runLimit() {160if (condensed) return null;161return (162result.run_limit != null && (163<Paragraph style={{ width: "100%", display: "flex" }}>164Run limit: {result.run_limit}165{result.number_running != null166? `; Currently running: ${result.number_running}`167: ""}168{result.run_limit && result.number_running != null && (169<Progress170style={{171marginLeft: "15px",172flex: 1,173}}174percent={Math.round(175(result.number_running / result.run_limit) * 100176)}177/>178)}179</Paragraph>180)181);182}183184function copyId() {185if (condensed) return;186return (187result.is_manager && (188<Copyable label="ID:" value={license_id} style={{ marginTop: "5px" }} />189)190);191}192193// this is a special case (fallback), used in billing/subscriptions. It loads additional license data194// and combines that with the amount and currency, already known from the plan it looks at.195if (type === "cost") {196if (plan == null) {197return <></>;198}199return (200<div style={style}>201Cost:{" "}202{stripeAmount(203plan.amount,204plan.currency,205result.info?.purchased.quantity ?? 1206)}207</div>208);209}210211return (212<div style={style}>213{title()}214{description()}215{managers()}216{date()}217{lastUsed()}218{quota()}219{runLimit()}220{copyId()}221</div>222);223}224225export function Quota({ quota, upgrades }: { quota?: any; upgrades?: any }) {226if (quota == null) {227if (upgrades == null) {228return <></>;229} else {230// These are very old, and we just do a little bit to display231// something valid.232quota = {233cpu: upgrades.cores,234ram: upgrades.memory / 1000,235};236}237}238239const info = describe_quota(quota, true);240return <span>{info}</span>;241}242243export function DateRange({ activates, expires, info }) {244const isExpired = expires && expires < Date.now();245const sub = info?.purchased?.subscription;246if (sub && sub != "no") {247return (248<span>249{capitalize(sub)} subscription250{isExpired && (251<>252<b> Expired </b> <Timestamp epoch={expires} absolute dateOnly />253</>254)}255</span>256);257}258const dates = (259<>260<Timestamp epoch={activates} absolute dateOnly /> –{" "}261<Timestamp epoch={expires} absolute dateOnly />262</>263);264return (265<span>266{isExpired ? (267<>268<b>Expired </b>(was valid {dates})269</>270) : (271<>Valid: {dates}</>272)}273</span>274);275}276277278