Skip to content

Commit c359155

Browse files
committed
refactor: centralize category logic in useCategories composable
1 parent 2132255 commit c359155

9 files changed

Lines changed: 130 additions & 87 deletions

File tree

src/App.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,22 @@
5858
</template>
5959

6060
<script setup lang="ts">
61-
import { ref, onMounted, watch } from 'vue'
61+
import { ref, onMounted, computed, watch } from 'vue'
6262
import { useSupabase } from './composables/useSupabase'
63-
import Auth from './components/Auth.vue'
6463
import AppHeader from './components/AppHeader.vue'
64+
import Auth from './components/Auth.vue'
6565
import TodaySummary from './components/TodaySummary.vue'
66+
import ExpenseForm from './components/ExpenseForm.vue'
6667
import ExpenseList from './components/ExpenseList.vue'
6768
import ChartsView from './components/ChartsView.vue'
6869
import BottomNavigation from './components/BottomNavigation.vue'
69-
import ExpenseForm from './components/ExpenseForm.vue'
7070
import PasswordChange from './components/PasswordChange.vue'
71+
import { type CategoryKey } from './composables/useCategories'
7172
7273
interface Expense {
7374
id: string
7475
amount: number
75-
category: string
76+
category: CategoryKey
7677
date: string
7778
note: string
7879
user_id?: string

src/components/ExpenseForm.vue

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,14 @@
7474
</template>
7575

7676
<script setup lang="ts">
77-
import { ref, watch } from 'vue'
77+
import { ref, watch, defineProps, defineEmits } from 'vue'
78+
import { useSupabase } from '../composables/useSupabase'
79+
import { useCategories, type CategoryKey } from '../composables/useCategories'
7880
7981
interface Expense {
8082
id: string
8183
amount: number
82-
category: string
84+
category: CategoryKey
8385
date: string
8486
note?: string
8587
}
@@ -98,33 +100,30 @@ interface Emits {
98100
const props = defineProps<Props>()
99101
const emit = defineEmits<Emits>()
100102
103+
const { supabase } = useSupabase()
104+
const { CATEGORY_OPTIONS } = useCategories()
105+
101106
const showForm = ref(props.modelValue)
102107
const amount = ref('')
103-
const category = ref('Food')
108+
const category = ref<CategoryKey>('Food')
104109
const note = ref('')
105110
106-
const categoryOptions = [
107-
{ text: '餐饮', value: 'Food' },
108-
{ text: '交通', value: 'Transport' },
109-
{ text: '购物', value: 'Shopping' },
110-
{ text: '娱乐', value: 'Entertainment' },
111-
{ text: '其他', value: 'Other' }
112-
]
111+
const categoryOptions = CATEGORY_OPTIONS
113112
114113
watch(() => props.modelValue, (newValue) => {
115114
showForm.value = newValue
116115
if (newValue && props.expense) {
117116
// Populate form with existing expense data
118117
amount.value = props.expense.amount.toString()
119-
category.value = props.expense.category
118+
category.value = props.expense.category as CategoryKey
120119
note.value = props.expense.note || ''
121120
}
122121
})
123122
124123
watch(() => props.expense, (newExpense) => {
125124
if (newExpense) {
126125
amount.value = newExpense.amount.toString()
127-
category.value = newExpense.category
126+
category.value = newExpense.category as CategoryKey
128127
note.value = newExpense.note || ''
129128
}
130129
})

src/components/ExpenseList.vue

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
size="small"
2727
variant="flat"
2828
>
29-
{{ categoryNames[expense.category] }}
29+
{{ getCategoryName(expense.category) }}
3030
</v-chip>
3131
<span class="text-caption text-medium-emphasis">
3232
{{ new Date(expense.date).toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }) }}
@@ -57,11 +57,12 @@
5757

5858
<script setup lang="ts">
5959
import { computed } from 'vue'
60+
import { useCategories, type CategoryKey } from '../composables/useCategories'
6061
6162
interface Expense {
6263
id: string
6364
amount: number
64-
category: string
65+
category: CategoryKey
6566
date: string
6667
note?: string
6768
}
@@ -77,24 +78,7 @@ interface Emits {
7778
const props = defineProps<Props>()
7879
const emit = defineEmits<Emits>()
7980
80-
const categoryNames: Record<string, string> = {
81-
Food: '餐饮',
82-
Transport: '交通',
83-
Shopping: '购物',
84-
Entertainment: '娱乐',
85-
Other: '其他'
86-
}
87-
88-
const getCategoryColor = (category: string) => {
89-
const colors: Record<string, string> = {
90-
Food: 'orange',
91-
Transport: 'blue',
92-
Shopping: 'pink',
93-
Entertainment: 'purple',
94-
Other: 'grey'
95-
}
96-
return colors[category] || 'grey'
97-
}
81+
const { getCategoryName, getCategoryColor } = useCategories()
9882
9983
const formatAmount = (amount: number) => {
10084
return new Intl.NumberFormat('zh-CN', {

src/components/TodaySummary.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111

1212
<script setup lang="ts">
1313
import { computed } from 'vue'
14+
import { type CategoryKey } from '../composables/useCategories'
1415
1516
interface Expense {
1617
id: string
1718
amount: number
18-
category: string
19+
category: CategoryKey
1920
date: string
2021
note?: string
2122
}

src/components/charts/CategoryChart.vue

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import {
2222
Legend,
2323
DoughnutController
2424
} from 'chart.js'
25+
import { useCategories, type CategoryKey } from '../../composables/useCategories'
2526
2627
Chart.register(ArcElement, Title, Tooltip, Legend, DoughnutController)
2728
2829
interface Expense {
2930
id: string
3031
amount: number
31-
category: string
32+
category: CategoryKey
3233
date: string
3334
note?: string
3435
}
@@ -41,13 +42,7 @@ const props = defineProps<Props>()
4142
const chartRef = ref<HTMLCanvasElement>()
4243
let chart: Chart | null = null
4344
44-
const categoryNames: Record<string, string> = {
45-
Food: '餐饮',
46-
Transport: '交通',
47-
Shopping: '购物',
48-
Entertainment: '娱乐',
49-
Other: '其他'
50-
}
45+
const { getCategoryName, getCategoryChartColor } = useCategories()
5146
5247
const formatAmount = (amount: number) => {
5348
return new Intl.NumberFormat('zh-CN', {
@@ -57,7 +52,7 @@ const formatAmount = (amount: number) => {
5752
}
5853
5954
const categoryBreakdown = computed(() => {
60-
const breakdown: Record<string, { amount: number; count: number; percentage: number }> = {}
55+
const breakdown: Record<CategoryKey, { amount: number; count: number; percentage: number }> = {} as any
6156
6257
props.expenses.forEach(expense => {
6358
if (!breakdown[expense.category]) {
@@ -69,14 +64,16 @@ const categoryBreakdown = computed(() => {
6964
7065
const total = props.expenses.reduce((sum, exp) => sum + exp.amount, 0)
7166
Object.keys(breakdown).forEach(category => {
72-
breakdown[category].percentage = total > 0 ? (breakdown[category].amount / total) * 100 : 0
67+
const cat = category as CategoryKey
68+
breakdown[cat].percentage = total > 0 ? (breakdown[cat].amount / total) * 100 : 0
7369
})
7470
75-
const sortedBreakdown: Record<string, any> = {}
71+
// Sort by amount descending
72+
const sortedBreakdown: Record<CategoryKey, any> = {} as any
7673
Object.entries(breakdown)
7774
.sort(([,a], [,b]) => b.amount - a.amount)
7875
.forEach(([category, data]) => {
79-
sortedBreakdown[category] = data
76+
sortedBreakdown[category as CategoryKey] = data
8077
})
8178
8279
return sortedBreakdown
@@ -87,18 +84,18 @@ const total = computed(() => props.expenses.reduce((sum, exp) => sum + exp.amoun
8784
const createChart = () => {
8885
if (!chartRef.value) return
8986
90-
const categories = Object.keys(categoryBreakdown.value)
87+
const categories = Object.keys(categoryBreakdown.value) as CategoryKey[]
9188
const amounts = categories.map(cat => categoryBreakdown.value[cat].amount)
92-
const colors = categories.map(cat => {
93-
const colorMap: Record<string, string> = {
94-
Food: '#FF9800',
95-
Transport: '#2196F3',
96-
Shopping: '#E91E63',
97-
Entertainment: '#9C27B0',
98-
Other: '#607D8B'
89+
const colors = categories.map(cat => getCategoryChartColor(cat))
90+
91+
// Don't create chart if no data
92+
if (categories.length === 0 || amounts.every(amount => amount === 0)) {
93+
if (chart) {
94+
chart.destroy()
95+
chart = null
9996
}
100-
return colorMap[cat] || '#607D8B'
101-
})
97+
return
98+
}
10299
103100
if (chart) {
104101
chart.destroy()
@@ -107,7 +104,7 @@ const createChart = () => {
107104
chart = new Chart(chartRef.value, {
108105
type: 'doughnut',
109106
data: {
110-
labels: categories.map(cat => categoryNames[cat]),
107+
labels: categories.map(cat => getCategoryName(cat)),
111108
datasets: [{
112109
data: amounts,
113110
backgroundColor: colors,

src/components/charts/CategoryDetails.vue

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
>
1414
<template v-slot:prepend>
1515
<v-avatar
16-
:color="getCategoryColor(category)"
16+
:color="getCategoryColor(category as CategoryKey)"
1717
size="small"
1818
class="mr-3"
1919
>
@@ -24,7 +24,7 @@
2424
</template>
2525

2626
<v-list-item-title class="font-weight-medium">
27-
{{ categoryNames[category] }}
27+
{{ getCategoryName(category as CategoryKey) }}
2828
</v-list-item-title>
2929

3030
<template v-slot:append>
@@ -41,11 +41,12 @@
4141

4242
<script setup lang="ts">
4343
import { computed } from 'vue'
44+
import { useCategories, type CategoryKey } from '../../composables/useCategories'
4445
4546
interface Expense {
4647
id: string
4748
amount: number
48-
category: string
49+
category: CategoryKey
4950
date: string
5051
note?: string
5152
}
@@ -56,24 +57,7 @@ interface Props {
5657
5758
const props = defineProps<Props>()
5859
59-
const categoryNames: Record<string, string> = {
60-
Food: '餐饮',
61-
Transport: '交通',
62-
Shopping: '购物',
63-
Entertainment: '娱乐',
64-
Other: '其他'
65-
}
66-
67-
const getCategoryColor = (category: string) => {
68-
const colors: Record<string, string> = {
69-
Food: 'orange',
70-
Transport: 'blue',
71-
Shopping: 'pink',
72-
Entertainment: 'purple',
73-
Other: 'grey'
74-
}
75-
return colors[category] || 'grey'
76-
}
60+
const { getCategoryName, getCategoryColor } = useCategories()
7761
7862
const formatAmount = (amount: number) => {
7963
return new Intl.NumberFormat('zh-CN', {
@@ -83,7 +67,7 @@ const formatAmount = (amount: number) => {
8367
}
8468
8569
const categoryBreakdown = computed(() => {
86-
const breakdown: Record<string, { amount: number; count: number; percentage: number }> = {}
70+
const breakdown: Record<CategoryKey, { amount: number; count: number; percentage: number }> = {} as any
8771
8872
props.expenses.forEach(expense => {
8973
if (!breakdown[expense.category]) {
@@ -95,14 +79,16 @@ const categoryBreakdown = computed(() => {
9579
9680
const total = props.expenses.reduce((sum, exp) => sum + exp.amount, 0)
9781
Object.keys(breakdown).forEach(category => {
98-
breakdown[category].percentage = total > 0 ? (breakdown[category].amount / total) * 100 : 0
82+
const cat = category as CategoryKey
83+
breakdown[cat].percentage = total > 0 ? (breakdown[cat].amount / total) * 100 : 0
9984
})
10085
101-
const sortedBreakdown: Record<string, any> = {}
86+
// Sort by amount descending
87+
const sortedBreakdown: Record<CategoryKey, any> = {} as any
10288
Object.entries(breakdown)
10389
.sort(([,a], [,b]) => b.amount - a.amount)
10490
.forEach(([category, data]) => {
105-
sortedBreakdown[category] = data
91+
sortedBreakdown[category as CategoryKey] = data
10692
})
10793
10894
return sortedBreakdown

src/components/charts/PeriodFilter.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,12 @@
4242

4343
<script setup lang="ts">
4444
import { computed } from 'vue'
45+
import { type CategoryKey } from '../../composables/useCategories'
4546
4647
interface Expense {
4748
id: string
4849
amount: number
49-
category: string
50+
category: CategoryKey
5051
date: string
5152
note?: string
5253
}

src/components/charts/TrendChart.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ import {
2424
Tooltip,
2525
LineController
2626
} from 'chart.js'
27+
import { type CategoryKey } from '../../composables/useCategories'
2728
2829
Chart.register(CategoryScale, LinearScale, LineElement, PointElement, Title, Tooltip, LineController)
2930
3031
interface Expense {
3132
id: string
3233
amount: number
33-
category: string
34+
category: CategoryKey
3435
date: string
3536
note?: string
3637
}

0 commit comments

Comments
 (0)