Skip to content
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

"Publish" in Squiggle AI #3464

Merged
merged 7 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions apps/hub/src/app/ai/WorkflowViewer/PublishWorkflowButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useRouter } from "next/navigation";
import { FC, useState } from "react";

import { ClientWorkflow } from "@quri/squiggle-ai";
import { Button } from "@quri/ui";

import { NewModelFormBody, NewModelFormShape } from "@/app/new/model/NewModel";
import { SafeActionFormModal } from "@/components/ui/SafeActionFormModal";
import { createModelAction } from "@/models/actions/createModelAction";

type Props = {
workflow: Extract<ClientWorkflow, { status: "finished" }>;
};

const PublishWorkflowModal: FC<Props & { close: () => void }> = ({
workflow,
close,
}) => {
const router = useRouter();

const code = `/*
Generated by Squiggle AI. Workflow ID: ${workflow.id}
*/
${workflow.result.code}`;

return (
<SafeActionFormModal<NewModelFormShape, typeof createModelAction>
title="Publish Model"
action={createModelAction}
close={close}
defaultValues={{
// TODO - LLM-generated slug
isPrivate: false,
}}
formDataToInput={(data) => ({
code,
slug: data.slug ?? "",
groupSlug: data.group?.slug,
isPrivate: data.isPrivate,
})}
submitText="Save"
onSuccess={(data) => {
// Note: redirect in server action would be incompatible with https://github.com/TheEdoRan/next-safe-action/issues/303
// (and might a bad idea anyway, returning a url is more verbose but more flexible for reuse)
router.push(data.url);
}}
closeOnSuccess={false}
>
<NewModelFormBody />
</SafeActionFormModal>
);
};

export const PublishWorkflowButton: FC<Props> = ({ workflow }) => {
const [isOpen, setIsOpen] = useState(false);

return (
<>
<Button theme="primary" size="small" onClick={() => setIsOpen(true)}>
Save Model
</Button>
{isOpen && (
<PublishWorkflowModal
workflow={workflow}
close={() => setIsOpen(false)}
/>
)}
</>
);
};
4 changes: 3 additions & 1 deletion apps/hub/src/app/ai/WorkflowViewer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useAvailableHeight } from "@/lib/hooks/useAvailableHeight";
import { LogsView } from "../LogsView";
import { SquigglePlaygroundForWorkflow } from "../SquigglePlaygroundForWorkflow";
import { Header } from "./Header";
import { PublishWorkflowButton } from "./PublishWorkflowButton";
import { WorkflowSteps } from "./WorkflowSteps";

type WorkflowViewerProps<
Expand Down Expand Up @@ -56,7 +57,8 @@ const FinishedWorkflowViewer: FC<WorkflowViewerProps<"finished">> = ({
/>
)}
renderRight={() => (
<div className="flex gap-2">
<div className="flex items-center gap-2">
<PublishWorkflowButton workflow={workflow} />
<StyledTab.List>
<StyledTab name="Playground" />
<StyledTab name="Steps" />
Expand Down
61 changes: 34 additions & 27 deletions apps/hub/src/app/groups/[slug]/members/AddUserToGroupAction.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { MembershipRole } from "@prisma/client";
import { FC } from "react";

import { PlusIcon, SelectStringFormField } from "@quri/ui";
import {
DropdownMenuModalActionItem,
PlusIcon,
SelectStringFormField,
} from "@quri/ui";

import { SelectUser, SelectUserOption } from "@/components/SelectUser";
import { SafeActionModalAction } from "@/components/ui/SafeActionModalAction";
import { SafeActionFormModal } from "@/components/ui/SafeActionFormModal";
import { addUserToGroupAction } from "@/groups/actions/addUserToGroupAction";
import { GroupMemberDTO } from "@/groups/data/members";

Expand All @@ -17,33 +21,36 @@ type FormShape = { user: SelectUserOption; role: MembershipRole };

export const AddUserToGroupAction: FC<Props> = ({ groupSlug, append }) => {
return (
<SafeActionModalAction<FormShape, typeof addUserToGroupAction>
<DropdownMenuModalActionItem
title="Add"
icon={PlusIcon}
action={addUserToGroupAction}
onSuccess={(membership) => {
append(membership);
}}
defaultValues={{ role: "Member" }}
formDataToInput={(data) => ({
group: groupSlug,
username: data.user.slug,
role: data.role,
})}
submitText="Add"
modalTitle={`Add to group ${groupSlug}`}
>
{() => (
<div className="space-y-2">
<SelectUser<FormShape> label="User" name="user" />
<SelectStringFormField<FormShape, MembershipRole>
name="role"
label="Role"
options={["Member", "Admin"]}
required
/>
</div>
render={({ close }) => (
<SafeActionFormModal<FormShape, typeof addUserToGroupAction>
close={close}
action={addUserToGroupAction}
onSuccess={(membership) => {
append(membership);
}}
defaultValues={{ role: "Member" }}
formDataToInput={(data) => ({
group: groupSlug,
username: data.user.slug,
role: data.role,
})}
submitText="Add"
title={`Add to group ${groupSlug}`}
>
<div className="space-y-2">
<SelectUser<FormShape> label="User" name="user" />
<SelectStringFormField<FormShape, MembershipRole>
name="role"
label="Role"
options={["Member", "Admin"]}
required
/>
</div>
</SafeActionFormModal>
)}
</SafeActionModalAction>
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export const ModelSettingsButton: FC<{
}> = ({ model }) => {
return (
<Dropdown
render={({ close }) => (
render={() => (
<DropdownMenu>
<UpdateModelSlugAction model={model} close={close} />
<UpdateModelSlugAction model={model} />
<MoveModelAction model={model} />
<DeleteModelAction model={model} />
</DropdownMenu>
Expand Down
71 changes: 37 additions & 34 deletions apps/hub/src/app/models/[owner]/[slug]/MoveModelAction.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useRouter } from "next/navigation";
import { FC } from "react";

import { RightArrowIcon } from "@quri/ui";
import { DropdownMenuModalActionItem, RightArrowIcon } from "@quri/ui";

import { SelectOwner, SelectOwnerOption } from "@/components/SelectOwner";
import { SafeActionModalAction } from "@/components/ui/SafeActionModalAction";
import { SafeActionFormModal } from "@/components/ui/SafeActionFormModal";
import { modelRoute } from "@/lib/routes";
import { moveModelAction } from "@/models/actions/moveModelAction";
import { ModelCardDTO } from "@/models/data/cards";
Expand All @@ -21,42 +21,45 @@ export const MoveModelAction: FC<Props> = ({ model }) => {
const router = useRouter();

return (
<SafeActionModalAction<FormShape, typeof moveModelAction>
<DropdownMenuModalActionItem
title="Change Owner"
modalTitle={`Change owner for ${model.owner.slug}/${model.slug}`}
submitText="Save"
defaultValues={{
// __typename from fragment is string, while SelectOwner requires 'User' | 'Group' union,
// so we have to explicitly recast
owner: model.owner as SelectOwnerOption,
}}
action={moveModelAction}
formDataToInput={(data) => ({
oldOwner: model.owner.slug,
owner: { slug: data.owner.slug },
slug: model.slug,
})}
onSuccess={({ model: newModel }) => {
draftUtils.rename(
modelToDraftLocator(model),
modelToDraftLocator(newModel)
);
router.push(
modelRoute({ owner: newModel.owner.slug, slug: newModel.slug })
);
}}
icon={RightArrowIcon}
initialFocus="owner"
blockOnSuccess
>
{() => (
<div className="mb-4">
render={({ close }) => (
<SafeActionFormModal<FormShape, typeof moveModelAction>
close={close}
title={`Change owner for ${model.owner.slug}/${model.slug}`}
submitText="Save"
defaultValues={{
// __typename from fragment is string, while SelectOwner requires 'User' | 'Group' union,
// so we have to explicitly recast
owner: model.owner as SelectOwnerOption,
}}
action={moveModelAction}
formDataToInput={(data) => ({
oldOwner: model.owner.slug,
owner: { slug: data.owner.slug },
slug: model.slug,
})}
onSuccess={({ model: newModel }) => {
draftUtils.rename(
modelToDraftLocator(model),
modelToDraftLocator(newModel)
);
router.push(
modelRoute({ owner: newModel.owner.slug, slug: newModel.slug })
);
}}
closeOnSuccess={false}
initialFocus="owner"
>
<div className="mb-4">
Are you sure? All existing links to the model will break.
<div className="mb-4">
Are you sure? All existing links to the model will break.
</div>
<SelectOwner<FormShape> name="owner" label="New owner" myOnly />
</div>
<SelectOwner<FormShape> name="owner" label="New owner" myOnly />
</div>
</SafeActionFormModal>
)}
</SafeActionModalAction>
/>
);
};
67 changes: 35 additions & 32 deletions apps/hub/src/app/models/[owner]/[slug]/UpdateModelSlugAction.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useRouter } from "next/navigation";
import { FC } from "react";

import { EditIcon } from "@quri/ui";
import { DropdownMenuModalActionItem, EditIcon } from "@quri/ui";

import { SafeActionModalAction } from "@/components/ui/SafeActionModalAction";
import { SafeActionFormModal } from "@/components/ui/SafeActionFormModal";
import { SlugFormField } from "@/components/ui/SlugFormField";
import { modelRoute } from "@/lib/routes";
import { updateModelSlugAction } from "@/models/actions/updateModelSlugAction";
Expand All @@ -13,46 +13,49 @@ import { draftUtils, modelToDraftLocator } from "./SquiggleSnippetDraftDialog";

type Props = {
model: ModelCardDTO;
close(): void;
};

type FormShape = { slug: string };

export const UpdateModelSlugAction: FC<Props> = ({ model, close }) => {
export const UpdateModelSlugAction: FC<Props> = ({ model }) => {
const router = useRouter();

return (
<SafeActionModalAction<FormShape, typeof updateModelSlugAction>
<DropdownMenuModalActionItem
title="Rename"
icon={EditIcon}
action={updateModelSlugAction}
defaultValues={{ slug: model.slug }}
formDataToInput={(data) => ({
owner: model.owner.slug,
oldSlug: model.slug,
slug: data.slug,
})}
onSuccess={({ model: newModel }) => {
draftUtils.rename(
modelToDraftLocator(model),
modelToDraftLocator(newModel)
);
router.push(
modelRoute({ owner: newModel.owner.slug, slug: newModel.slug })
);
}}
submitText="Save"
modalTitle={`Rename ${model.owner.slug}/${model.slug}`}
initialFocus="slug"
>
{() => (
<div>
<div className="mb-4">
Are you sure? All existing links to the model will break.
render={({ close }) => (
<SafeActionFormModal<FormShape, typeof updateModelSlugAction>
close={close}
action={updateModelSlugAction}
defaultValues={{ slug: model.slug }}
formDataToInput={(data) => ({
owner: model.owner.slug,
oldSlug: model.slug,
slug: data.slug,
})}
onSuccess={({ model: newModel }) => {
draftUtils.rename(
modelToDraftLocator(model),
modelToDraftLocator(newModel)
);
router.push(
modelRoute({ owner: newModel.owner.slug, slug: newModel.slug })
);
}}
closeOnSuccess={false}
submitText="Save"
title={`Rename ${model.owner.slug}/${model.slug}`}
initialFocus="slug"
>
<div>
<div className="mb-4">
Are you sure? All existing links to the model will break.
</div>
<SlugFormField<FormShape> name="slug" label="New slug" />
</div>
<SlugFormField<FormShape> name="slug" label="New slug" />
</div>
</SafeActionFormModal>
)}
</SafeActionModalAction>
/>
);
};
4 changes: 2 additions & 2 deletions apps/hub/src/app/new/group/NewGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export const NewGroup: FC = () => {
/>
</div>
<Button
onClick={onSubmit}
disabled={!form.formState.isValid || inFlight}
type="submit"
theme="primary"
disabled={!form.formState.isValid || inFlight}
>
Create
</Button>
Expand Down
Loading
Loading