Skip to content

Commit

Permalink
Auto import if id in URL param
Browse files Browse the repository at this point in the history
remove preview in card screen and instead go to import flow
  • Loading branch information
timothycarambat committed Nov 9, 2024
1 parent 890fb29 commit 3f04c71
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 221 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { isMobile } from "react-device-detect";
import { useState } from "react";
import { useEffect, useState } from "react";
import Sidebar from "@/components/SettingsSidebar";
import Introduction from "./Introduction";
import PullAndReview from "./PullAndReview";
import Completed from "./Completed";
import useQuery from "@/hooks/useQuery";

const CommunityHubImportItemSteps = {
itemId: {
Expand Down Expand Up @@ -44,11 +45,22 @@ const CommunityHubImportItemSteps = {
};

export function CommunityHubImportItemLayout({ setStep, children }) {
const query = useQuery();
const [settings, setSettings] = useState({
itemId: null,
item: null,
});

useEffect(() => {
function autoForward() {
if (query.get("id")) {
setSettings({ itemId: query.get("id") });
setStep(CommunityHubImportItemSteps.itemId.next());
}
}
autoForward();
}, []);

return (
<div
id="community-hub-import-item-container"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from "react";
import { Sparkle, Toolbox } from "@phosphor-icons/react";
import { Toolbox } from "@phosphor-icons/react";
import { isMobile } from "react-device-detect";
import CommunityHubImportItemSteps, {
CommunityHubImportItemLayout,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import paths from "@/utils/paths";
import { Link } from "react-router-dom";

export default function GenericHubCard({ item }) {
return (
<div
Expand All @@ -7,14 +10,12 @@ export default function GenericHubCard({ item }) {
<p className="text-white text-sm font-medium">{item.name}</p>
<p className="text-white/60 text-xs mt-1">{item.description}</p>
<div className="flex justify-end mt-2">
<button
<Link
className="text-primary-button hover:text-primary-button/80 text-xs"
onClick={() => {
/* TODO: Add import action */
}}
to={paths.communityHub.importItem(item.importId)}
>
Import →
</button>
</Link>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import ModalWrapper from "@/components/ModalWrapper";
import { useModal } from "@/hooks/useModal";
import Workspace from "@/models/workspace";
import { X } from "@phosphor-icons/react";
import truncate from "truncate";
import { useState, useEffect } from "react";
import showToast from "@/utils/toast";
import System from "@/models/system";
import { Link } from "react-router-dom";
import paths from "@/utils/paths";

export default function SlashCommandHubCard({ item }) {
const { isOpen, openModal, closeModal } = useModal();

return (
<>
<div
Expand All @@ -27,105 +20,14 @@ export default function SlashCommandHubCard({ item }) {
</p>
</div>
<div className="flex justify-end mt-2">
<button
<Link
className="text-primary-button hover:text-primary-button/80 text-xs"
onClick={openModal}
to={paths.communityHub.importItem(item.importId)}
>
Import →
</button>
</Link>
</div>
</div>
{isOpen && (
<ImportSlashCommandModal
item={item}
isOpen={isOpen}
closeModal={closeModal}
/>
)}
</>
);
}

function ImportSlashCommandModal({ item, isOpen, closeModal }) {
const [loading, setLoading] = useState(false);

const importSlashCommand = async (e) => {
e.preventDefault();
setLoading(true);
try {
const { preset, error } = await System.createSlashCommandPreset({
command: item.command,
prompt: item.prompt,
description: item.description,
});
if (error) throw new Error(error);
showToast(
`Slash command ${preset.command} imported successfully!`,
"success"
);
closeModal();
} catch (e) {
console.error(e);
showToast("Failed to import slash command.", "error");
} finally {
setLoading(false);
}
};

return (
<ModalWrapper isOpen={isOpen} closeModal={closeModal}>
<div className="relative w-full max-w-2xl max-h-full">
<div className="relative bg-main-gradient rounded-lg shadow py-4">
<div className="flex items-start justify-between p-4 rounded-t border-gray-500/50">
<div className="flex flex-col gap-2">
<div className="flex items-center gap-x-2 w-full justify-between">
<h3 className="text-xl font-semibold text-white">
Import "{item.name}" slash command
</h3>
<button
onClick={closeModal}
className="text-white/60 hover:text-white"
>
<X className="text-lg" />
</button>
</div>
<p className="text-white/60 text-sm">
Importing a slash command will make it available during chatting
by simply invoking it with{" "}
<code className="font-mono bg-zinc-900 px-1 py-0.5 rounded-md text-sm">
{item.command}
</code>{" "}
like you would any other command.
</p>
</div>
</div>

<div className="flex flex-col gap-y-2 p-4 mt-2">
<div className="w-full text-white text-md gap-x-2 flex items-center">
<p className="text-white/60 w-fit font-mono bg-zinc-900 px-2 py-1 rounded-md text-sm whitespace-pre-line">
{item.command}
</p>
</div>

<div className="w-full text-white text-md flex flex-col gap-y-2">
<p className="text-white/60 font-mono bg-zinc-900 p-4 rounded-md text-sm whitespace-pre-line max-h-[calc(200px)] overflow-y-auto">
{item.prompt}
</p>
</div>
</div>

<div className="flex w-full justify-center px-4">
<button
type="button"
onClick={importSlashCommand}
disabled={loading}
className="w-full text-center border border-slate-200 px-4 py-2 rounded-lg text-white text-sm hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
>
{loading ? "Importing..." : "Import slash command"}
</button>
</div>
</div>
</div>
</ModalWrapper>
);
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import ModalWrapper from "@/components/ModalWrapper";
import { useModal } from "@/hooks/useModal";
import Workspace from "@/models/workspace";
import { X } from "@phosphor-icons/react";
import truncate from "truncate";
import { useState, useEffect } from "react";
import showToast from "@/utils/toast";
import paths from "@/utils/paths";
import { Link } from "react-router-dom";

export default function SystemPromptHubCard({ item }) {
const { isOpen, openModal, closeModal } = useModal();

return (
<>
<div
Expand All @@ -23,112 +17,14 @@ export default function SystemPromptHubCard({ item }) {
</p>
</div>
<div className="flex justify-end mt-2">
<button
<Link
className="text-primary-button hover:text-primary-button/80 text-xs"
onClick={openModal}
to={paths.communityHub.importItem(item.importId)}
>
Import →
</button>
</Link>
</div>
</div>
{isOpen && (
<ImportSystemPromptModal
item={item}
isOpen={isOpen}
closeModal={closeModal}
/>
)}
</>
);
}

function ImportSystemPromptModal({ item, isOpen, closeModal }) {
const [workspaces, setWorkspaces] = useState([]);

useEffect(() => {
async function getWorkspaces() {
const workspaces = await Workspace.all();
setWorkspaces(workspaces);
}
getWorkspaces();
}, []);

async function handleSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
const destinationWorkspaceSlug = formData.get("destinationWorkspaceSlug");

showToast("Importing system prompt...", "info");
await Workspace.update(destinationWorkspaceSlug, {
openAiPrompt: item.prompt,
});
showToast("System prompt applied to workspace.", "success", {
clear: true,
});
closeModal();
}

return (
<ModalWrapper isOpen={isOpen} closeModal={closeModal}>
<div className="relative w-full max-w-2xl max-h-full">
<div className="relative bg-main-gradient rounded-lg shadow py-4">
<div className="flex items-start justify-between p-4 rounded-t border-gray-500/50">
<div className="flex flex-col gap-2">
<div className="flex items-center gap-x-2 w-full justify-between">
<h3 className="text-xl font-semibold text-white">
Import "{item.name}" to a workspace
</h3>
<button
onClick={closeModal}
className="text-white/60 hover:text-white"
>
<X className="text-lg" />
</button>
</div>
<p className="text-white/60 text-sm">
Importing a system prompt will overwrite your workspace's
current prompt. Simply select the workspace you want to import
the prompt to and click import.
</p>
</div>
</div>

<div className="w-full text-white text-md flex flex-col gap-y-2 p-4 max-h-[calc(300px)] overflow-y-auto my-2">
<p className="text-white/60 font-mono bg-zinc-900 px-2 py-1 rounded-md text-sm whitespace-pre-line">
{item.prompt}
</p>
</div>

<form
onSubmit={handleSubmit}
className="flex p-4 justify-between items-end"
>
<div className="flex flex-col w-60">
<label className="text-white text-sm font-semibold block mb-3">
Destination Workspace
</label>
<select
name="destinationWorkspaceSlug"
required={true}
className="bg-zinc-900 border-gray-500 text-white text-sm rounded-lg block w-full p-2.5"
>
<optgroup label="Available workspaces">
{workspaces.map((workspace) => {
return (
<option key={workspace.id} value={workspace.slug}>
{workspace.name}
</option>
);
})}
</optgroup>
</select>
</div>
<button className="h-fit border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800">
Import into workspace
</button>
</form>
</div>
</div>
</ModalWrapper>
);
}
4 changes: 2 additions & 2 deletions frontend/src/utils/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ export default {
authentication: () => {
return `/settings/community-hub/authentication`;
},
importItem: () => {
return `/settings/community-hub/import-item`;
importItem: (importItemId) => {
return `/settings/community-hub/import-item${importItemId ? `?id=${importItemId}` : ""}`;
},
profile: function (username) {
if (username) return `${this.website()}/u/${username}`;
Expand Down
12 changes: 12 additions & 0 deletions server/models/slashCommandsPresets.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ const SlashCommandPresets = {
// Command + userId must be unique combination.
create: async function (userId = null, presetData = {}) {
try {
const existingPreset = await this.get({
userId: userId ? Number(userId) : null,
command: String(presetData.command),
});

if (existingPreset) {
console.log(
"SlashCommandPresets.create - preset already exists - will not create"
);
return existingPreset;
}

const preset = await prisma.slash_command_presets.create({
data: {
...presetData,
Expand Down

0 comments on commit 3f04c71

Please sign in to comment.