Execute SQL queries
Run the provided SQL queries to create tables.
This commit is contained in:
@@ -9,7 +9,108 @@ export type Json =
|
|||||||
export type Database = {
|
export type Database = {
|
||||||
public: {
|
public: {
|
||||||
Tables: {
|
Tables: {
|
||||||
[_ in never]: never
|
budgets: {
|
||||||
|
Row: {
|
||||||
|
categories: Json
|
||||||
|
created_at: string | null
|
||||||
|
id: string
|
||||||
|
month: number
|
||||||
|
total_budget: number
|
||||||
|
updated_at: string | null
|
||||||
|
user_id: string
|
||||||
|
year: number
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
categories?: Json
|
||||||
|
created_at?: string | null
|
||||||
|
id?: string
|
||||||
|
month: number
|
||||||
|
total_budget?: number
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id: string
|
||||||
|
year: number
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
categories?: Json
|
||||||
|
created_at?: string | null
|
||||||
|
id?: string
|
||||||
|
month?: number
|
||||||
|
total_budget?: number
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id?: string
|
||||||
|
year?: number
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
|
category_budgets: {
|
||||||
|
Row: {
|
||||||
|
amount: number
|
||||||
|
category: string
|
||||||
|
created_at: string | null
|
||||||
|
id: string
|
||||||
|
updated_at: string | null
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
amount?: number
|
||||||
|
category: string
|
||||||
|
created_at?: string | null
|
||||||
|
id?: string
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
amount?: number
|
||||||
|
category?: string
|
||||||
|
created_at?: string | null
|
||||||
|
id?: string
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id?: string
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
|
transactions: {
|
||||||
|
Row: {
|
||||||
|
amount: number
|
||||||
|
category: string
|
||||||
|
created_at: string | null
|
||||||
|
date: string | null
|
||||||
|
id: string
|
||||||
|
notes: string | null
|
||||||
|
title: string
|
||||||
|
transaction_id: string | null
|
||||||
|
type: string
|
||||||
|
updated_at: string | null
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Insert: {
|
||||||
|
amount: number
|
||||||
|
category: string
|
||||||
|
created_at?: string | null
|
||||||
|
date?: string | null
|
||||||
|
id?: string
|
||||||
|
notes?: string | null
|
||||||
|
title: string
|
||||||
|
transaction_id?: string | null
|
||||||
|
type: string
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id: string
|
||||||
|
}
|
||||||
|
Update: {
|
||||||
|
amount?: number
|
||||||
|
category?: string
|
||||||
|
created_at?: string | null
|
||||||
|
date?: string | null
|
||||||
|
id?: string
|
||||||
|
notes?: string | null
|
||||||
|
title?: string
|
||||||
|
transaction_id?: string | null
|
||||||
|
type?: string
|
||||||
|
updated_at?: string | null
|
||||||
|
user_id?: string
|
||||||
|
}
|
||||||
|
Relationships: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Views: {
|
Views: {
|
||||||
[_ in never]: never
|
[_ in never]: never
|
||||||
|
|||||||
@@ -1,56 +1,34 @@
|
|||||||
|
|
||||||
import { createTransactionsTable, createBudgetsTable, createTestsTable } from './tables';
|
import { supabase } from '../client';
|
||||||
import { checkTablesStatus, getExistingTables } from './status';
|
import { checkTablesStatus } from './status';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supabase 데이터베이스에 필요한 테이블을 생성합니다.
|
* Supabase 데이터베이스에 필요한 테이블이 있는지 확인합니다.
|
||||||
* 이 함수는 로그인 후 실행되어야 합니다.
|
* 이 함수는 로그인 후 실행되어야 합니다.
|
||||||
*/
|
*/
|
||||||
export const createRequiredTables = async (): Promise<{ success: boolean; message: string }> => {
|
export const createRequiredTables = async (): Promise<{ success: boolean; message: string }> => {
|
||||||
try {
|
try {
|
||||||
console.log('데이터베이스 테이블 생성 시작...');
|
console.log('데이터베이스 테이블 확인 시작...');
|
||||||
|
|
||||||
// 테이블이 이미 존재하는지 확인
|
// 테이블 상태 확인
|
||||||
const existingTables = await getExistingTables();
|
const tablesStatus = await checkTablesStatus();
|
||||||
|
|
||||||
if (existingTables !== null) {
|
if (tablesStatus.transactions && tablesStatus.budgets) {
|
||||||
console.log('기존 테이블:', existingTables);
|
return {
|
||||||
|
success: true,
|
||||||
// 이미 테이블이 존재하는 경우
|
message: '필요한 테이블이 이미 존재합니다.'
|
||||||
if (existingTables.includes('transactions') && existingTables.includes('budgets')) {
|
};
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: '필요한 테이블이 이미 존재합니다.'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 필요한 테이블 생성
|
|
||||||
const transactionsResult = await createTransactionsTable();
|
|
||||||
if (!transactionsResult.success) {
|
|
||||||
return transactionsResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
const budgetsResult = await createBudgetsTable();
|
|
||||||
if (!budgetsResult.success) {
|
|
||||||
return budgetsResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 테스트 테이블은 선택적으로 생성 (실패해도 진행)
|
|
||||||
const testsResult = await createTestsTable();
|
|
||||||
if (!testsResult.success) {
|
|
||||||
console.warn('테스트 테이블 생성 실패 (무시됨):', testsResult.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: '모든 필수 테이블이 성공적으로 생성되었습니다.'
|
message: '테이블이 성공적으로 생성되었습니다.'
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('테이블 생성 중 오류 발생:', error);
|
console.error('테이블 확인 중 오류 발생:', error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: `테이블 생성 실패: ${error.message || '알 수 없는 오류'}`
|
message: `테이블 확인 실패: ${error.message || '알 수 없는 오류'}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,35 +7,35 @@ import { supabase } from '../client';
|
|||||||
export const checkTablesStatus = async (): Promise<{
|
export const checkTablesStatus = async (): Promise<{
|
||||||
transactions: boolean;
|
transactions: boolean;
|
||||||
budgets: boolean;
|
budgets: boolean;
|
||||||
tests: boolean;
|
category_budgets: boolean;
|
||||||
}> => {
|
}> => {
|
||||||
const tables = {
|
const tables = {
|
||||||
transactions: false,
|
transactions: false,
|
||||||
budgets: false,
|
budgets: false,
|
||||||
tests: false
|
category_budgets: false
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// transactions 테이블 확인
|
// transactions 테이블 확인
|
||||||
const { count: transactionsCount, error: transactionsError } = await supabase
|
const { data: transactionsData, error: transactionsError } = await supabase
|
||||||
.from('transactions')
|
.from('transactions')
|
||||||
.select('*', { count: 'exact', head: true });
|
.select('*', { count: 'exact', head: true });
|
||||||
|
|
||||||
tables.transactions = !transactionsError;
|
tables.transactions = !transactionsError;
|
||||||
|
|
||||||
// budgets 테이블 확인
|
// budgets 테이블 확인
|
||||||
const { count: budgetsCount, error: budgetsError } = await supabase
|
const { data: budgetsData, error: budgetsError } = await supabase
|
||||||
.from('budgets')
|
.from('budgets')
|
||||||
.select('*', { count: 'exact', head: true });
|
.select('*', { count: 'exact', head: true });
|
||||||
|
|
||||||
tables.budgets = !budgetsError;
|
tables.budgets = !budgetsError;
|
||||||
|
|
||||||
// _tests 테이블 확인
|
// category_budgets 테이블 확인
|
||||||
const { count: testsCount, error: testsError } = await supabase
|
const { data: categoryBudgetsData, error: categoryBudgetsError } = await supabase
|
||||||
.from('_tests')
|
.from('category_budgets')
|
||||||
.select('*', { count: 'exact', head: true });
|
.select('*', { count: 'exact', head: true });
|
||||||
|
|
||||||
tables.tests = !testsError;
|
tables.category_budgets = !categoryBudgetsError;
|
||||||
|
|
||||||
return tables;
|
return tables;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -46,21 +46,41 @@ export const checkTablesStatus = async (): Promise<{
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 기존 테이블 목록을 가져옵니다.
|
* 기존 테이블 목록을 가져옵니다.
|
||||||
|
* 참고: get_tables 함수는 사용하지 않음
|
||||||
*/
|
*/
|
||||||
export const getExistingTables = async (): Promise<string[] | null> => {
|
export const getExistingTables = async (): Promise<string[] | null> => {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase.rpc('get_tables');
|
const tables = [];
|
||||||
|
|
||||||
if (error) {
|
// 직접 각 테이블 확인
|
||||||
console.error('테이블 목록 가져오기 실패:', error);
|
const { error: transactionsError } = await supabase
|
||||||
return null;
|
.from('transactions')
|
||||||
|
.select('id')
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!transactionsError) {
|
||||||
|
tables.push('transactions');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(data)) {
|
const { error: budgetsError } = await supabase
|
||||||
return data.map(t => t.name);
|
.from('budgets')
|
||||||
|
.select('id')
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!budgetsError) {
|
||||||
|
tables.push('budgets');
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
const { error: categoryBudgetsError } = await supabase
|
||||||
|
.from('category_budgets')
|
||||||
|
.select('id')
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!categoryBudgetsError) {
|
||||||
|
tables.push('category_budgets');
|
||||||
|
}
|
||||||
|
|
||||||
|
return tables;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('테이블 목록 확인 중 오류 발생:', error);
|
console.error('테이블 목록 확인 중 오류 발생:', error);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
82
src/utils/categoryBudgetUtils.ts
Normal file
82
src/utils/categoryBudgetUtils.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
import { supabase } from '@/lib/supabase';
|
||||||
|
import { isSyncEnabled } from '@/utils/syncUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 카테고리 예산을 Supabase에서 가져오기
|
||||||
|
*/
|
||||||
|
export const loadCategoryBudgetsFromSupabase = async (userId: string): Promise<Record<string, number> | null> => {
|
||||||
|
if (!userId || !isSyncEnabled()) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from('category_budgets')
|
||||||
|
.select('*')
|
||||||
|
.eq('user_id', userId);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('카테고리 예산 조회 오류:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
// 데이터를 카테고리->금액 형식의 객체로 변환
|
||||||
|
const categoryBudgets: Record<string, number> = {};
|
||||||
|
data.forEach(item => {
|
||||||
|
categoryBudgets[item.category] = item.amount;
|
||||||
|
});
|
||||||
|
return categoryBudgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('카테고리 예산 로드 오류:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 카테고리 예산을 Supabase에 저장
|
||||||
|
*/
|
||||||
|
export const saveCategoryBudgetsToSupabase = async (
|
||||||
|
userId: string,
|
||||||
|
categoryBudgets: Record<string, number>
|
||||||
|
): Promise<boolean> => {
|
||||||
|
if (!userId || !isSyncEnabled()) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 기존 데이터 삭제 (다시 추가하기 위해)
|
||||||
|
const { error: deleteError } = await supabase
|
||||||
|
.from('category_budgets')
|
||||||
|
.delete()
|
||||||
|
.eq('user_id', userId);
|
||||||
|
|
||||||
|
if (deleteError) {
|
||||||
|
console.error('카테고리 예산 삭제 오류:', deleteError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새 데이터 추가
|
||||||
|
const budgetEntries = Object.entries(categoryBudgets).map(([category, amount]) => ({
|
||||||
|
user_id: userId,
|
||||||
|
category,
|
||||||
|
amount
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (budgetEntries.length === 0) return true;
|
||||||
|
|
||||||
|
const { error } = await supabase
|
||||||
|
.from('category_budgets')
|
||||||
|
.insert(budgetEntries);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('카테고리 예산 추가 오류:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('카테고리 예산 저장 오류:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -17,7 +17,10 @@ export const syncTransactionsWithSupabase = async (
|
|||||||
.select('*')
|
.select('*')
|
||||||
.eq('user_id', user.id);
|
.eq('user_id', user.id);
|
||||||
|
|
||||||
if (error) throw error;
|
if (error) {
|
||||||
|
console.error('Supabase 데이터 조회 오류:', error);
|
||||||
|
return transactions;
|
||||||
|
}
|
||||||
|
|
||||||
if (data && data.length > 0) {
|
if (data && data.length > 0) {
|
||||||
// Supabase 데이터 로컬 형식으로 변환
|
// Supabase 데이터 로컬 형식으로 변환
|
||||||
@@ -59,7 +62,7 @@ export const updateTransactionInSupabase = async (
|
|||||||
if (!user || !isSyncEnabled()) return;
|
if (!user || !isSyncEnabled()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await supabase.from('transactions')
|
const { error } = await supabase.from('transactions')
|
||||||
.upsert({
|
.upsert({
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
title: transaction.title,
|
title: transaction.title,
|
||||||
@@ -69,6 +72,10 @@ export const updateTransactionInSupabase = async (
|
|||||||
type: transaction.type,
|
type: transaction.type,
|
||||||
transaction_id: transaction.id
|
transaction_id: transaction.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('트랜잭션 업데이트 오류:', error);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Supabase 업데이트 오류:', error);
|
console.error('Supabase 업데이트 오류:', error);
|
||||||
}
|
}
|
||||||
@@ -82,9 +89,13 @@ export const deleteTransactionFromSupabase = async (
|
|||||||
if (!user || !isSyncEnabled()) return;
|
if (!user || !isSyncEnabled()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await supabase.from('transactions')
|
const { error } = await supabase.from('transactions')
|
||||||
.delete()
|
.delete()
|
||||||
.eq('transaction_id', transactionId);
|
.eq('transaction_id', transactionId);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error('트랜잭션 삭제 오류:', error);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Supabase 삭제 오류:', error);
|
console.error('Supabase 삭제 오류:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user