Add Sheets-first mirror control
This commit is contained in:
parent
10d386bc7d
commit
fbd1c2cc4a
6
app/api/google/data-system-mode/route.ts
Normal file
6
app/api/google/data-system-mode/route.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { NextRequest } from "next/server";
|
||||
import { proxyRequest } from "@/lib/backend";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
return proxyRequest(req, "google/data-system-mode");
|
||||
}
|
||||
@ -24,6 +24,23 @@ type TellerEnrollment = {
|
||||
};
|
||||
};
|
||||
|
||||
type GoogleStatus = {
|
||||
connected: boolean;
|
||||
googleEmail?: string;
|
||||
driveMirror?: {
|
||||
enabled: boolean;
|
||||
status: string;
|
||||
url?: string | null;
|
||||
lastSyncedAt?: string | null;
|
||||
};
|
||||
dataSystem?: {
|
||||
mode: string;
|
||||
operationalSystemOfRecord: string;
|
||||
userOwnedMirror: boolean;
|
||||
note: string;
|
||||
};
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
TellerConnect?: {
|
||||
@ -53,6 +70,7 @@ export default function ConnectPage() {
|
||||
const [manualType, setManualType] = useState("checking");
|
||||
const [accounts, setAccounts] = useState<Account[]>([]);
|
||||
const [tellerReady, setTellerReady] = useState(false);
|
||||
const [googleStatus, setGoogleStatus] = useState<GoogleStatus | null>(null);
|
||||
|
||||
const createLinkToken = useCallback(async () => {
|
||||
setStatus("Requesting Plaid link token...");
|
||||
@ -84,6 +102,13 @@ export default function ConnectPage() {
|
||||
setAccounts(payload.data?.accounts ?? payload.data ?? []);
|
||||
}, []);
|
||||
|
||||
const loadGoogleStatus = useCallback(async () => {
|
||||
const res = await fetch("/api/google/status");
|
||||
if (!res.ok) return;
|
||||
const payload = await res.json();
|
||||
setGoogleStatus(payload.data ?? payload);
|
||||
}, []);
|
||||
|
||||
const loadTellerScript = useCallback(() => {
|
||||
if (typeof window === "undefined") return Promise.resolve(false);
|
||||
if (window.TellerConnect) {
|
||||
@ -115,8 +140,9 @@ export default function ConnectPage() {
|
||||
useEffect(() => {
|
||||
createLinkToken();
|
||||
loadAccounts();
|
||||
loadGoogleStatus();
|
||||
loadTellerScript();
|
||||
}, [createLinkToken, loadAccounts, loadTellerScript]);
|
||||
}, [createLinkToken, loadAccounts, loadGoogleStatus, loadTellerScript]);
|
||||
|
||||
const onSuccess = useCallback(
|
||||
async (publicToken: string | null) => {
|
||||
@ -301,6 +327,22 @@ export default function ConnectPage() {
|
||||
await loadAccounts();
|
||||
};
|
||||
|
||||
const enableSheetsMirror = async () => {
|
||||
setStatus("Enabling Sheets-first mirror mode...");
|
||||
const res = await fetch("/api/google/data-system-mode", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ mode: "google_sheets_mirror" })
|
||||
});
|
||||
const payload = await res.json();
|
||||
if (!res.ok || payload.error) {
|
||||
setStatus(payload.error?.message ?? "Unable to enable Sheets-first mirror mode.");
|
||||
return;
|
||||
}
|
||||
setStatus("Sheets-first mirror mode enabled.");
|
||||
await loadGoogleStatus();
|
||||
};
|
||||
|
||||
return (
|
||||
<AppShell
|
||||
title="Connect a bank"
|
||||
@ -447,6 +489,34 @@ export default function ConnectPage() {
|
||||
Free includes two connections. Pro supports ten active accounts, and Elite supports unlimited accounts.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="glass-panel mt-6 p-6 rounded-2xl shadow-sm">
|
||||
<h2 className="text-lg font-bold text-foreground">Google Sheets ownership</h2>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Keep a user-owned Google Sheets mirror synced from LedgerOne transaction changes.
|
||||
</p>
|
||||
<div className="mt-4 rounded-xl border border-border bg-secondary/30 px-4 py-3 text-sm">
|
||||
<p className="font-semibold text-foreground">
|
||||
{googleStatus?.connected ? `Connected: ${googleStatus.googleEmail}` : "Google is not connected"}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
Mode: {googleStatus?.dataSystem?.mode ?? "backend_db"} · Mirror: {googleStatus?.driveMirror?.status ?? "not_started"}
|
||||
</p>
|
||||
{googleStatus?.driveMirror?.url ? (
|
||||
<a className="mt-2 inline-block text-xs font-semibold text-primary" href={googleStatus.driveMirror.url} target="_blank" rel="noreferrer">
|
||||
Open Google Sheet
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-4 rounded-full border border-border bg-background px-5 py-2 text-sm font-semibold text-foreground hover:bg-secondary transition-colors disabled:opacity-50"
|
||||
onClick={enableSheetsMirror}
|
||||
disabled={!googleStatus?.connected || googleStatus?.dataSystem?.mode === "google_sheets_mirror"}
|
||||
>
|
||||
Enable Sheets-first mirror
|
||||
</button>
|
||||
</div>
|
||||
</AppShell>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user