98 lines
3.1 KiB
TypeScript
98 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
|
|
type ApiResponse<T> = {
|
|
data: T;
|
|
meta: { timestamp: string; version: "v1" };
|
|
error: null | { message: string; code?: string };
|
|
};
|
|
|
|
type LinkTokenData = { linkToken?: string };
|
|
type ExportData = { status?: string; url?: string; csv?: string };
|
|
|
|
async function postJson<T>(path: string) {
|
|
const res = await fetch(path, {
|
|
method: "POST"
|
|
});
|
|
if (!res.ok) {
|
|
throw new Error("Request failed");
|
|
}
|
|
const payload = (await res.json()) as ApiResponse<T>;
|
|
return payload.data;
|
|
}
|
|
|
|
async function getJson<T>(path: string) {
|
|
const res = await fetch(path);
|
|
if (!res.ok) {
|
|
throw new Error("Request failed");
|
|
}
|
|
const payload = (await res.json()) as ApiResponse<T>;
|
|
return payload.data;
|
|
}
|
|
|
|
export function HeroActions() {
|
|
const [status, setStatus] = useState<string>("");
|
|
|
|
const onStart = async () => {
|
|
setStatus("Requesting a secure link token...");
|
|
try {
|
|
const data = await postJson<LinkTokenData>("/api/accounts/link");
|
|
if (data.linkToken) {
|
|
setStatus(`Link token ready: ${data.linkToken}`);
|
|
} else {
|
|
setStatus("Link token requested.");
|
|
}
|
|
} catch {
|
|
setStatus("Unable to request link token.");
|
|
}
|
|
};
|
|
|
|
const onViewExport = async () => {
|
|
setStatus("Preparing export sample...");
|
|
try {
|
|
const userId = localStorage.getItem("ledgerone_user_id");
|
|
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : "";
|
|
const data = await getJson<ExportData>(`/api/exports/csv${query}`);
|
|
if (data.csv) {
|
|
const blob = new Blob([data.csv], { type: "text/csv" });
|
|
const url = URL.createObjectURL(blob);
|
|
window.open(url, "_blank", "noopener,noreferrer");
|
|
setStatus("Export sample opened.");
|
|
} else {
|
|
setStatus("Export sample ready.");
|
|
}
|
|
} catch {
|
|
setStatus("Unable to fetch export sample.");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-3">
|
|
<div className="flex flex-wrap gap-4">
|
|
<button
|
|
type="button"
|
|
onClick={onStart}
|
|
className="btn-primary inline-flex items-center gap-2 px-6 py-3 transition-all hover:-translate-y-0.5 active:translate-y-0"
|
|
>
|
|
<svg className="h-4 w-4 text-white/95" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
Start a private ledger
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={onViewExport}
|
|
className="btn-secondary inline-flex items-center gap-2 px-6 py-3"
|
|
>
|
|
<svg className="h-4 w-4 text-muted-foreground" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
<path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
|
</svg>
|
|
View export sample
|
|
</button>
|
|
</div>
|
|
{status ? <p className="text-xs text-muted-foreground">{status}</p> : null}
|
|
</div>
|
|
);
|
|
}
|