mitlist/fe/src/stores/listDetailStore.ts
mohamad f49e15c05c
Some checks failed
Deploy to Production, build images and push to Gitea Registry / build_and_push (pull_request) Failing after 1m24s
feat: Introduce FastAPI and Vue.js guidelines, enhance API structure, and add caching support
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.
2025-06-09 21:02:51 +02:00

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;
}
*/