Implement debt variable management with database integration in DebtCard component

This commit is contained in:
2025-06-07 14:53:02 -03:00
parent 1239e0f6f3
commit 7f70ec880f
3 changed files with 161 additions and 21 deletions

View File

@@ -27,8 +27,8 @@ import {
TrendingUp,
Edit3,
} from "lucide-react";
import { supabase } from "../lib/supabase";
import type { Debt } from "../lib/supabase";
import { supabase, type Debt, type DebtVariable } from "../lib/supabase";
import { toast } from "../hooks/use-toast";
interface DebtCardProps {
debt: Debt;
@@ -110,22 +110,82 @@ export function DebtCard({ debt, onUpdate }: DebtCardProps) {
const [body, setBody] = useState("");
const [variables, setVariables] = useState<Record<string, string>>({});
// Load variables from database
const loadVariables = async () => {
try {
const { data: dbVariables, error } = await supabase
.from("debt_variables")
.select("variable_name, variable_value")
.eq("debt_id", debt.id);
if (error) throw error;
const loadedVariables: Record<string, string> = {};
dbVariables?.forEach((dbVar) => {
loadedVariables[dbVar.variable_name] = dbVar.variable_value || "";
});
return loadedVariables;
} catch (error) {
console.error("Error loading variables:", error);
return {};
}
};
// Save variables to database
const saveVariables = async (variablesToSave: Record<string, string>) => {
try {
// First, delete existing variables for this debt
await supabase.from("debt_variables").delete().eq("debt_id", debt.id);
// Then insert new variables
const variableRecords = Object.entries(variablesToSave).map(
([name, value]) => ({
debt_id: debt.id,
variable_name: name,
variable_value: value,
})
);
if (variableRecords.length > 0) {
const { error } = await supabase
.from("debt_variables")
.insert(variableRecords);
if (error) throw error;
}
} catch (error) {
console.error("Error saving variables:", error);
throw error;
}
};
// Initialize data when dialog opens
useEffect(() => {
if (debt.metadata?.aiEmail) {
const aiEmail = debt.metadata.aiEmail;
setSubject(aiEmail.subject || "");
setBody(aiEmail.body || "");
const initializeData = async () => {
if (debt.metadata?.aiEmail) {
const aiEmail = debt.metadata.aiEmail;
setSubject(aiEmail.subject || "");
setBody(aiEmail.body || "");
// Extract variables from both subject and body
const allText = `${aiEmail.subject || ""} ${aiEmail.body || ""}`;
const extractedVars = extractVariables(allText);
const initialVariables: Record<string, string> = {};
extractedVars.forEach((variable) => {
initialVariables[variable] = "";
});
setVariables(initialVariables);
}
// Extract variables from both subject and body
const allText = `${aiEmail.subject || ""} ${aiEmail.body || ""}`;
const extractedVars = extractVariables(allText);
// Load saved variables from database
const savedVariables = await loadVariables();
// Merge extracted variables with saved values
const initialVariables: Record<string, string> = {};
extractedVars.forEach((variable) => {
initialVariables[variable] = savedVariables[variable] || "";
});
setVariables(initialVariables);
}
};
initializeData();
}, [debt.metadata?.aiEmail]);
// Update variables when body changes
@@ -223,11 +283,24 @@ export function DebtCard({ debt, onUpdate }: DebtCardProps) {
.eq("id", debt.id);
if (error) {
console.error("Error saving changes:", error);
// You could add toast notification here
console.error("Error saving debt metadata:", error);
toast({
title: "Error",
description: "Failed to save email changes. Please try again.",
variant: "destructive",
});
return;
}
// Save variables to database
await saveVariables(variables);
toast({
title: "Changes saved",
description:
"Your email and variables have been updated successfully.",
});
// Call onUpdate callback to refresh the parent component
if (onUpdate) {
onUpdate();
@@ -236,6 +309,11 @@ export function DebtCard({ debt, onUpdate }: DebtCardProps) {
setIsEditing(false);
} catch (error) {
console.error("Error saving changes:", error);
toast({
title: "Error",
description: "Failed to save changes. Please try again.",
variant: "destructive",
});
} finally {
setIsSaving(false);
}

View File

@@ -1,10 +1,10 @@
import { createClient } from '@supabase/supabase-js';
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.PUBLIC_SUPABASE_ANON_KEY;
if (!supabaseUrl || !supabaseAnonKey) {
throw new Error('Missing Supabase environment variables');
throw new Error("Missing Supabase environment variables");
}
export const supabase = createClient(supabaseUrl, supabaseAnonKey);
@@ -22,7 +22,7 @@ export type Debt = {
vendor: string;
amount: number;
raw_email: string | null;
status: 'received' | 'negotiating' | 'settled' | 'failed' | 'opted_out';
status: "received" | "negotiating" | "settled" | "failed" | "opted_out";
negotiated_plan: string | null;
projected_savings: number;
user_id: string;
@@ -66,4 +66,13 @@ export type EmailProcessingUsage = {
emails_processed: number;
created_at: string;
updated_at: string;
};
};
export type DebtVariable = {
id: string;
debt_id: string;
variable_name: string;
variable_value: string | null;
created_at: string;
updated_at: string;
};

View File

@@ -0,0 +1,53 @@
-- Create debt_variables table to store variable values for each debt
CREATE TABLE IF NOT EXISTS debt_variables (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
debt_id uuid REFERENCES debts(id) ON DELETE CASCADE NOT NULL,
variable_name text NOT NULL,
variable_value text,
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now(),
UNIQUE(debt_id, variable_name)
);
-- Enable RLS
ALTER TABLE debt_variables ENABLE ROW LEVEL SECURITY;
-- Create policies for debt_variables
CREATE POLICY "Users can manage their debt variables"
ON debt_variables
FOR ALL
TO authenticated
USING (
EXISTS (
SELECT 1 FROM debts
WHERE debts.id = debt_variables.debt_id
AND debts.user_id = auth.uid()
)
)
WITH CHECK (
EXISTS (
SELECT 1 FROM debts
WHERE debts.id = debt_variables.debt_id
AND debts.user_id = auth.uid()
)
);
-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_debt_variables_debt_id ON debt_variables(debt_id);
CREATE INDEX IF NOT EXISTS idx_debt_variables_name ON debt_variables(variable_name);
-- Create function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_debt_variables_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Create trigger for updated_at
DROP TRIGGER IF EXISTS update_debt_variables_updated_at ON debt_variables;
CREATE TRIGGER update_debt_variables_updated_at
BEFORE UPDATE ON debt_variables
FOR EACH ROW
EXECUTE FUNCTION update_debt_variables_updated_at_column();