
Some checks failed
Deploy to Production, build images and push to Gitea Registry / build_and_push (pull_request) Failing after 1m24s
This commit adds new guidelines for FastAPI and Vue.js development, emphasizing best practices for component structure, API performance, and data handling. It also introduces caching mechanisms using Redis for improved performance and updates the API structure to streamline authentication and user management. Additionally, new endpoints for categories and time entries are implemented, enhancing the overall functionality of the application.
160 lines
5.2 KiB
TypeScript
160 lines
5.2 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import { apiClient, API_ENDPOINTS } from '@/services/api'
|
|
import type {
|
|
Expense,
|
|
ExpenseSplit,
|
|
SettlementActivity,
|
|
SettlementActivityPublic,
|
|
} from '@/types/expense'
|
|
import type { SettlementActivityCreate } from '@/types/expense'
|
|
import type { List } from '@/types/list'
|
|
import type { AxiosResponse } from 'axios'
|
|
|
|
export interface ListWithExpenses extends List {
|
|
id: number
|
|
expenses: Expense[]
|
|
}
|
|
|
|
interface ListDetailState {
|
|
currentList: ListWithExpenses | null
|
|
isLoading: boolean
|
|
error: string | null
|
|
isSettlingSplit: boolean
|
|
}
|
|
|
|
export const useListDetailStore = defineStore('listDetail', {
|
|
state: (): ListDetailState => ({
|
|
currentList: null,
|
|
isLoading: false,
|
|
error: null,
|
|
isSettlingSplit: false,
|
|
}),
|
|
|
|
actions: {
|
|
async fetchListWithExpenses(listId: string) {
|
|
if (!listId || listId === 'undefined' || listId === 'null') {
|
|
this.error = 'Invalid list ID provided.';
|
|
console.warn(`fetchListWithExpenses called with invalid ID: ${listId}`);
|
|
return;
|
|
}
|
|
this.isLoading = true
|
|
this.error = null
|
|
try {
|
|
// Get list details
|
|
const listEndpoint = API_ENDPOINTS.LISTS.BY_ID(listId)
|
|
const listResponse = await apiClient.get(listEndpoint)
|
|
const listData = listResponse.data as List
|
|
|
|
// Get expenses for this list
|
|
const expensesEndpoint = API_ENDPOINTS.LISTS.EXPENSES(listId)
|
|
const expensesResponse = await apiClient.get(expensesEndpoint)
|
|
const expensesData = expensesResponse.data as Expense[]
|
|
|
|
// Combine into ListWithExpenses
|
|
this.currentList = {
|
|
...listData,
|
|
expenses: expensesData,
|
|
} as ListWithExpenses
|
|
} catch (err: any) {
|
|
this.error = err.response?.data?.detail || err.message || 'Failed to fetch list details'
|
|
this.currentList = null
|
|
console.error('Error fetching list details:', err)
|
|
} finally {
|
|
this.isLoading = false
|
|
}
|
|
},
|
|
|
|
async settleExpenseSplit(payload: {
|
|
list_id_for_refetch: string // ID of the list to refetch after settlement
|
|
expense_split_id: number
|
|
activity_data: SettlementActivityCreate
|
|
}): Promise<boolean> {
|
|
this.isSettlingSplit = true
|
|
this.error = null
|
|
try {
|
|
// Call the actual API endpoint using generic post method
|
|
const endpoint = `/financials/expense_splits/${payload.expense_split_id}/settle`
|
|
const response = await apiClient.post(endpoint, payload.activity_data)
|
|
|
|
// Refresh list data to show updated statuses
|
|
if (payload.list_id_for_refetch) {
|
|
await this.fetchListWithExpenses(payload.list_id_for_refetch)
|
|
} else if (this.currentList?.id) {
|
|
// Fallback if list_id_for_refetch is not provided but currentList exists
|
|
await this.fetchListWithExpenses(String(this.currentList.id))
|
|
} else {
|
|
console.warn(
|
|
'Could not refetch list details: list_id_for_refetch not provided and no currentList available.',
|
|
)
|
|
}
|
|
|
|
this.isSettlingSplit = false
|
|
return true // Indicate success
|
|
} catch (err: any) {
|
|
const errorMessage =
|
|
err.response?.data?.detail || err.message || 'Failed to settle expense split.'
|
|
this.error = errorMessage
|
|
console.error('Error settling expense split:', err)
|
|
this.isSettlingSplit = false
|
|
return false // Indicate failure
|
|
}
|
|
},
|
|
|
|
setError(errorMessage: string) {
|
|
this.error = errorMessage
|
|
this.isLoading = false
|
|
},
|
|
},
|
|
|
|
getters: {
|
|
getList(state: ListDetailState): ListWithExpenses | null {
|
|
return state.currentList
|
|
},
|
|
getExpenses(state: ListDetailState): Expense[] {
|
|
return state.currentList?.expenses || []
|
|
},
|
|
getPaidAmountForSplit:
|
|
(state: ListDetailState) =>
|
|
(splitId: number): number => {
|
|
let totalPaid = 0
|
|
if (state.currentList && state.currentList.expenses) {
|
|
for (const expense of state.currentList.expenses) {
|
|
const split = expense.splits.find((s) => s.id === splitId)
|
|
if (split && split.settlement_activities) {
|
|
totalPaid = split.settlement_activities.reduce((sum, activity) => {
|
|
return sum + parseFloat(activity.amount_paid)
|
|
}, 0)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return totalPaid
|
|
},
|
|
getExpenseSplitById:
|
|
(state: ListDetailState) =>
|
|
(splitId: number): ExpenseSplit | undefined => {
|
|
if (!state.currentList || !state.currentList.expenses) return undefined
|
|
for (const expense of state.currentList.expenses) {
|
|
const split = expense.splits.find((s) => s.id === splitId)
|
|
if (split) return split
|
|
}
|
|
return undefined
|
|
},
|
|
},
|
|
})
|
|
|
|
// Assuming List interface might be defined in fe/src/types/list.ts
|
|
// If not, it should be defined like this:
|
|
/*
|
|
export interface List {
|
|
id: number;
|
|
name: string;
|
|
description?: string | null;
|
|
is_complete: boolean;
|
|
group_id?: number | null;
|
|
// items: Item[]; // Item interface would also need to be defined
|
|
// version: number;
|
|
// updated_at: string;
|
|
}
|
|
*/
|