Refactor: Split TransactionForm into smaller components
Refactors TransactionForm.tsx into smaller, reusable components and a custom hook to improve code organization and maintainability.
This commit is contained in:
parent
1cb5be29cc
commit
e6d6f5c2b3
@ -1,42 +1,13 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import * as z from 'zod';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from '@/components/ui/select';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useEffect } from 'react';
|
||||
import { Transaction } from '@/types/financialTypes';
|
||||
import { updateTransacao } from '@/services/transacaoService';
|
||||
|
||||
const transactionSchema = z.object({
|
||||
estabelecimento: z.string().min(1, { message: 'Estabelecimento é obrigatório' }),
|
||||
valor: z.string().min(1, { message: 'Valor é obrigatório' }),
|
||||
detalhes: z.string().min(1, { message: 'Detalhes são obrigatórios' }),
|
||||
categoria: z.string().min(1, { message: 'Categoria é obrigatória' }),
|
||||
tipo: z.string().min(1, { message: 'Tipo é obrigatório' }),
|
||||
quando: z.string().min(1, { message: 'Data é obrigatória' }),
|
||||
grupo_id: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
type TransactionFormValues = z.infer<typeof transactionSchema>;
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { useTransactionForm } from '@/hooks/useTransactionForm';
|
||||
import { TransactionTextField } from '@/components/transaction/TransactionTextField';
|
||||
import { TransactionTypeField } from '@/components/transaction/TransactionTypeField';
|
||||
import { TransactionDateField } from '@/components/transaction/TransactionDateField';
|
||||
import { GroupSelectField } from '@/components/transaction/GroupSelectField';
|
||||
import { TransactionFormActions } from '@/components/transaction/TransactionFormActions';
|
||||
|
||||
interface TransactionFormProps {
|
||||
onSuccess: () => void;
|
||||
@ -55,305 +26,69 @@ export function TransactionForm({
|
||||
transaction = null,
|
||||
isEditing = false
|
||||
}: TransactionFormProps) {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [grupos, setGrupos] = useState<{remote_jid: string, nome_grupo: string | null}[]>([]);
|
||||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
form,
|
||||
grupos,
|
||||
isSubmitting,
|
||||
onSubmit,
|
||||
fetchGrupos
|
||||
} = useTransactionForm(
|
||||
onSuccess,
|
||||
onCancel,
|
||||
defaultTipo,
|
||||
transaction,
|
||||
isEditing
|
||||
);
|
||||
|
||||
// Fetch user groups when the form loads
|
||||
useEffect(() => {
|
||||
const fetchGrupos = async () => {
|
||||
const userEmail = localStorage.getItem('userEmail');
|
||||
if (!userEmail) {
|
||||
console.error('Email do usuário não encontrado');
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('grupos_whatsapp')
|
||||
.select('remote_jid, nome_grupo')
|
||||
.eq('login', normalizedEmail);
|
||||
|
||||
if (error) {
|
||||
console.error('Erro ao buscar grupos:', error);
|
||||
} else if (data) {
|
||||
setGrupos(data);
|
||||
}
|
||||
};
|
||||
|
||||
fetchGrupos();
|
||||
}, []);
|
||||
|
||||
// Set up form with default values or transaction data if editing
|
||||
const form = useForm<TransactionFormValues>({
|
||||
resolver: zodResolver(transactionSchema),
|
||||
defaultValues: {
|
||||
estabelecimento: transaction?.estabelecimento || '',
|
||||
valor: transaction ? String(Math.abs(transaction.valor)) : '',
|
||||
detalhes: transaction?.detalhes || '',
|
||||
categoria: transaction?.categoria || '',
|
||||
tipo: transaction?.tipo as 'receita' | 'despesa' || defaultTipo,
|
||||
quando: transaction?.quando ?
|
||||
new Date(transaction.quando).toISOString().split('T')[0] :
|
||||
new Date().toISOString().split('T')[0],
|
||||
grupo_id: transaction?.grupo_id || grupoId || null
|
||||
}
|
||||
});
|
||||
|
||||
async function onSubmit(data: TransactionFormValues) {
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
// Get user email from localStorage - principal identifier now
|
||||
const userEmail = localStorage.getItem('userEmail');
|
||||
|
||||
if (!userEmail) {
|
||||
toast({
|
||||
title: "Erro no formulário",
|
||||
description: "Email do usuário não encontrado",
|
||||
variant: "destructive"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize the email (lowercase and trim spaces)
|
||||
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||
|
||||
// Get user ID from localStorage - kept for compatibility
|
||||
const userId = localStorage.getItem('userId') || '';
|
||||
|
||||
const valorNumerico = parseFloat(data.valor.replace(',', '.'));
|
||||
|
||||
if (isNaN(valorNumerico)) {
|
||||
toast({
|
||||
title: "Erro no formulário",
|
||||
description: "O valor precisa ser um número válido",
|
||||
variant: "destructive"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the value based on transaction type
|
||||
const valorFinal = data.tipo.toLowerCase() === 'receita'
|
||||
? Math.abs(valorNumerico)
|
||||
: Math.abs(valorNumerico);
|
||||
|
||||
console.log(`Salvando transação para usuário: ${normalizedEmail} (ID: ${userId})`);
|
||||
|
||||
// Handle editing vs creating new transaction
|
||||
if (isEditing && transaction) {
|
||||
// Update existing transaction
|
||||
const updatedTransaction = {
|
||||
...transaction,
|
||||
estabelecimento: data.estabelecimento,
|
||||
valor: valorFinal,
|
||||
detalhes: data.detalhes,
|
||||
categoria: data.categoria,
|
||||
tipo: data.tipo,
|
||||
quando: data.quando,
|
||||
grupo_id: data.grupo_id || null
|
||||
};
|
||||
|
||||
await updateTransacao(updatedTransaction);
|
||||
toast({
|
||||
title: "Transação atualizada",
|
||||
description: "Transação atualizada com sucesso",
|
||||
});
|
||||
onSuccess();
|
||||
} else {
|
||||
// Create new transaction
|
||||
// Prepare transaction data including login and grupo_id
|
||||
const transactionData = {
|
||||
user: userId, // Kept for compatibility
|
||||
login: normalizedEmail, // Main field for identification
|
||||
estabelecimento: data.estabelecimento,
|
||||
valor: valorFinal,
|
||||
detalhes: data.detalhes,
|
||||
categoria: data.categoria,
|
||||
tipo: data.tipo,
|
||||
quando: data.quando,
|
||||
grupo_id: data.grupo_id || null
|
||||
};
|
||||
|
||||
console.log('Dados da transação a serem salvos:', transactionData);
|
||||
|
||||
// With RLS disabled, we can insert directly to the fixed table
|
||||
const { error } = await supabase
|
||||
.from('transacoes')
|
||||
.insert([transactionData]);
|
||||
|
||||
if (error) {
|
||||
console.error('Erro ao salvar transação:', error);
|
||||
|
||||
toast({
|
||||
title: "Erro ao salvar",
|
||||
description: `Não foi possível salvar a transação: ${error.message}`,
|
||||
variant: "destructive"
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Transação salva",
|
||||
description: "Transação registrada com sucesso",
|
||||
});
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao processar formulário:', error);
|
||||
toast({
|
||||
title: "Erro inesperado",
|
||||
description: "Ocorreu um erro ao processar sua solicitação",
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
<TransactionTextField
|
||||
form={form}
|
||||
name="estabelecimento"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Estabelecimento</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Nome do estabelecimento" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
label="Estabelecimento"
|
||||
placeholder="Nome do estabelecimento"
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
<TransactionTextField
|
||||
form={form}
|
||||
name="valor"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Valor</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="0,00"
|
||||
{...field}
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
label="Valor"
|
||||
placeholder="0,00"
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="tipo"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Tipo</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione o tipo" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="despesa">Despesa</SelectItem>
|
||||
<SelectItem value="receita">Receita</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<TransactionTypeField form={form} />
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
<TransactionTextField
|
||||
form={form}
|
||||
name="categoria"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Categoria</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Categoria" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
label="Categoria"
|
||||
placeholder="Categoria"
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
<TransactionTextField
|
||||
form={form}
|
||||
name="detalhes"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Detalhes</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Detalhes da transação" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
label="Detalhes"
|
||||
placeholder="Detalhes da transação"
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="quando"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Data</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="date" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
<TransactionDateField form={form} />
|
||||
|
||||
<GroupSelectField form={form} grupos={grupos} />
|
||||
|
||||
<TransactionFormActions
|
||||
onCancel={onCancel}
|
||||
isSubmitting={isSubmitting}
|
||||
defaultTipo={defaultTipo}
|
||||
/>
|
||||
|
||||
{grupos.length > 0 && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="grupo_id"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Grupo WhatsApp (opcional)</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
value={field.value || undefined}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione um grupo (opcional)" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">Nenhum grupo</SelectItem>
|
||||
{grupos.map((grupo) => (
|
||||
<SelectItem key={grupo.remote_jid} value={grupo.remote_jid}>
|
||||
{grupo.nome_grupo || grupo.remote_jid}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="flex justify-end space-x-2 pt-2">
|
||||
<Button variant="outline" type="button" onClick={onCancel} disabled={isSubmitting}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button type="submit" disabled={isSubmitting} className={defaultTipo === 'receita' ? 'bg-finance-green hover:bg-finance-green/90' : ''}>
|
||||
{isSubmitting ? "Salvando..." : "Salvar"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
|
||||
45
src/components/transaction/GroupSelectField.tsx
Normal file
45
src/components/transaction/GroupSelectField.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
import { FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@/components/ui/select";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { TransactionFormValues } from "@/hooks/useTransactionForm";
|
||||
|
||||
interface GroupSelectFieldProps {
|
||||
form: UseFormReturn<TransactionFormValues>;
|
||||
grupos: {remote_jid: string, nome_grupo: string | null}[];
|
||||
}
|
||||
|
||||
export function GroupSelectField({ form, grupos }: GroupSelectFieldProps) {
|
||||
if (grupos.length === 0) return null;
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Grupo WhatsApp (opcional)</FormLabel>
|
||||
<Select
|
||||
onValueChange={(value) => form.setValue("grupo_id", value === "none" ? null : value)}
|
||||
value={form.getValues("grupo_id") || undefined}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione um grupo (opcional)" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">Nenhum grupo</SelectItem>
|
||||
{grupos.map((grupo) => (
|
||||
<SelectItem key={grupo.remote_jid} value={grupo.remote_jid}>
|
||||
{grupo.nome_grupo || grupo.remote_jid}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
24
src/components/transaction/TransactionDateField.tsx
Normal file
24
src/components/transaction/TransactionDateField.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
import { FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { TransactionFormValues } from "@/hooks/useTransactionForm";
|
||||
|
||||
interface TransactionDateFieldProps {
|
||||
form: UseFormReturn<TransactionFormValues>;
|
||||
}
|
||||
|
||||
export function TransactionDateField({ form }: TransactionDateFieldProps) {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Data</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="date"
|
||||
{...form.register("quando")}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
34
src/components/transaction/TransactionFormActions.tsx
Normal file
34
src/components/transaction/TransactionFormActions.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface TransactionFormActionsProps {
|
||||
onCancel: () => void;
|
||||
isSubmitting: boolean;
|
||||
defaultTipo?: 'receita' | 'despesa';
|
||||
}
|
||||
|
||||
export function TransactionFormActions({
|
||||
onCancel,
|
||||
isSubmitting,
|
||||
defaultTipo = 'despesa'
|
||||
}: TransactionFormActionsProps) {
|
||||
return (
|
||||
<div className="flex justify-end space-x-2 pt-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={isSubmitting}
|
||||
className={defaultTipo === 'receita' ? 'bg-finance-green hover:bg-finance-green/90' : ''}
|
||||
>
|
||||
{isSubmitting ? "Salvando..." : "Salvar"}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
38
src/components/transaction/TransactionTextField.tsx
Normal file
38
src/components/transaction/TransactionTextField.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
import { FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { TransactionFormValues } from "@/hooks/useTransactionForm";
|
||||
|
||||
interface TransactionTextFieldProps {
|
||||
form: UseFormReturn<TransactionFormValues>;
|
||||
name: keyof TransactionFormValues;
|
||||
label: string;
|
||||
placeholder: string;
|
||||
type?: string;
|
||||
inputMode?: "text" | "numeric" | "decimal" | "tel" | "search" | "email" | "url" | undefined;
|
||||
}
|
||||
|
||||
export function TransactionTextField({
|
||||
form,
|
||||
name,
|
||||
label,
|
||||
placeholder,
|
||||
type = "text",
|
||||
inputMode
|
||||
}: TransactionTextFieldProps) {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
{...form.register(name)}
|
||||
type={type}
|
||||
inputMode={inputMode}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
38
src/components/transaction/TransactionTypeField.tsx
Normal file
38
src/components/transaction/TransactionTypeField.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
import { FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from "@/components/ui/select";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { TransactionFormValues } from "@/hooks/useTransactionForm";
|
||||
|
||||
interface TransactionTypeFieldProps {
|
||||
form: UseFormReturn<TransactionFormValues>;
|
||||
}
|
||||
|
||||
export function TransactionTypeField({ form }: TransactionTypeFieldProps) {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>Tipo</FormLabel>
|
||||
<Select
|
||||
onValueChange={(value) => form.setValue("tipo", value)}
|
||||
defaultValue={form.getValues("tipo")}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione o tipo" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="despesa">Despesa</SelectItem>
|
||||
<SelectItem value="receita">Receita</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
);
|
||||
}
|
||||
191
src/hooks/useTransactionForm.ts
Normal file
191
src/hooks/useTransactionForm.ts
Normal file
@ -0,0 +1,191 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import * as z from 'zod';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { Transaction } from '@/types/financialTypes';
|
||||
import { updateTransacao } from '@/services/transacaoService';
|
||||
|
||||
const transactionSchema = z.object({
|
||||
estabelecimento: z.string().min(1, { message: 'Estabelecimento é obrigatório' }),
|
||||
valor: z.string().min(1, { message: 'Valor é obrigatório' }),
|
||||
detalhes: z.string().min(1, { message: 'Detalhes são obrigatórios' }),
|
||||
categoria: z.string().min(1, { message: 'Categoria é obrigatória' }),
|
||||
tipo: z.string().min(1, { message: 'Tipo é obrigatório' }),
|
||||
quando: z.string().min(1, { message: 'Data é obrigatória' }),
|
||||
grupo_id: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export type TransactionFormValues = z.infer<typeof transactionSchema>;
|
||||
|
||||
export const useTransactionForm = (
|
||||
onSuccess: () => void,
|
||||
onCancel: () => void,
|
||||
defaultTipo: 'receita' | 'despesa' = 'despesa',
|
||||
transaction: Transaction | null = null,
|
||||
isEditing: boolean = false
|
||||
) => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [grupos, setGrupos] = useState<{remote_jid: string, nome_grupo: string | null}[]>([]);
|
||||
const { toast } = useToast();
|
||||
|
||||
// Fetch user groups when the form loads
|
||||
const fetchGrupos = async () => {
|
||||
const userEmail = localStorage.getItem('userEmail');
|
||||
if (!userEmail) {
|
||||
console.error('Email do usuário não encontrado');
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from('grupos_whatsapp')
|
||||
.select('remote_jid, nome_grupo')
|
||||
.eq('login', normalizedEmail);
|
||||
|
||||
if (error) {
|
||||
console.error('Erro ao buscar grupos:', error);
|
||||
} else if (data) {
|
||||
setGrupos(data);
|
||||
}
|
||||
};
|
||||
|
||||
// Set up form with default values or transaction data if editing
|
||||
const form = useForm<TransactionFormValues>({
|
||||
resolver: zodResolver(transactionSchema),
|
||||
defaultValues: {
|
||||
estabelecimento: transaction?.estabelecimento || '',
|
||||
valor: transaction ? String(Math.abs(transaction.valor)) : '',
|
||||
detalhes: transaction?.detalhes || '',
|
||||
categoria: transaction?.categoria || '',
|
||||
tipo: transaction?.tipo as 'receita' | 'despesa' || defaultTipo,
|
||||
quando: transaction?.quando ?
|
||||
new Date(transaction.quando).toISOString().split('T')[0] :
|
||||
new Date().toISOString().split('T')[0],
|
||||
grupo_id: transaction?.grupo_id || null
|
||||
}
|
||||
});
|
||||
|
||||
async function onSubmit(data: TransactionFormValues) {
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
// Get user email from localStorage - principal identifier now
|
||||
const userEmail = localStorage.getItem('userEmail');
|
||||
|
||||
if (!userEmail) {
|
||||
toast({
|
||||
title: "Erro no formulário",
|
||||
description: "Email do usuário não encontrado",
|
||||
variant: "destructive"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize the email (lowercase and trim spaces)
|
||||
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||
|
||||
// Get user ID from localStorage - kept for compatibility
|
||||
const userId = localStorage.getItem('userId') || '';
|
||||
|
||||
const valorNumerico = parseFloat(data.valor.replace(',', '.'));
|
||||
|
||||
if (isNaN(valorNumerico)) {
|
||||
toast({
|
||||
title: "Erro no formulário",
|
||||
description: "O valor precisa ser um número válido",
|
||||
variant: "destructive"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the value based on transaction type
|
||||
const valorFinal = data.tipo.toLowerCase() === 'receita'
|
||||
? Math.abs(valorNumerico)
|
||||
: Math.abs(valorNumerico);
|
||||
|
||||
console.log(`Salvando transação para usuário: ${normalizedEmail} (ID: ${userId})`);
|
||||
|
||||
// Handle editing vs creating new transaction
|
||||
if (isEditing && transaction) {
|
||||
// Update existing transaction
|
||||
const updatedTransaction = {
|
||||
...transaction,
|
||||
estabelecimento: data.estabelecimento,
|
||||
valor: valorFinal,
|
||||
detalhes: data.detalhes,
|
||||
categoria: data.categoria,
|
||||
tipo: data.tipo,
|
||||
quando: data.quando,
|
||||
grupo_id: data.grupo_id || null
|
||||
};
|
||||
|
||||
await updateTransacao(updatedTransaction);
|
||||
toast({
|
||||
title: "Transação atualizada",
|
||||
description: "Transação atualizada com sucesso",
|
||||
});
|
||||
onSuccess();
|
||||
} else {
|
||||
// Create new transaction
|
||||
// Prepare transaction data including login and grupo_id
|
||||
const transactionData = {
|
||||
user: userId, // Kept for compatibility
|
||||
login: normalizedEmail, // Main field for identification
|
||||
estabelecimento: data.estabelecimento,
|
||||
valor: valorFinal,
|
||||
detalhes: data.detalhes,
|
||||
categoria: data.categoria,
|
||||
tipo: data.tipo,
|
||||
quando: data.quando,
|
||||
grupo_id: data.grupo_id || null
|
||||
};
|
||||
|
||||
console.log('Dados da transação a serem salvos:', transactionData);
|
||||
|
||||
// With RLS disabled, we can insert directly to the fixed table
|
||||
const { error } = await supabase
|
||||
.from('transacoes')
|
||||
.insert([transactionData]);
|
||||
|
||||
if (error) {
|
||||
console.error('Erro ao salvar transação:', error);
|
||||
|
||||
toast({
|
||||
title: "Erro ao salvar",
|
||||
description: `Não foi possível salvar a transação: ${error.message}`,
|
||||
variant: "destructive"
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: "Transação salva",
|
||||
description: "Transação registrada com sucesso",
|
||||
});
|
||||
onSuccess();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao processar formulário:', error);
|
||||
toast({
|
||||
title: "Erro inesperado",
|
||||
description: "Ocorreu um erro ao processar sua solicitação",
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
form,
|
||||
grupos,
|
||||
isSubmitting,
|
||||
onSubmit,
|
||||
fetchGrupos
|
||||
};
|
||||
};
|
||||
|
||||
export { transactionSchema };
|
||||
Loading…
Reference in New Issue
Block a user