-
Notifications
You must be signed in to change notification settings - Fork 11.2k
feat(frontend): classic skin default + frontend UI rework #4914
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 28 commits
013edda
8d3b1b2
16422d1
6426419
0f2de46
72b9561
cf3c545
85b2ba0
30e8435
e863f95
441c807
d26a7fd
1a5f32e
3b030e3
d44673a
a4e03d2
9cb7f4e
158be8c
4f14ad9
e433ad0
c46d4a8
6fae72c
acca5a2
112be92
49b5ad7
4c22a5e
78f3d19
91a0872
d8da426
bd73174
bbd2aae
eb7ee01
b5f19c0
bbf1096
e11d5fa
05a1fe0
78dd7a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| "use client"; | ||
|
|
||
| import { ArrowLeftIcon, CalendarClock, TriangleAlertIcon } from "lucide-react"; | ||
| import { useRouter, useSearchParams } from "next/navigation"; | ||
| import { useState } from "react"; | ||
|
|
||
| import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; | ||
| import { Button } from "@/components/ui/button"; | ||
| import { Input } from "@/components/ui/input"; | ||
| import { Textarea } from "@/components/ui/textarea"; | ||
| import { | ||
| ScheduledTaskScheduleInput, | ||
| type ScheduleValue, | ||
| } from "@/components/workspace/scheduled-task-schedule-input"; | ||
| import { useI18n } from "@/core/i18n/hooks"; | ||
| import { useCreateScheduledTask } from "@/core/scheduled-tasks/hooks"; | ||
|
|
||
| function ReuseThreadNotice({ | ||
| title, | ||
| description, | ||
| }: { | ||
| title: string; | ||
| description: string; | ||
| }) { | ||
| return ( | ||
| <Alert className="border-amber-500/50 bg-amber-500/10"> | ||
| <TriangleAlertIcon className="text-amber-600 dark:text-amber-400" /> | ||
| <AlertTitle>{title}</AlertTitle> | ||
| <AlertDescription>{description}</AlertDescription> | ||
| </Alert> | ||
| ); | ||
| } | ||
|
|
||
| export default function NewScheduledTaskPage() { | ||
| const { t } = useI18n(); | ||
| const router = useRouter(); | ||
| const searchParams = useSearchParams(); | ||
| const initialThreadId = searchParams.get("thread_id"); | ||
| const st = t.scheduledTasks; | ||
| const createTask = useCreateScheduledTask(); | ||
| const [contextMode, setContextMode] = useState< | ||
| "fresh_thread_per_run" | "reuse_thread" | ||
| >(initialThreadId ? "reuse_thread" : "fresh_thread_per_run"); | ||
| const [targetThreadId, setTargetThreadId] = useState(initialThreadId ?? ""); | ||
| const [title, setTitle] = useState(""); | ||
| const [prompt, setPrompt] = useState(""); | ||
| const [createSchedule, setCreateSchedule] = useState<ScheduleValue>({ | ||
| schedule_type: "cron", | ||
| schedule_spec: { cron: "0 9 * * *" }, | ||
| timezone: "", | ||
| }); | ||
|
|
||
| const handleCreate = () => { | ||
| createTask.mutate( | ||
| { | ||
| context_mode: contextMode, | ||
| thread_id: contextMode === "reuse_thread" ? targetThreadId : null, | ||
| title, | ||
| prompt, | ||
| schedule_type: createSchedule.schedule_type, | ||
| schedule_spec: createSchedule.schedule_spec, | ||
| timezone: createSchedule.timezone || "UTC", | ||
| }, | ||
| { onSuccess: () => router.push("/workspace/scheduled-tasks") }, | ||
| ); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="flex size-full flex-col"> | ||
| <header className="flex shrink-0 items-center justify-between gap-3 border-b px-4 py-3"> | ||
| <div className="flex items-center gap-3"> | ||
| <Button | ||
| variant="ghost" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Give the back button an accessible name This icon-only navigation button has no
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in d8da426. The icon-only back button now has a localized |
||
| size="icon-sm" | ||
| onClick={() => router.push("/workspace/scheduled-tasks")} | ||
| > | ||
| <ArrowLeftIcon className="h-4 w-4" /> | ||
| </Button> | ||
| <h1 className="text-sm font-semibold">{st.create.title}</h1> | ||
| </div> | ||
| </header> | ||
| <main className="flex flex-1 justify-center overflow-y-auto px-4 py-6"> | ||
| <div | ||
| className="my-auto w-full max-w-xl space-y-4" | ||
| data-testid="scheduled-task-create-form" | ||
| > | ||
| <div className="space-y-3 pb-2 text-center"> | ||
| <div className="bg-primary/10 mx-auto flex h-14 w-14 items-center justify-center rounded-full"> | ||
| <CalendarClock className="text-primary h-7 w-7" /> | ||
| </div> | ||
| <div className="space-y-1"> | ||
| <h2 className="text-xl font-semibold">{st.create.title}</h2> | ||
| <p className="text-muted-foreground text-sm">{st.description}</p> | ||
| </div> | ||
| </div> | ||
| <div className="flex flex-wrap gap-2"> | ||
| <Button | ||
| type="button" | ||
| variant={ | ||
| contextMode === "fresh_thread_per_run" ? "default" : "outline" | ||
| } | ||
| size="sm" | ||
| onClick={() => setContextMode("fresh_thread_per_run")} | ||
| > | ||
| {st.context.fresh} | ||
| </Button> | ||
| <Button | ||
| type="button" | ||
| variant={contextMode === "reuse_thread" ? "default" : "outline"} | ||
| size="sm" | ||
| onClick={() => setContextMode("reuse_thread")} | ||
| > | ||
| {st.context.reuse} | ||
| </Button> | ||
| </div> | ||
| {contextMode === "reuse_thread" && ( | ||
| <> | ||
| <Input | ||
| value={targetThreadId} | ||
| onChange={(event) => setTargetThreadId(event.target.value)} | ||
| placeholder={st.context.threadIdPlaceholder} | ||
| /> | ||
| <ReuseThreadNotice | ||
| title={st.context.reuseNoticeTitle} | ||
| description={st.context.reuseNoticeDescription} | ||
| /> | ||
| </> | ||
| )} | ||
| <Input | ||
| value={title} | ||
| onChange={(event) => setTitle(event.target.value)} | ||
| placeholder={st.create.taskTitle} | ||
| /> | ||
| <Textarea | ||
| rows={4} | ||
| value={prompt} | ||
| onChange={(event) => setPrompt(event.target.value)} | ||
| placeholder={st.create.prompt} | ||
| /> | ||
| <ScheduledTaskScheduleInput | ||
| initial={createSchedule} | ||
| onChange={setCreateSchedule} | ||
| /> | ||
| {createSchedule.schedule_type === "once" && | ||
| !createSchedule.schedule_spec.run_at && ( | ||
| <div className="text-muted-foreground text-sm"> | ||
| {st.create.invalidOnce} | ||
| </div> | ||
| )} | ||
| <div className="flex justify-end gap-2"> | ||
| <Button | ||
| variant="outline" | ||
| onClick={() => router.push("/workspace/scheduled-tasks")} | ||
| disabled={createTask.isPending} | ||
| > | ||
| {t.common.cancel} | ||
| </Button> | ||
| <Button | ||
| onClick={handleCreate} | ||
| disabled={ | ||
| !title || | ||
| !prompt || | ||
| (!createSchedule.schedule_spec.cron && | ||
| !createSchedule.schedule_spec.run_at) || | ||
| (contextMode === "reuse_thread" && !targetThreadId) || | ||
| createTask.isPending | ||
| } | ||
| > | ||
| {createTask.isPending ? t.common.loading : st.create.submit} | ||
| </Button> | ||
| </div> | ||
| </div> | ||
| </main> | ||
| </div> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: the inbound half of the thread_id round-trip is handled (
createHrefon the list page forwards?thread_id=, and this page seedsreuse_thread+ the target id), but every exit from this page hardcodes the unfiltered URL — this success callback, plus the Back button (line 58) and Cancel (line 127). A user arriving from a thread's "Scheduled tasks" link who creates the task, or just navigates back, lands on the global list and loses the thread context this page deliberately preserved. SinceinitialThreadIdis already in scope,router.push(initialThreadId ?/workspace/scheduled-tasks?thread_id=${encodeURIComponent(initialThreadId)}: "/workspace/scheduled-tasks")(or plainrouter.back()for Back/Cancel) would keep the filter symmetrical.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d8da426. Create success, Back, and Cancel now all return to
listHref, which keeps?thread_id=when the page was seeded from inbound query params.