Skip to content

Commit

Permalink
feat: Fix bugs and implement remaining template-related features
Browse files Browse the repository at this point in the history
  • Loading branch information
daavidrgz committed Dec 24, 2024
1 parent 57c9ea4 commit 5e32ebb
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 72 deletions.
4 changes: 4 additions & 0 deletions crates/web/frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@ h4 {
p {
@apply text-xs;
}

.cm-editor {
outline: none !important;
}
5 changes: 1 addition & 4 deletions crates/web/frontend/src/app/page-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ export const applyTemplate = async (
jinjaContent: string,
silent = true,
): Promise<string> => {
let input = JSON.parse(inputContent);
if (inputContent.startsWith("[")) {
input = { data: input };
}
const input = { data: JSON.parse(inputContent) };
const result = nunjucks.renderString(jinjaContent, input);
!silent && notify.success(`Template applied to ${inputType.toUpperCase()}`);
return result;
Expand Down
59 changes: 38 additions & 21 deletions crates/web/frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const ShareLoader = ({

const Home = () => {
const [errorMessage, setErrorMessage] = useState<string>();
const [templateErrorMessage, setTemplateErrorMessage] = useState<string>();
const [warningMessages, setWarningMessages] = useState<string[]>([]);
const inputContent = useRef<string>("");
const queryContent = useRef<string>("");
Expand Down Expand Up @@ -99,6 +100,37 @@ const Home = () => {
const debounce = useDebounce();
const { gqWorker, lspWorker } = useWorker();


const updateTemplateOutput = useCallback(
async (inputContent: string, inputType: FileType, jinjaContent: string, silent = true) => {
if (!gqWorker || isApplying) return;
setIsTemplateApplying(true);
renderEditorLoadingCallback.current(loading("Applying template..."));
try {
const result = await applyTemplate(inputContent, inputType, jinjaContent, silent);
addNewTemplateCallback.current(jinjaContent);
updateRenderEditorCallback.current(new Data(result, FileType.UNKNOWN));
setTemplateErrorMessage(undefined);
} catch (err) {
setTemplateErrorMessage(err.message);
} finally {
setIsTemplateApplying(false);
renderEditorLoadingCallback.current(notLoading());
}
},
[gqWorker, isApplying],
);

const handleChangeTemplateContent = useCallback(
(content: string) => {
autoApply &&
debounce(debounceTime, () =>
updateTemplateOutput(outputContent.current, outputType.current, content, debounceTime < 500),
);
},
[autoApply, debounce, updateTemplateOutput, debounceTime],
);

const updateOutputData = useCallback(
async (
inputContent: string,
Expand All @@ -125,6 +157,7 @@ const Home = () => {
addNewQueryCallback.current(queryContent);
setErrorMessage(undefined);
updateOutputEditorCallback.current(result);
handleChangeTemplateContent(jinjaContent.current);
} catch (err) {
setErrorMessage(err.message);
setWarningMessages([]);
Expand All @@ -133,27 +166,7 @@ const Home = () => {
outputEditorLoadingCallback.current(notLoading());
}
},
[gqWorker, dataTabSize, isApplying],
);

const updateTemplateOutput = useCallback(
async (inputContent: string, inputType: FileType, jinjaContent: string, silent = true) => {
if (!gqWorker || isApplying) return;
setIsTemplateApplying(true);
renderEditorLoadingCallback.current(loading("Applying template..."));
try {
const result = await applyTemplate(inputContent, inputType, jinjaContent, silent);
addNewTemplateCallback.current(jinjaContent);
updateRenderEditorCallback.current(new Data(result, FileType.UNKNOWN));
} catch (err) {
// setErrorMessage(err.message);
// setWarningMessages([]);
} finally {
setIsTemplateApplying(false);
renderEditorLoadingCallback.current(notLoading());
}
},
[gqWorker, isApplying],
[gqWorker, dataTabSize, isApplying, handleChangeTemplateContent],
);

const handleClickExample = useCallback(
Expand All @@ -171,6 +184,7 @@ const Home = () => {
}, []);

const handleClickHistoryTemplate = useCallback((templateContent: string) => {
setTemplateOpen(true);
updateJinjaEditorCallback.current(new Data(templateContent, FileType.JINJA));
}, []);

Expand Down Expand Up @@ -335,6 +349,7 @@ const Home = () => {
title="Input"
defaultFileName="template"
fileTypes={[FileType.JINJA]}
onChangeContent={handleChangeTemplateContent}
onApply={() =>
updateTemplateOutput(
outputContent.current,
Expand All @@ -355,6 +370,8 @@ const Home = () => {
defaultFileName="output"
fileTypes={[FileType.UNKNOWN]}
updateCallback={updateRenderEditorCallback}
errorMessage={templateErrorMessage}
onDismissError={() => setTemplateErrorMessage(undefined)}
/>
</div>
</aside>
Expand Down
72 changes: 72 additions & 0 deletions crates/web/frontend/src/components/dump-block/dump-block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { cn } from "@/lib/utils";
import ActionButton from "../action-button/action-button";
import { Redo, Trash } from "lucide-react";
import { notify } from "@/lib/notify";

interface Props extends React.HTMLAttributes<HTMLDivElement> {
onDump: () => void;
onDumpMessage?: string;
lines: number;
onDelete?: () => void;
onDeleteMessage?: string;
}

export const DumpBlock = ({
onDump,
onDumpMessage,
lines,
onDelete,
onDeleteMessage,
children,
className,
...props
}: Props) => {
const handleDump = () => {
onDumpMessage && notify.success(onDumpMessage);
onDump();
};

const handleDelete = () => {
onDeleteMessage && notify.success(onDeleteMessage);
onDelete?.();
};

return (
<div className={cn("relative group flex justify-between", className)} {...props}>
{children}
<div
className={cn(
"max-w-0 transition-all",
lines > 2 ? "group-hover:max-w-10" : "group-hover:max-w-20 flex",
)}
>
<ActionButton
side={lines > 2 ? "right" : "bottom"}
containerClassName={cn(
"min-h-10 flex items-center justify-center border-l",
lines > 2 && onDelete ? "h-1/2 border-b" : "h-full",
)}
className="h-full w-10 border-0"
description="Dump content into the editor"
onClick={handleDump}
>
<Redo className="w-3 h-3" />
</ActionButton>
{onDelete && (
<ActionButton
side={lines > 2 ? "right" : "bottom"}
containerClassName={cn(
"min-h-10 flex items-center justify-center border-l",
lines > 2 ? "h-1/2" : "h-full",
)}
className="h-full w-10 border-0"
description="Delete from history"
onClick={handleDelete}
>
<Trash className="w-3 h-3" />
</ActionButton>
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@

&[data-visible="true"] {
opacity: 1;
pointer-events: all;
}

&[data-visible="false"] {
opacity: 0;
pointer-events: none;
}
}
63 changes: 18 additions & 45 deletions crates/web/frontend/src/components/history-tab/history-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from "@/services/history/history-service";
import { TabsContent } from "@radix-ui/react-tabs";
import { AnimatePresence, motion } from "framer-motion";
import { Redo, Search, Trash, X } from "lucide-react";
import { Code, Redo, Search, SquareDashed, Trash, X } from "lucide-react";
import { type MutableRefObject, useCallback, useEffect, useState } from "react";
import ActionButton from "../action-button/action-button";
import SimpleEditor from "../editor/simple-editor";
Expand All @@ -21,6 +21,7 @@ import { Input } from "../ui/input";
import { SidebarContent, SidebarDescription, SidebarHeader, SidebarTitle } from "../ui/sidebar";
import { Tabs, TabsList, TabsTrigger } from "../ui/tabs";
import { groupItems } from "./history-tab-utils";
import { DumpBlock } from "../dump-block/dump-block";

interface HistoryTabContentProps {
value: string;
Expand All @@ -44,7 +45,7 @@ const HistoryTabContent = ({
{items.length === 0 ? (
<div className="w-full mt-8 flex gap-2 items-center justify-center">
<Search className="w-3 h-3" />
<span className="text-xs">There is nothing here yet!</span>
<span className="text-xs">There is nothing here</span>
</div>
) : (
Object.entries(groupItems(items)).map((entry) => (
Expand All @@ -54,51 +55,20 @@ const HistoryTabContent = ({
</div>
<AnimatePresence mode="sync">
{entry[1].map((item) => (
<motion.div
// initial={{ left: "100%" }}
// animate={{ left: 0, transition: { ease: "easeOut", duration: 0.25 } }}
// exit={{ left: "110%", transition: { ease: "easeOut", duration: 0.25 } }}
<DumpBlock
key={item.timestamp}
className="relative border-b flex justify-between group"
className="border-b"
lines={countLines(item.content)}
onDump={() => onClickItem(item.content)}
onDelete={() => onDeleteItem(item.id)}
onDumpMessage="Query dumped into the editor"
onDeleteMessage="Query deleted from history"
>
<SimpleEditor
className="px-2 py-4 max-h-40 bg-background w-full text-nowrap"
content={item.content}
/>
<div
className={cn(
"max-w-0 transition-all",
countLines(item.content) > 2
? "group-hover:max-w-10"
: "group-hover:max-w-20 flex",
)}
>
<ActionButton
side={countLines(item.content) > 2 ? "right" : "bottom"}
containerClassName={cn(
"min-h-10 flex items-center justify-center border-l",
countLines(item.content) > 2 ? "h-1/2 border-b" : "h-full",
)}
className="h-full w-10 border-0"
description="Dump query into the editor"
onClick={() => onClickItem(item.content)}
>
<Redo className="w-3 h-3" />
</ActionButton>
<ActionButton
side={countLines(item.content) > 2 ? "right" : "bottom"}
containerClassName={cn(
"min-h-10 flex items-center justify-center border-l",
countLines(item.content) > 2 ? "h-1/2" : "h-full",
)}
className="h-full w-10 border-0"
description="Delete from history"
onClick={() => onDeleteItem(item.id)}
>
<Trash className="w-3 h-3" />
</ActionButton>
</div>
</motion.div>
</DumpBlock>
))}
</AnimatePresence>
</div>
Expand Down Expand Up @@ -258,17 +228,20 @@ const HistoryTab = ({
<TabsList className="flex">
<TabsTrigger
value="queries"
className="w-1/2 text-xs font-semibold py-4"
className="flex gap-2 items-center justify-center w-1/2 py-4"
variant="outline"
>
Queries
<Code className="w-3.5 h-3.5" />
<span className="text-xs">Queries</span>
</TabsTrigger>

<TabsTrigger
value="templates"
className="w-1/2 text-xs font-semibold py-4"
className="flex gap-2 items-center justify-center w-1/2 py-4"
variant="outline"
>
Templates
<SquareDashed className="w-3.5 h-3.5" />
<span className="text-xs">Templates</span>
</TabsTrigger>
</TabsList>
<HistoryTabContent
Expand Down
27 changes: 25 additions & 2 deletions crates/web/frontend/src/components/left-sidebar/left-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { useOnboarding } from "@/hooks/use-onboarding";
import { cn, isMac } from "@/lib/utils";
import type { Data } from "@/model/data";
import type FileType from "@/model/file-type";
import { BookMarked, History, Settings, Share } from "lucide-react";
import { BookMarked, History, Settings, Share, SquareDashed } from "lucide-react";
import { type MutableRefObject, useCallback, useEffect, useState } from "react";
import ActionButton from "../action-button/action-button";
import ExamplesTab from "../examples-tab/examples-tab";
import HistoryTab from "../history-tab/history-tab";
import SettingsTab from "../settings-tab/settings-tab";
import ShareTab from "../share-tab/share-tab";
import ThemeButton from "../theme-button/theme-button";
import { TemplatesTab } from "../templates-tab/templates-tab";

type Tab = "examples" | "share" | "history" | "settings";
type Tab = "examples" | "share" | "history" | "templates" | "settings";

interface Props {
open: boolean;
Expand Down Expand Up @@ -126,6 +127,24 @@ export const LeftSidebar = ({
)}
/>
</ActionButton>
<ActionButton
className={cn(
"w-full h-12",
selectedTab === "templates" && "bg-muted",
isOnboardingVisible && "border-y border-accent",
)}
side="right"
description="Show default templates"
variant="subtle"
onClick={() => handleClick("templates")}
>
<SquareDashed
className={cn(
"w-4 h-4 transition-opacity",
selectedTab !== "templates" && "opacity-80",
)}
/>
</ActionButton>

<OnboardingComponent className="absolute left-full top-24 -translate-y-1/4 z-20 w-80" />
</div>
Expand Down Expand Up @@ -171,6 +190,10 @@ export const LeftSidebar = ({
className={cn(selectedTab === "examples" ? "block" : "hidden")}
onClickExample={onClickExample}
/>
<TemplatesTab
className={cn(selectedTab === "templates" ? "block" : "hidden")}
onClickTemplate={onClickTemplate}
/>
<SettingsTab className={cn(selectedTab === "settings" ? "block" : "hidden")} />
</div>
</div>
Expand Down
Loading

0 comments on commit 5e32ebb

Please sign in to comment.