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/support/create.tsx
Views: 687
import {1Alert,2Button,3Divider,4Input,5Layout,6Modal,7Radio,8Space,9} from "antd";10import { useRouter } from "next/router";11import { ReactNode, useRef, useState } from "react";12import { Icon } from "@cocalc/frontend/components/icon";13import { is_valid_email_address as isValidEmailAddress } from "@cocalc/util/misc";14import { COLORS } from "@cocalc/util/theme";15import { Paragraph, Title } from "components/misc";16import A from "components/misc/A";17import ChatGPTHelp from "components/openai/chatgpt-help";18import CodeMirror from "components/share/codemirror";19import Loading from "components/share/loading";20import SiteName from "components/share/site-name";21import apiPost from "lib/api/post";22import { MAX_WIDTH } from "lib/config";23import { useCustomize } from "lib/customize";24import getBrowserInfo from "./browser-info";25import RecentFiles from "./recent-files";26import { Type } from "./tickets";27import { NoZendesk } from "./util";28import { VideoItem } from "components/videos";2930const CHATGPT_DISABLED = true;31const MIN_BODY_LENGTH = 16;3233function VSpace({ children }) {34return (35<Space direction="vertical" style={{ width: "100%", fontSize: "12pt" }}>36{children}37</Space>38);39}4041export type Type = "problem" | "question" | "task" | "purchase" | "chat";4243function stringToType(s?: any): Type {44if (45s == "problem" ||46s == "question" ||47s == "task" ||48s == "purchase" ||49s == "chat"50)51return s;52return "problem"; // default;53}5455export default function Create() {56const { account, onCoCalcCom, helpEmail, openaiEnabled, siteName, zendesk } =57useCustomize();58const router = useRouter();59// The URL the user was viewing when they requested support.60// This could easily be blank, but if it is set it can be useful.61const { url } = router.query;62const [files, setFiles] = useState<{ project_id: string; path?: string }[]>(63[],64);65const [type, setType] = useState<Type>(stringToType(router.query.type));66const [email, setEmail] = useState<string>(account?.email_address ?? "");67const [body, setBody] = useState<string>(68router.query.body ? `${router.query.body}` : "",69);70const required = router.query.required ? `${router.query.required}` : "";71const [subject, setSubject] = useState<string>(72router.query.subject ? `${router.query.subject}` : "",73);7475const [submitError, setSubmitError] = useState<ReactNode>("");76const [submitting, setSubmitting] = useState<boolean>(false);77const [success, setSuccess] = useState<ReactNode>("");7879const showExtra = router.query.hideExtra != "true";8081// hasRequired means "has the required information", which82// means that body does NOT have required in it!83const hasRequired = !required || !body.includes(required);8485const submittable = useRef<boolean>(false);86submittable.current = !!(87!submitting &&88!submitError &&89!success &&90isValidEmailAddress(email) &&91subject &&92(body ?? "").length >= MIN_BODY_LENGTH &&93hasRequired94);9596if (!zendesk) {97return <NoZendesk />;98}99100async function createSupportTicket() {101const info = getBrowserInfo();102if (router.query.context) {103// used to pass context info along in the url when104// creating a support ticket,105// e.g., from the crash reporter.106info.context = `${router.query.context}`;107}108const options = { type, files, email, body, url, subject, info };109setSubmitError("");110let result;111try {112setSubmitting(true);113result = await apiPost("/support/create-ticket", { options });114} catch (err) {115setSubmitError(err.message);116return;117} finally {118setSubmitting(false);119}120setSuccess(121<div>122<p>123Please save this URL: <A href={result.url}>{result.url}</A>124</p>125<p>126You can also see the{" "}127<A href="/support/tickets">status of your support tickets</A>.128</p>129</div>,130);131}132133return (134<Layout.Content style={{ backgroundColor: "white" }}>135<div136style={{137maxWidth: MAX_WIDTH,138margin: "15px auto",139padding: "15px",140backgroundColor: "white",141color: COLORS.GRAY_D,142}}143>144<Title level={1} style={{ textAlign: "center" }}>145{router.query.title ?? "Create a New Support Ticket"}146</Title>147{showExtra && (148<>149<Space>150<p style={{ fontSize: "12pt" }}>151Create a new support ticket below or{" "}152<A href="/support/tickets">153check the status of your support tickets154</A>155.{" "}156{helpEmail ? (157<>158You can also email us directly at{" "}159<A href={`mailto:${helpEmail}`}>{helpEmail}</A> or{" "}160<A href="https://calendly.com/cocalc/discovery">161book a demo or discovery call162</A>163.164</>165) : undefined}166</p>167<VideoItem168width={600}169style={{ margin: "15px 0", width: "600px" }}170id={"4Ef9sxX59XM"}171/>172</Space>173{openaiEnabled && onCoCalcCom && !CHATGPT_DISABLED ? (174<ChatGPT siteName={siteName} />175) : undefined}176<FAQ />177<Title level={2}>Create Your Ticket</Title>178<Instructions />179<Divider>Support Ticket</Divider>180</>181)}182<form>183<VSpace>184<b>185<Status done={isValidEmailAddress(email)} /> Your Email Address186</b>187<Input188prefix={189<Icon name="envelope" style={{ color: "rgba(0,0,0,.25)" }} />190}191defaultValue={email}192placeholder="Email address..."193style={{ maxWidth: "500px" }}194onChange={(e) => setEmail(e.target.value)}195/>196<br />197<b>198<Status done={subject} /> Subject199</b>200<Input201placeholder="Summarize what this is about..."202onChange={(e) => setSubject(e.target.value)}203defaultValue={subject}204/>205<br />206<b>207Is this a <i>Problem</i>, <i>Question</i>, or{" "}208<i>Software Install Task</i>?209</b>210<Radio.Group211name="radiogroup"212defaultValue={type}213onChange={(e) => setType(e.target.value)}214>215<VSpace>216<Radio value={"problem"}>217<Type type="problem" /> Something is not working the way I218think it should work.219</Radio>220<Radio value={"question"}>221<Type type="question" /> I have a question about billing,222functionality, teaching, something not working, etc.223</Radio>224<Radio value={"task"}>225<Type type="task" /> Is it possible for you to install some226software that I need in order to use <SiteName />?227</Radio>228<Radio value={"purchase"}>229<Type type="purchase" /> I have a question regarding230purchasing a product.231</Radio>232<Radio value={"chat"}>233<Type type="chat" /> I would like to schedule a video chat.234</Radio>235</VSpace>236</Radio.Group>237<br />238{showExtra && type !== "purchase" && type != "chat" && (239<>240<Files onChange={setFiles} />241<br />242</>243)}244{type == "chat" && (245<h1 style={{ textAlign: "center" }}>246<b>247<A href="https://calendly.com/cocalc">Book a Video Chat...</A>248</b>249</h1>250)}251{type != "chat" && (252<>253<b>254<Status255done={body && body.length >= MIN_BODY_LENGTH && hasRequired}256/>{" "}257Description258</b>259<div260style={{261marginLeft: "30px",262borderLeft: "1px solid lightgrey",263paddingLeft: "15px",264}}265>266{type == "problem" && <Problem onChange={setBody} />}267{type == "question" && (268<Question onChange={setBody} defaultValue={body} />269)}270{type == "purchase" && (271<Purchase272onChange={setBody}273defaultValue={body}274showExtra={showExtra}275/>276)}277{type == "task" && <Task onChange={setBody} />}278</div>279</>280)}281</VSpace>282283<div style={{ textAlign: "center", marginTop: "30px" }}>284{!hasRequired && (285<Alert286showIcon287style={{ margin: "15px 30px" }}288type="error"289description={`You must replace the text '${required}' everywhere above with the requested information.`}290/>291)}292{type != "chat" && (293<Button294shape="round"295size="large"296disabled={!submittable.current}297type="primary"298onClick={createSupportTicket}299>300<Icon name="paper-plane" />{" "}301{submitting302? "Submitting..."303: success304? "Thank you for creating a ticket"305: submitError306? "Close the error box to try again"307: !isValidEmailAddress(email)308? "Enter Valid Email Address above"309: !subject310? "Enter Subject above"311: (body ?? "").length < MIN_BODY_LENGTH312? `Describe your ${type} in detail above`313: "Create Support Ticket"}314</Button>315)}316{submitting && <Loading style={{ fontSize: "32pt" }} />}317{submitError && (318<div>319<Alert320type="error"321message="Error creating support ticket"322description={submitError}323closable324showIcon325onClose={() => setSubmitError("")}326style={{ margin: "15px auto", maxWidth: "500px" }}327/>328<br />329{helpEmail ? (330<>331If you continue to have problems, email us directly at{" "}332<A href={`mailto:${helpEmail}`}>{helpEmail}</A>.333</>334) : undefined}335</div>336)}337{success && (338<Alert339type="success"340message="Successfully created support ticket"341description={success}342onClose={() => {343// simplest way to reset all the information in the form.344router.reload();345}}346closable347showIcon348style={{ margin: "15px auto", maxWidth: "500px" }}349/>350)}351</div>352</form>353<p style={{ marginTop: "30px" }}>354After submitting this, you'll receive a link, which you should save355until you receive a confirmation email. You can also{" "}356<A href="/support/tickets">check the status of your tickets here</A>.357</p>358</div>359</Layout.Content>360);361}362363function Files({ onChange }) {364return (365<VSpace>366<b>Relevant Files</b>367Select any relevant projects and files below. This will make it much368easier for us to quickly understand your problem.369<RecentFiles interval="1 day" onChange={onChange} />370</VSpace>371);372}373374function Problem({ onChange }) {375const answers = useRef<[string, string, string]>(["", "", ""]);376function update(i: 0 | 1 | 2, value: string): void {377answers.current[i] = value;378onChange?.(answers.current.join("\n\n\n").trim());379}380381return (382<VSpace>383<b>What did you do exactly?</b>384<Input.TextArea385rows={3}386placeholder="Describe what you did..."387onChange={(e) =>388update(3890,390e.target.value391? "\n\nWHAT DID YOU DO EXACTLY?\n\n" + e.target.value392: "",393)394}395/>396<br />397<b>What happened?</b>398<Input.TextArea399rows={3}400placeholder="Tell us what happened..."401onChange={(e) =>402update(4031,404e.target.value ? "\n\nWHAT HAPPENED?\n\n" + e.target.value : "",405)406}407/>408<br />409<b>How did this differ from what you expected?</b>410<Input.TextArea411rows={3}412placeholder="Explain how this differs from what you expected..."413onChange={(e) =>414update(4152,416e.target.value417? "\n\nHOW DID THIS DIFFER FROM WHAT YOU EXPECTED?\n\n" +418e.target.value419: "",420)421}422/>423</VSpace>424);425}426427function Question({ defaultValue, onChange }) {428return (429<Input.TextArea430rows={8}431defaultValue={defaultValue}432placeholder="Your question..."433onChange={(e) => onChange(e.target.value)}434/>435);436}437438function Purchase({ defaultValue, onChange, showExtra }) {439return (440<>441{showExtra && (442<Paragraph>443Please describe what you want to purchase. We need some context in444order to guide you. In particular:445<ul>446<li>447The expected number of projects: this is either the number of448users, or how many projects they'll collectively be using.449</li>450<li>451The kind of workload: this ranges from student projects with452minimal resource requirements to large and resource intensive453research projects.454</li>455<li>How long you expect to use the services.</li>456<li>457Your type of organization: i.e. if an academic discount applies to458you.459</li>460</ul>461</Paragraph>462)}463<Input.TextArea464rows={8}465defaultValue={defaultValue}466placeholder="Your purchase request..."467onChange={(e) => onChange(e.target.value)}468/>469</>470);471}472473function Task({ onChange }) {474const answers = useRef<[string, string, string]>(["", "", ""]);475function update(i: 0 | 1 | 2, value: string): void {476answers.current[i] = value;477onChange?.(answers.current.join("\n\n\n").trim());478}479480const [showWestPoint, setShowWestPoint] = useState<boolean>(false);481482return (483<div>484<Modal485width="700px"486open={showWestPoint}487onCancel={() => setShowWestPoint(false)}488onOk={() => setShowWestPoint(false)}489title={490<div>491A question about CoCalc ...492<A href="https://www.westpoint.edu/mathematical-sciences/profile/joseph_lindquist">493<div494style={{495fontSize: "10px",496float: "right",497width: "125px",498margin: "0 20px",499}}500>501<img502style={{ width: "125px" }}503src="https://s3.amazonaws.com/usma-media/styles/profile_image_display/s3/inline-images/academics/academic_departments/mathematical_sciences/images/profiles/COL%20JOE%20LINDQUIST.jpg?itok=r9vjncwh"504/>505Colonel Joe Lindquist506<br />507West Point508</div>509</A>510</div>511}512>513<b>WHAT SOFTWARE DO YOU NEED?</b>514<br />515Hi Team! I'm getting ready to kick off our short course at West Point516that will deal with Natural Language Processing. We're still sorting out517the purchase request, but expect it to be complete in the next day or518so. It looks like you have the "big" packages installed that we will be519exploring... Huggingface, Transformers, NLTK, WordBlob... but another520package that I was hoping to use is vadersentiment (521<A href="https://pypi.org/project/vaderSentiment/">522https://pypi.org/project/vaderSentiment/523</A>524).525<br />526<br />527<b>HOW DO YOU PLAN TO USE THIS SOFTWARE?</b>528<br />529The course begins on 15MAR and I'd love to be able to use it for this.530I'm happy to assume some guidance on how to best incorporate this into531CoCalc if unable to install the package.532<br />533<br />534<b>HOW CAN WE TEST THAT THE SOFTWARE IS PROPERLY INSTALLED?</b>535<CodeMirror536fontSize={12}537lineNumbers={false}538filename="a.py"539content={`from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer540sid_obj = SentimentIntensityAnalyzer()541text = "CoCalc is an amazing platform for students to learn how to understand NLP!"542print(sid_obj.polarity_scores(text))`}543/>544<br />545This should return:546<CodeMirror547fontSize={12}548lineNumbers={false}549filename="a.json"550content={551"{'neg': 0.0, 'neu': 0.746, 'pos': 0.254, 'compound': 0.6239}"552}553/>554<br />555One Day Later556<br />557You guys are fantastic! Such a quick turn-around. Please feel free to558use the request in any fashion you wish 😊559<br />560By the way… in case you were wondering, “You guys are fantastic!” has a561compound polarity score of 0.598 😊. I used it in CoCalc to test the562update.563</Modal>564Each <SiteName /> project is a Docker image running Ubuntu Linux on 64-bit565x86 hardware, so it is possible for us to install most standard Linux566software, and we have already installed{" "}567<A href="/software">a huge amount</A>. If there is something you need that568is missing, let us know below. You can also{" "}569<a onClick={() => setShowWestPoint(true)}>570view a recent ticket from West Point571</a>{" "}572for an example install request.573<br />574<br />575<b>What software do you need?</b> In particular, if this is a Python576library, explain which of the{" "}577<A href="software/python">many Python environments</A> you need it578installed into and why you can't just{" "}579<A href="https://doc.cocalc.com/howto/install-python-lib.html">580install it yourself581</A>582.583<br />584<Input.TextArea585style={{ marginTop: "10px" }}586rows={4}587placeholder="Describe what software you need installed..."588onChange={(e) =>589update(5900,591e.target.value592? "\n\nWHAT SOFTWARE DO YOU NEED?\n\n" + e.target.value593: "",594)595}596/>597<br />598<br />599<br />600<b>How do you plan to use this software?</b> For example, does it need to601be installed across <SiteName /> for a course you are teaching that starts602in 3 weeks?603<br />604<Input.TextArea605style={{ marginTop: "10px" }}606rows={3}607placeholder="Explain how you will use the software ..."608onChange={(e) =>609update(6101,611e.target.value612? "\n\nHOW DO YOU PLAN TO USE THIS SOFTWARE?\n\n" + e.target.value613: "",614)615}616/>617<br />618<br />619<br />620<b>How can we test that the software is properly installed?</b>621<br />622<Input.TextArea623style={{ marginTop: "10px" }}624rows={3}625placeholder="Explain how we can test the software..."626onChange={(e) =>627update(6282,629e.target.value630? "\n\nHOW CAN WE TEST THAT THE SOFTWARE IS PROPERLY INSTALLED?\n\n" +631e.target.value632: "",633)634}635/>636</div>637);638}639function Instructions() {640return (641<div>642<p>643If the above links don't help you solve your problem, please create a644support ticket below. Support is currently available in{" "}645<b>English, German, and Russian</b> only.646</p>647</div>648);649}650651function ChatGPT({ siteName }) {652return (653<div style={{ margin: "15px 0 20px 0" }}>654<Title level={2}>ChatGPT</Title>655<div style={{ color: "#666" }}>656If you have a question about how to do something using {siteName},657ChatGPT might save you some time:658</div>659<ChatGPTHelp style={{ marginTop: "15px" }} tag={"support"} />660</div>661);662}663664function FAQ() {665return (666<div>667<Title level={2}>Helpful Links</Title>668<Alert669message={""}670style={{ margin: "20px 0" }}671type="warning"672description={673<ul style={{ marginBottom: 0, fontSize: "11pt" }}>674<li>675<A href="https://doc.cocalc.com/">The CoCalc Manual</A>676</li>677<li>678<A href="https://github.com/sagemathinc/cocalc/issues">679Bug reports680</A>681</li>682<li>683<A href="https://github.com/sagemathinc/cocalc/discussions">684The CoCalc Discussion Forum685</A>686</li>687<li>688{" "}689<A href="https://doc.cocalc.com/howto/missing-project.html">690Help: My file or project appears to be missing!691</A>{" "}692</li>693<li>694{" "}695I have{" "}696<A href="https://doc.cocalc.com/howto/sage-question.html">697general questions about SageMath...698</A>699</li>700</ul>701}702/>703</div>704);705}706707function Status({ done }) {708return (709<Icon710style={{711color: done ? "green" : "red",712fontWeight: "bold",713fontSize: "12pt",714}}715name={done ? "check" : "arrow-right"}716/>717);718}719720721