typography overhaul, custom scrollbars, import/export, settings UI

This commit is contained in:
2026-02-16 14:56:36 +02:00
parent 8d26af0b0e
commit 98226775ed
9 changed files with 343 additions and 205 deletions

View File

@@ -1,60 +1,23 @@
import { useRef } from "react";
import { Download, Upload } from "lucide-react";
import { Upload } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useAppStore } from "@/stores/app-store";
import { useBoardStore } from "@/stores/board-store";
import { useToastStore } from "@/stores/toast-store";
import { saveBoard } from "@/lib/storage";
import {
exportBoardAsJson,
exportBoardAsCsv,
importBoardFromJson,
importFromTrelloJson,
} from "@/lib/import-export";
function downloadBlob(content: string, filename: string, mimeType: string) {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
export function ImportExportButtons() {
export function ImportButton() {
const fileInputRef = useRef<HTMLInputElement>(null);
const addToast = useToastStore((s) => s.addToast);
const board = useBoardStore((s) => s.board);
const refreshBoards = useAppStore((s) => s.refreshBoards);
const setView = useAppStore((s) => s.setView);
const addRecentBoard = useAppStore((s) => s.addRecentBoard);
const openBoard = useBoardStore((s) => s.openBoard);
function handleExportJson() {
if (!board) return;
const json = exportBoardAsJson(board);
const safeName = board.title.replace(/[^a-zA-Z0-9-_]/g, "_");
downloadBlob(json, `${safeName}.json`, "application/json");
addToast("Board exported as JSON", "success");
}
function handleExportCsv() {
if (!board) return;
const csv = exportBoardAsCsv(board);
const safeName = board.title.replace(/[^a-zA-Z0-9-_]/g, "_");
downloadBlob(csv, `${safeName}.csv`, "text/csv");
addToast("Board exported as CSV", "success");
}
function handleImportClick() {
fileInputRef.current?.click();
}
@@ -94,8 +57,7 @@ export function ImportExportButtons() {
}
return (
<div className="flex gap-2">
{/* Import button */}
<>
<Button variant="outline" size="sm" onClick={handleImportClick}>
<Upload className="size-4" />
Import
@@ -107,24 +69,6 @@ export function ImportExportButtons() {
onChange={handleFileSelected}
className="hidden"
/>
{/* Export dropdown */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" disabled={!board}>
<Download className="size-4" />
Export
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleExportJson}>
Export as JSON
</DropdownMenuItem>
<DropdownMenuItem onClick={handleExportCsv}>
Export as CSV
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</>
);
}