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/tickets.tsx
Views: 687
import { Alert, Divider, Layout, Tag, Timeline, Tooltip } from "antd";1import { ReactNode } from "react";23import { Icon } from "@cocalc/frontend/components/icon";4import Markdown from "@cocalc/frontend/editors/slate/static-markdown";5import { capitalize, trunc } from "@cocalc/util/misc";6import { COLORS } from "@cocalc/util/theme";7import { Title } from "components/misc";8import A from "components/misc/A";9import Loading from "components/share/loading";10import { MAX_WIDTH } from "lib/config";11import { useCustomize } from "lib/customize";12import useAPI from "lib/hooks/api";13import type { Type } from "./create";14import { NoZendesk } from "./util";1516export default function Tickets() {17let { result, error } = useAPI("support/tickets");18const { zendesk } = useCustomize();1920if (!zendesk) {21return <NoZendesk />;22}23return (24<Layout.Content style={{ backgroundColor: "white" }}>25<div26style={{27maxWidth: MAX_WIDTH,28margin: "15px auto",29padding: "15px",30backgroundColor: "white",31color: COLORS.GRAY_D,32}}33>34<Title level={1} style={{ textAlign: "center" }}>35Support Tickets36</Title>37<p style={{ fontSize: "12pt" }}>38Check the status of your support tickets here or{" "}39<A href="/support/new">create a new ticket</A>. Newly created tickets40do not appear here for a few minutes.41</p>42{error && (43<Alert44style={{ margin: "30px 0" }}45type="error"46message={"Error loading support tickets"}47description={error}48/>49)}50<Divider>Tickets</Divider>51<br />52{result ? (53<SupportTimeline tickets={result.tickets} />54) : (55<Loading style={{ fontSize: "20pt" }} />56)}57</div>58</Layout.Content>59);60}6162function SupportTimeline({ tickets }) {63const v: ReactNode[] = [];64for (const ticket of tickets ?? []) {65v.push(66<Timeline.Item key={ticket.id} color={statusToColor(ticket.status)}>67<Ticket ticket={ticket} />68</Timeline.Item>,69);70}71return <Timeline>{v}</Timeline>;72}7374const COLOR = {75new: "orange",76open: "orange",77pending: "red",78solved: "#666",79};8081function statusToColor(status: string): string {82return COLOR[status] ?? "#f5ca00";83}8485function Ticket({ ticket }) {86const {87id,88userURL,89created_at,90updated_at,91description,92status,93subject,94type,95} = ticket;96return (97<div style={{ marginBottom: "15px" }}>98<div style={{ float: "right" }}>99<Type type={type} status={status} />100</div>101<A href={userURL}>102<Status status={status} />103<b style={{ fontSize: "13pt" }}>{trunc(subject, 80)}</b>104</A>105<br />106<div style={{ float: "right" }}>107<Tooltip title="Click to visit Zendesk and see all responses to your support request.">108<A href={userURL}>109<Icon name="external-link" /> Ticket: {id}110</A>111</Tooltip>112</div>113(created: {dateToString(created_at)}, updated: {dateToString(updated_at)})114<br />115<div116style={{117overflow: "auto",118maxHeight: "30vh",119border: "1px solid lightgrey",120padding: "15px",121marginTop: "5px",122backgroundColor: "#fdfdfd",123borderRadius: "3px",124}}125>126<Markdown value={description} />127</div>128</div>129);130}131132// Note that this is what to show from the POV of the user.133// See https://github.com/sagemathinc/cocalc/issues/6239134135interface StatusDescription {136title: string;137label: string;138color: string;139}140141const STATUS: { [status: string]: StatusDescription } = {142pending: {143title: "We are waiting for your response.",144label: "AWAITING YOUR REPLY",145color: "#f5ca00",146},147new: {148title: "We are looking at your support request but have not responded yet.",149label: "NEW",150color: "#59bbe0" /* blue */,151},152open: {153title: "We are trying to solve your support request.",154label: "OPEN",155color: "#59bbe0",156},157solved: {158title: "This support request has been solved.",159label: "SOLVED",160color: "#666",161},162};163164function Status({ status }) {165const { title, label, color } = STATUS[status] ?? {166title: "",167label: "Status",168color: "blue",169};170return (171<Tooltip title={title}>172<Tag color={color} style={{ fontSize: "12pt", color: "white" }}>173{label}174</Tag>175</Tooltip>176);177}178179const TYPE_COLOR: { [name in Type]: string } = {180problem: "red",181question: "blue",182task: "orange",183purchase: "green",184chat: "purple",185};186187export function Type({ status, type }: { status?: string; type: Type }) {188return (189<Tag190color={status == "solved" ? COLORS.GRAY_M : TYPE_COLOR[type]}191style={{ fontSize: "12pt" }}192>193{capitalize(type)}194</Tag>195);196}197198function dateToString(d: string): string {199try {200return new Date(d).toLocaleString();201} catch (_err) {202return d;203}204}205206207