mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(web): add tool settings UI for all core image tools
Adds Zustand file store, useToolProcessor hook for upload/process/download flow, and 7 settings components: resize (with social media presets), crop (with aspect ratio presets), rotate/flip, convert, compress (quality + target size modes), strip-metadata, and color adjustments (brightness, contrast, saturation, channels, effects). Updates tool-page to render the appropriate settings panel based on toolId.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { useCallback } from "react";
|
||||
import { useFileStore } from "@/stores/file-store";
|
||||
|
||||
function getToken(): string {
|
||||
return localStorage.getItem("stirling-token") || "";
|
||||
}
|
||||
|
||||
interface ProcessResult {
|
||||
jobId: string;
|
||||
downloadUrl: string;
|
||||
originalSize: number;
|
||||
processedSize: number;
|
||||
}
|
||||
|
||||
export function useToolProcessor(toolId: string) {
|
||||
const {
|
||||
processing,
|
||||
error,
|
||||
processedUrl,
|
||||
originalSize,
|
||||
processedSize,
|
||||
setProcessing,
|
||||
setError,
|
||||
setProcessedUrl,
|
||||
setSizes,
|
||||
setJobId,
|
||||
} = useFileStore();
|
||||
|
||||
const processFiles = useCallback(
|
||||
async (files: File[], settings: Record<string, unknown>) => {
|
||||
if (files.length === 0) {
|
||||
setError("No files selected");
|
||||
return;
|
||||
}
|
||||
|
||||
setProcessing(true);
|
||||
setError(null);
|
||||
setProcessedUrl(null);
|
||||
|
||||
try {
|
||||
// Build multipart form with the file and settings
|
||||
const formData = new FormData();
|
||||
formData.append("file", files[0]);
|
||||
formData.append("settings", JSON.stringify(settings));
|
||||
|
||||
const res = await fetch(`/api/v1/tools/${toolId}`, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${getToken()}` },
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
throw new Error(
|
||||
body.error || body.details || `Processing failed: ${res.status}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result: ProcessResult = await res.json();
|
||||
|
||||
setJobId(result.jobId);
|
||||
setProcessedUrl(result.downloadUrl);
|
||||
setSizes(result.originalSize, result.processedSize);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Processing failed");
|
||||
} finally {
|
||||
setProcessing(false);
|
||||
}
|
||||
},
|
||||
[toolId, setProcessing, setError, setProcessedUrl, setSizes, setJobId],
|
||||
);
|
||||
|
||||
return {
|
||||
processFiles,
|
||||
processing,
|
||||
error,
|
||||
downloadUrl: processedUrl,
|
||||
originalSize,
|
||||
processedSize,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user