Files
model-platform/frontend/app/features/platform/CreateScriptModal.tsx
T
2026-08-27 15:43:29 +08:00

190 lines
6.1 KiB
TypeScript

import { type FormEvent } from "react";
import { BookOpen, Check, FileCode, Folder, Plus } from "lucide-react";
import type { ScriptType, Visibility } from "../../services/api";
import {
AppFormDialog,
dialogPrimaryButtonClass,
dialogSecondaryButtonClass,
} from "~/components/common/AppFormDialog";
import { Button } from "~/components/ui/button";
import {
destinationChipClass,
formFieldClass,
formHintClass,
formInputClass,
modalFormClass,
} from "./modalUi";
type NewScriptForm = {
name: string;
scriptType: ScriptType;
visibility: Visibility;
parentPath: string;
};
type CreateScriptModalProps = {
open: boolean;
creating: boolean;
form: NewScriptForm;
scripts: Array<{
script_type: ScriptType;
script_name: string;
relative_path?: string;
}>;
onFormChange: (form: NewScriptForm) => void;
onSubmit: (event: FormEvent) => void;
onClose: () => void;
};
export function CreateScriptModal({
open,
creating,
form,
scripts: _scripts,
onFormChange,
onSubmit,
onClose,
}: CreateScriptModalProps) {
return (
<AppFormDialog
open={open}
onOpenChange={(nextOpen) => {
if (!nextOpen) onClose();
}}
eyebrow="工作副本"
title="新建构建脚本"
size="panel"
>
<form className={modalFormClass} onSubmit={onSubmit}>
<div className={destinationChipClass}>
<Folder size={16} />
保存到:{form.parentPath || "个人根目录"}
</div>
<label className={formFieldClass}>
<span>脚本名称</span>
<input
className={formInputClass}
autoFocus
maxLength={255}
placeholder={form.scriptType === "notebook"
? "例如:数据探索"
: "例如:data_process"}
value={form.name}
onChange={(event) =>
onFormChange({
...form,
name: event.target.value,
})}
/>
<small className={formHintClass}>
系统会自动补充
{form.scriptType === "notebook" ? " .ipynb" : " .py"} 后缀
</small>
</label>
<fieldset className="mb-[17px] grid grid-cols-2 gap-[9px] border-0 p-0">
<legend className="mb-[7px] text-xs font-[650] text-[#3c4e62]">
脚本类型
</legend>
{(
[
{
type: "notebook" as const,
title: "Jupyter Notebook",
desc: "交互式数据探索与模型实验",
Icon: BookOpen,
iconTone: "bg-[#fff0ed] text-[#dd5a4c]",
size: 22,
},
{
type: "python" as const,
title: "Python 脚本",
desc: "批处理、训练和模型调用任务",
Icon: FileCode,
iconTone: "bg-[#e9f3ff] text-[#2a75c5]",
size: 23,
},
] as const
).map((item) => {
const selected = form.scriptType === item.type;
const TypeIcon = item.Icon;
return (
<button
key={item.type}
className={`relative flex min-h-[72px] cursor-pointer items-center gap-[9px] rounded-[7px] border p-2.5 text-left ${
selected
? "border-[#4d98df] bg-[#f4f9ff] shadow-[0_0_0_2px_rgb(49_133_216/7%)]"
: "border-[#dae2ea] bg-white hover:border-[#a9c6e1]"
}`}
type="button"
onClick={() => onFormChange({
...form,
scriptType: item.type,
})}
>
<span
className={`grid size-[37px] shrink-0 place-items-center rounded-lg ${item.iconTone}`}
>
<TypeIcon size={item.size} />
</span>
<span className="flex min-w-0 flex-col">
<strong className="text-[11px] text-[#34475b]">
{item.title}
</strong>
<small className="mt-[3px] text-[8px] leading-[1.35] text-[#919eac]">
{item.desc}
</small>
</span>
{selected && (
<span className="absolute top-1.5 right-1.5 grid size-[17px] place-items-center rounded-full bg-[#2e88dd] text-white">
<Check size={14} />
</span>
)}
</button>
);
})}
</fieldset>
<label className={formFieldClass}>
<span>可见范围</span>
<select
className={formInputClass}
value={form.visibility}
onChange={(event) =>
onFormChange({
...form,
visibility: event.target.value as Visibility,
})}
>
<option value="private">仅自己可见</option>
<option value="workspace">Workspace 成员可见</option>
<option value="public">公开</option>
</select>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
type="button"
onClick={onClose}
className={dialogSecondaryButtonClass}
>
取消
</Button>
<Button
variant="default"
size="sm"
type="submit"
disabled={creating || !form.name.trim()}
className={dialogPrimaryButtonClass}
>
{creating ? <span className="button-spinner" /> : <Plus size={16} />}
{creating ? "正在创建…" : "创建脚本"}
</Button>
</div>
</form>
</AppFormDialog>
);
}