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/course/configuration/configuration-panel.tsx
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { Card, Col, Row, Spin } from "antd";6import { debounce } from "lodash";7import { FormattedMessage, useIntl } from "react-intl";89import {10redux,11useActions,12useState,13useStore,14useTypedRedux,15} from "@cocalc/frontend/app-framework";16import {17Icon,18LabeledRow,19MarkdownInput,20TextInput,21} from "@cocalc/frontend/components";22import ShowError from "@cocalc/frontend/components/error";23import { course } from "@cocalc/frontend/i18n";24import { KUCALC_ON_PREMISES } from "@cocalc/util/db-schema/site-defaults";25import { contains_url } from "@cocalc/util/misc";26import { CourseActions } from "../actions";27import { CourseSettingsRecord, CourseStore } from "../store";28import ConfigurationCopying from "./configuration-copying";29import { CustomizeStudentProjectFunctionality } from "./customize-student-project-functionality";30import { DatastoreConfig } from "./datastore-config";31import { DisableStudentCollaboratorsPanel } from "./disable-collaborators";32import { EnvironmentVariablesConfig } from "./envvars-config";33import { Nbgrader } from "./nbgrader";34import { Parallel } from "./parallel";35import StudentPay from "./student-pay";36import { StudentProjectSoftwareEnvironment } from "./student-project-software-environment";37import { StudentProjectUpgrades } from "./upgrades";38import { COLORS } from "@cocalc/util/theme";3940interface Props {41name: string;42project_id: string;43settings: CourseSettingsRecord;44configuring_projects?: boolean;45}4647export function ConfigurationPanel({48name,49project_id,50settings,51configuring_projects,52}: Props) {53const actions = useActions<CourseActions>({ name });5455return (56<div57className="smc-vfill"58style={{59overflowY: "scroll",60}}61>62<Row>63<Col md={12} style={{ padding: "15px 15px 15px 0" }}>64<UpgradeConfiguration65name={name}66settings={settings}67configuring_projects={configuring_projects}68actions={actions}69/>70<br />71<TitleAndDescription72actions={actions}73settings={settings}74name={name}75/>76<br />77<EmailInvitation78actions={actions}79redux={redux}80project_id={project_id}81name={name}82/>83<br />84<Nbgrader name={name} />85</Col>86<Col md={12} style={{ padding: "15px" }}>87<CollaboratorPolicy settings={settings} actions={actions} />88<br />89<RestrictStudentProjects settings={settings} actions={actions} />90<br />91<ConfigureSoftwareEnvironment92actions={actions}93settings={settings}94project_id={project_id}95/>96<br />97<Parallel name={name} />98<NetworkFilesystem99actions={actions}100settings={settings}101project_id={project_id}102/>103<br />104<EnvVariables105actions={actions}106settings={settings}107project_id={project_id}108/>109<br />110<ConfigurationCopying111actions={actions}112settings={settings}113project_id={project_id}114/>115</Col>116</Row>117</div>118);119}120121export function UpgradeConfiguration({122name,123settings,124configuring_projects,125actions,126}) {127const is_commercial = useTypedRedux("customize", "is_commercial");128const kucalc = useTypedRedux("customize", "kucalc");129130function render_require_institute_pay() {131if (!is_commercial) return;132return (133<>134<StudentProjectUpgrades135name={name}136is_onprem={false}137is_commercial={is_commercial}138institute_pay={settings?.get("institute_pay")}139student_pay={settings?.get("student_pay")}140site_license_id={settings?.get("site_license_id")}141site_license_strategy={settings?.get("site_license_strategy")}142shared_project_id={settings?.get("shared_project_id")}143disabled={configuring_projects}144settings={settings}145actions={actions.configuration}146/>147<br />148</>149);150}151152/**153* OnPrem instances support licenses to be distributed to all student projects.154*/155function render_onprem_upgrade_projects() {156if (is_commercial || kucalc !== KUCALC_ON_PREMISES) {157return;158}159return (160<>161<StudentProjectUpgrades162name={name}163is_onprem={true}164is_commercial={false}165site_license_id={settings?.get("site_license_id")}166site_license_strategy={settings?.get("site_license_strategy")}167shared_project_id={settings?.get("shared_project_id")}168disabled={configuring_projects}169settings={settings}170actions={actions.configuration}171/>172<br />173</>174);175}176177return (178<Card179title={180<>181<Icon name="gears" />{" "}182<FormattedMessage183id="course.configuration.configure_upgrades.title"184defaultMessage={"Configure Upgrades"}185/>186</>187}188>189{is_commercial && <StudentPay actions={actions} settings={settings} />}190{render_require_institute_pay()}191{render_onprem_upgrade_projects()}192</Card>193);194}195196export function TitleAndDescription({ actions, settings, name }) {197const intl = useIntl();198if (settings == null) {199return <Spin />;200}201return (202<Card203title={204<>205<Icon name="header" />{" "}206{intl.formatMessage(course.title_and_description_label)}207</>208}209>210<LabeledRow label="Title">211<TextInput212text={settings.get("title") ?? ""}213on_change={(title) => actions.configuration.set_title(title)}214/>215</LabeledRow>216<LabeledRow label="Description">217<MarkdownInput218persist_id={name + "course-description"}219attach_to={name}220rows={6}221default_value={settings.get("description")}222on_save={(desc) => actions.configuration.set_description(desc)}223/>224</LabeledRow>225<hr />226<span style={{ color: COLORS.GRAY_M }}>227<FormattedMessage228id="course.configuration.title_and_description.info"229defaultMessage={`Set the course title and description here.230When you change the title or description,231the corresponding title and description of each student project will be updated.232The description is set to this description,233and the title is set to the student name followed by this title.234Use the description to provide additional information about the course,235e.g., a link to the main course website.`}236/>237</span>238</Card>239);240}241242export function EmailInvitation({ actions, redux, project_id, name }) {243const intl = useIntl();244const [error, setError] = useState<string>("");245const store = useStore<CourseStore>({ name });246247const check_email_body = debounce(248(value) => {249const allow_urls: boolean = redux250.getStore("projects")251.allow_urls_in_emails(project_id);252if (!allow_urls && contains_url(value)) {253setError(254intl.formatMessage({255id: "course.configuration.email_invitation.url_error",256defaultMessage:257"URLs in emails are not allowed for free trial projects. Please upgrade or delete the URL. This is an anti-spam measure.",258}),259);260} else {261setError("");262}263},264500,265{ leading: true, trailing: true },266);267268return (269<Card270title={271<>272<Icon name="envelope" />{" "}273{intl.formatMessage(course.email_invitation_label)}274</>275}276>277<div278style={{279border: "1px solid lightgrey",280padding: "10px",281borderRadius: "5px",282}}283>284<ShowError error={error} />285<MarkdownInput286persist_id={name + "email-invite-body"}287attach_to={name}288rows={6}289default_value={store.get_email_invite()}290on_save={(body) => actions.configuration.set_email_invite(body)}291save_disabled={!!error}292on_change={check_email_body}293on_cancel={() => setError("")}294/>295</div>296<hr />297<span style={{ color: COLORS.GRAY_M }}>298<FormattedMessage299id="course.configuration.email_invitation.info"300defaultMessage={`If you add a student to this course using their email address,301and they do not have a CoCalc account, then they will receive this email invitation.302Also, "{title}" will be replaced by the title of the course and "{name}" by your name.`}303description={`Email invitations for students in an online course. Do not change {name} and {title} since they are variables.`}304values={{305// the curly brackets are replaced by the variable with brackets, since we also use this for template vars.306title: "{title}",307name: "{name}",308}}309/>310</span>311</Card>312);313}314315export function CollaboratorPolicy({ settings, actions }) {316return (317<DisableStudentCollaboratorsPanel318checked={!!settings.get("allow_collabs")}319on_change={(val) => actions.configuration.set_allow_collabs(val)}320/>321);322}323324export function RestrictStudentProjects({ settings, actions }) {325const functionality =326settings.get("student_project_functionality")?.toJS() ?? {};327return (328<CustomizeStudentProjectFunctionality329functionality={functionality}330onChange={async (opts) =>331await actions.configuration.set_student_project_functionality(opts)332}333/>334);335}336337export function NetworkFilesystem({338settings,339actions,340project_id,341close,342}: {343settings;344actions;345project_id;346close?;347}) {348return (349<DatastoreConfig350actions={actions.configuration}351datastore={settings.get("datastore")}352project_id={project_id}353close={close}354/>355);356}357358export function EnvVariables({359settings,360actions,361project_id,362close,363}: {364settings;365actions;366project_id;367close?;368}) {369return (370<EnvironmentVariablesConfig371actions={actions.configuration}372envvars={settings.get("envvars")}373project_id={project_id}374close={close}375/>376);377}378379export function ConfigureSoftwareEnvironment({380actions,381settings,382project_id,383close,384}: {385actions;386settings;387project_id;388close?;389}) {390return (391<StudentProjectSoftwareEnvironment392actions={actions.configuration}393software_image={settings.get("custom_image")}394course_project_id={project_id}395inherit_compute_image={settings.get("inherit_compute_image")}396close={close}397/>398);399}400401402