Skip to content

Commit

Permalink
Little improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinDCarlson committed Feb 8, 2025
1 parent f6fc7b3 commit 73ba9e3
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 53 deletions.
23 changes: 17 additions & 6 deletions packages/frontend/src/components/json_import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import { createSignal } from "solid-js";
import type { Document } from "../api";
import "./json_import.css";

interface Props<T extends string> {
interface JsonImportProps<T extends string> {
onImport: (data: Document<T>) => void;
validate?: (data: Document<T>) => boolean | string;
}

export const JsonImport = <T extends string>(props: Props<T>) => {
export const JsonImport = <T extends string>(props: JsonImportProps<T>) => {
const [error, setError] = createSignal<string | null>(null);
const [pasteValue, setPasteValue] = createSignal("");

const handleError = (e: unknown) => {
setError(e instanceof Error ? e.message : "Unknown error occurred");
};

const validateAndImport = (jsonString: string) => {
try {
// Parse JSON
Expand All @@ -30,7 +34,7 @@ export const JsonImport = <T extends string>(props: Props<T>) => {
props.onImport(data);
setPasteValue(""); // Clear paste area after successful import
} catch (e) {
setError(e instanceof Error ? e.message : "Invalid JSON format");
handleError(e);
}
};

Expand All @@ -43,17 +47,22 @@ export const JsonImport = <T extends string>(props: Props<T>) => {
const file = input.files[0];

// Validate file type
if (!file?.type || !file?.name.endsWith(".json")) {
if (!(file?.type === "application/json") && !file?.name.endsWith(".json")) {
throw new Error("Please upload a JSON file");
}

const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
if (file.size > MAX_FILE_SIZE) {
throw new Error("File size exceeds 5MB limit");
}

const text = await file?.text();
validateAndImport(text);

// Reset file input
input.value = "";
} catch (e) {
setError(e instanceof Error ? e.message : "Error reading file");
handleError(e);
}
};

Expand Down Expand Up @@ -88,7 +97,9 @@ export const JsonImport = <T extends string>(props: Props<T>) => {
onPaste={handleInput}
placeholder="Paste your JSON here..."
/>
<button onClick={handlePaste}>Import Pasted JSON</button>
<button onClick={handlePaste} aria-label="Import JSON">
Import Pasted JSON
</button>
</div>

{/* Error display */}
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/diagram/diagram_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ export function DiagramMenuItems(props: {

//Can this be less repetitive?
const onDownloadJSON = (diagram: DiagramDocument) => {
downloadJson({ data: JSON.stringify(diagram), filename: `${diagram.name}.json` });
downloadJson(JSON.stringify(diagram), `${diagram.name}.json`);
};
const onCopy = (diagram: DiagramDocument) => {
copyToClipboard({ data: JSON.stringify(diagram) });
copyToClipboard(JSON.stringify(diagram));
};
return (
<>
Expand Down
4 changes: 2 additions & 2 deletions packages/frontend/src/model/model_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ export function ModelMenuItems(props: {
navigate(`/model/${newRef}`);
};
const onDownloadJSON = (model: ModelDocument) => {
downloadJson({ data: JSON.stringify(model), filename: `${model.name}.json` });
downloadJson(JSON.stringify(model), `${model.name}.json`);
};
const onCopy = (model: ModelDocument) => {
copyToClipboard({ data: JSON.stringify(model) });
copyToClipboard(JSON.stringify(model));
};

return (
Expand Down
25 changes: 16 additions & 9 deletions packages/frontend/src/page/import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,22 @@ export function Import(props: { onComplete?: () => void }) {

switch (data.type) {
case "model": {
const newRef = await createModel(api, data as ModelDocument);
navigate(`/model/${newRef}`);
const newRef = await createModel(api, data);
try {
navigate(`/model/${newRef}`);
} catch (e) {
throw new Error(`Failed to navigate to new ${data.type}: ${e}`);
}
break;
}

case "diagram": {
const newRef = await createDiagramFromDocument(api, data as DiagramDocument);
navigate(`/diagram/${newRef}`);
const newRef = await createDiagramFromDocument(api, data);
try {
navigate(`/diagram/${newRef}`);
} catch (e) {
throw new Error(`Failed to navigate to new ${data.type}: ${e}`);
}
break;
}

Expand All @@ -42,16 +50,15 @@ export function Import(props: { onComplete?: () => void }) {
// Placeholder, not doing more than typechecking does for now but
// will eventually validate against json schema
const validateJson = (data: Document<string>) => {
invariant(
isImportableDocument(data),
"Analysis and other document types cannot be imported at this time.",
);
if (!isImportableDocument(data)) {
return "Analysis and other document types cannot be imported at this time.";
}
return true;
};

return (
<div>
<JsonImport onImport={handleImport} validate={validateJson} />
<JsonImport<"model" | "diagram"> onImport={handleImport} validate={validateJson} />
</div>
);
}
1 change: 0 additions & 1 deletion packages/frontend/src/page/menubar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ export function AppMenu(props: {
<SettingsMenuItem />
<LogOutMenuItem />
</Show>

</HamburgerMenu>
<Dialog open={loginOpen()} onOpenChange={setLoginOpen} title="Log in">
<Login onComplete={() => setLoginOpen(false)} />
Expand Down
42 changes: 9 additions & 33 deletions packages/frontend/src/util/json_export.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,19 @@
// JsonExport.tsx
import { createSignal } from "solid-js";

interface ExportProps {
data: string;
filename?: string;
}
export function downloadJson(data: string, filename = "export.json") {
const blob = new Blob([data], { type: "application/json" });
const url = URL.createObjectURL(blob);

// Download as file
export const downloadJson = (props: ExportProps) => {
try {
const jsonString = props.data;
const blob = new Blob([jsonString], { type: "application/json" });
const url = URL.createObjectURL(blob);

const link = document.createElement("a");
link.href = url;
link.download = props.filename || "export.json";
document.body.appendChild(link);
link.download = filename;
link.click();

// Cleanup
document.body.removeChild(link);
} finally {
URL.revokeObjectURL(url);
} catch (error) {
console.error("Error downloading JSON:", error);
}
};
}

export const copyToClipboard = async (props: ExportProps) => {
const [copyStatus, setCopyStatus] = createSignal<"idle" | "copied" | "error">("idle");
try {
const jsonString = props.data;
await navigator.clipboard.writeText(jsonString);
setCopyStatus("copied");
setTimeout(() => setCopyStatus("idle"), 2000);
} catch (error) {
console.error("Error copying to clipboard:", error);
setCopyStatus("error");
setTimeout(() => setCopyStatus("idle"), 2000);
}
return copyStatus;
};
export async function copyToClipboard(data: string): Promise<void> {
await navigator.clipboard.writeText(data);
}

0 comments on commit 73ba9e3

Please sign in to comment.