From ce4bca827248ad5d8b03321b0573badf64eaa7a3 Mon Sep 17 00:00:00 2001 From: MaysWind Date: Sun, 8 Jun 2025 02:10:11 +0800 Subject: [PATCH] code refactor --- jest.config.ts | 50 ++-- package.json | 2 +- pkg/core/fiscalyear.go | 52 +++-- pkg/core/fiscalyear_test.go | 3 +- .../base/FiscalYearStartSelectionBase.ts | 125 +++++----- .../desktop/FiscalYearStartSelect.vue | 64 +++--- .../mobile/FiscalYearStartSelectionSheet.vue | 69 +++--- src/core/fiscalyear.ts | 216 +++++------------- src/lib/__tests__/fiscal_year.ts | 88 ++++--- src/lib/datetime.ts | 101 ++++---- src/locales/helpers.ts | 44 ++-- src/views/mobile/users/UserProfilePage.vue | 7 +- tsconfig.jest.json | 7 + tsconfig.json | 3 +- 14 files changed, 364 insertions(+), 467 deletions(-) create mode 100644 tsconfig.jest.json diff --git a/jest.config.ts b/jest.config.ts index b1c97e05..08b9a939 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,37 +1,21 @@ -/** - * For a detailed explanation regarding each configuration property, visit: - * https://jestjs.io/docs/configuration - */ +import { type JestConfigWithTsJest, createDefaultEsmPreset } from 'ts-jest'; -import type { Config } from 'jest'; - -const config: Config = { - - // Automatically clear mock calls, instances, contexts and results before every test - clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - collectCoverage: false, - - // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - moduleNameMapper: { - "^@/(.*)$": "/src/$1" - }, - - // The test environment that will be used for testing - testEnvironment: "node", - - // The glob patterns Jest uses to detect test files - testMatch: [ - "**/__tests__/**/*.[jt]s?(x)", - "!**/__tests__/*_gen.[jt]s?(x)" - ], - - // A map from regular expressions to paths to transformers - transform: { - '^.+\\.m?tsx?$': ['ts-jest', { useESM: true, isolatedModules: true }], - }, +const presetConfig = createDefaultEsmPreset({ + tsconfig: '/tsconfig.jest.json' +}); +const config: JestConfigWithTsJest = { + ...presetConfig, + clearMocks: true, + collectCoverage: false, + moduleNameMapper: { + "^@/(.*)$": "/src/$1" + }, + testEnvironment: "node", + testMatch: [ + "**/__tests__/**/*.[jt]s?(x)", + "!**/__tests__/*_gen.[jt]s?(x)" + ] }; -module.exports = config; +export default config; diff --git a/package.json b/package.json index b78b4e5f..73759060 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "build": "cross-env NODE_ENV=production vite build", "serve:dist": "vite preview", "lint": "tsc --noEmit && eslint . --fix", - "test": "jest" + "test": "cross-env TS_NODE_PROJECT=\"./tsconfig.jest.json\" jest" }, "dependencies": { "@mdi/js": "^7.4.47", diff --git a/pkg/core/fiscalyear.go b/pkg/core/fiscalyear.go index bcbfa390..8c06264e 100644 --- a/pkg/core/fiscalyear.go +++ b/pkg/core/fiscalyear.go @@ -17,11 +17,25 @@ const ( FISCAL_YEAR_START_INVALID FiscalYearStart = 0xFFFF // Invalid ) +var MONTH_MAX_DAYS = []uint8{ + uint8(31), // January + uint8(28), // February (Disallow fiscal year start on leap day) + uint8(31), // March + uint8(30), // April + uint8(31), // May + uint8(30), // June + uint8(31), // July + uint8(31), // August + uint8(30), // September + uint8(31), // October + uint8(30), // November + uint8(31), // December +} + // NewFiscalYearStart creates a new FiscalYearStart from month and day values func NewFiscalYearStart(month uint8, day uint8) (FiscalYearStart, error) { - month, day, err := validateMonthDay(month, day) - if err != nil { - return 0, err + if !isValidMonthDay(month, day) { + return 0, errs.ErrFormatInvalid } return FiscalYearStart(uint16(month)<<8 | uint16(day)), nil @@ -37,39 +51,27 @@ func (f FiscalYearStart) GetMonthDay() (uint8, uint8, error) { month := uint8(f >> 8) day := uint8(f & 0xFF) - return validateMonthDay(month, day) + if !isValidMonthDay(month, day) { + return 0, 0, errs.ErrFormatInvalid + } + + return month, day, nil } // String returns a string representation of FiscalYearStart in MM/DD format func (f FiscalYearStart) String() string { month, day, err := f.GetMonthDay() + if err != nil { return "Invalid" } + return fmt.Sprintf("%02d-%02d", month, day) } -// validateMonthDay validates a month and day and returns them if valid -func validateMonthDay(month uint8, day uint8) (uint8, uint8, error) { - if month < 1 || month > 12 || day < 1 { - return 0, 0, errs.ErrFormatInvalid - } - - maxDays := uint8(31) - switch month { - case 1, 3, 5, 7, 8, 10, 12: // January, March, May, July, August, October, December - maxDays = 31 - case 4, 6, 9, 11: // April, June, September, November - maxDays = 30 - case 2: // February - maxDays = 28 // Disallow fiscal year start on leap day - } - - if day > maxDays { - return 0, 0, errs.ErrFormatInvalid - } - - return month, day, nil +// isValidMonthDay returns whether the specified month and day is valid +func isValidMonthDay(month uint8, day uint8) bool { + return uint8(1) <= month && month <= uint8(12) && uint8(1) <= day && day <= MONTH_MAX_DAYS[int(month)-1] } // FiscalYearFormat represents the fiscal year format as a uint8 diff --git a/pkg/core/fiscalyear_test.go b/pkg/core/fiscalyear_test.go index 9cffd1b3..2dfe1df7 100644 --- a/pkg/core/fiscalyear_test.go +++ b/pkg/core/fiscalyear_test.go @@ -3,8 +3,9 @@ package core import ( "testing" - "github.com/mayswind/ezbookkeeping/pkg/errs" "github.com/stretchr/testify/assert" + + "github.com/mayswind/ezbookkeeping/pkg/errs" ) func TestNewFiscalYearStart_ValidMonthDay(t *testing.T) { diff --git a/src/components/base/FiscalYearStartSelectionBase.ts b/src/components/base/FiscalYearStartSelectionBase.ts index 4a4ba210..b221fd95 100644 --- a/src/components/base/FiscalYearStartSelectionBase.ts +++ b/src/components/base/FiscalYearStartSelectionBase.ts @@ -1,88 +1,95 @@ -import { computed } from 'vue'; +import { ref, computed } from 'vue'; import { FiscalYearStart } from '@/core/fiscalyear.ts'; - -import { useUserStore } from '@/stores/user.ts'; +import { arrangeArrayWithNewStartIndex } from '@/lib/common.ts'; import { useI18n } from '@/locales/helpers.ts'; -import { arrangeArrayWithNewStartIndex } from '@/lib/common'; +import { useUserStore } from '@/stores/user.ts'; -export interface FiscalYearStartSelectionBaseProps { +export interface CommonFiscalYearStartSelectionProps { modelValue?: number; + disabled?: boolean; + readonly?: boolean; + label?: string; } -export interface FiscalYearStartSelectionBaseEmits { +export interface CommonFiscalYearStartSelectionEmits { (e: 'update:modelValue', value: number): void; } -export function useFiscalYearStartSelectionBase(props: FiscalYearStartSelectionBaseProps, emit: FiscalYearStartSelectionBaseEmits) { - const { getAllMinWeekdayNames, formatMonthDayToLongDay, getCurrentFiscalYearStartFormatted } = useI18n(); +function getFiscalYearStartFromProps(props: CommonFiscalYearStartSelectionProps): number { + if (!props.modelValue) { + return FiscalYearStart.Default.value; + } - const dayNames = computed(() => arrangeArrayWithNewStartIndex(getAllMinWeekdayNames(), firstDayOfWeek.value)); + const fiscalYearStart = FiscalYearStart.valueOf(props.modelValue); - const displayName = computed(() => { - const fy = FiscalYearStart.fromNumber(selectedFiscalYearStart.value); + if (!fiscalYearStart) { + return FiscalYearStart.Default.value; + } - if ( fy ) { - return formatMonthDayToLongDay(fy.toMonthDashDayString()) - } + return fiscalYearStart.value; +} - return formatMonthDayToLongDay(FiscalYearStart.strictFromNumber(userStore.currentUserFiscalYearStart).toMonthDashDayString()); - - }); - - const disabledDates = (date: Date) => { - // Disable February 29 (leap day) - return date.getMonth() === 1 && date.getDate() === 29; - }; - - const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); - - const selectedFiscalYearStart = computed(() => { - return props.modelValue !== undefined ? props.modelValue : userStore.currentUserFiscalYearStart; - }); +export function useFiscalYearStartSelectionBase(props: CommonFiscalYearStartSelectionProps) { + const { + getAllMinWeekdayNames, + formatMonthDayToLongDay + } = useI18n(); const userStore = useUserStore(); - function selectedDisplayName(dateString: string): string { - const fy = FiscalYearStart.fromMonthDashDayString(dateString); - if ( fy ) { - return formatMonthDayToLongDay(fy.toMonthDashDayString()); + const disabledDates = (date: Date) => { + // Disable February 29 (leap day) + return date.getMonth() === 1 && date.getDate() === 29; + }; + + const selectedFiscalYearStart = ref(getFiscalYearStartFromProps(props)); + + const selectedFiscalYearStartValue = computed({ + get: () => { + const fiscalYearStart = FiscalYearStart.valueOf(selectedFiscalYearStart.value); + + if (fiscalYearStart) { + return fiscalYearStart.toMonthDashDayString(); + } else { + return FiscalYearStart.Default.toMonthDashDayString(); + } + }, + set: (value: string) => { + const fiscalYearStart = FiscalYearStart.parse(value); + + if (fiscalYearStart) { + selectedFiscalYearStart.value = fiscalYearStart.value; + } else { + selectedFiscalYearStart.value = FiscalYearStart.Default.value; + } } - return displayName.value; - } + }); - function getModelValueToDateString(): string { - const input = selectedFiscalYearStart.value; - - const fy = FiscalYearStart.fromNumber(input); + const displayFiscalYearStartDate = computed(() => { + let fiscalYearStart = FiscalYearStart.valueOf(selectedFiscalYearStart.value); - if ( fy ) { - return fy.toMonthDashDayString(); + if (!fiscalYearStart) { + fiscalYearStart = FiscalYearStart.Default; } - return getCurrentFiscalYearStartFormatted(); - } + return formatMonthDayToLongDay(fiscalYearStart.toMonthDashDayString()); + }); + + const firstDayOfWeek = computed(() => userStore.currentUserFirstDayOfWeek); + const dayNames = computed(() => arrangeArrayWithNewStartIndex(getAllMinWeekdayNames(), firstDayOfWeek.value)); - function getDateStringToModelValue(input: string): number { - const fyString = FiscalYearStart.fromMonthDashDayString(input); - if (fyString) { - return fyString.value; - } - return userStore.currentUserFiscalYearStart; - } - return { - // functions - getDateStringToModelValue, - getModelValueToDateString, - selectedDisplayName, - // computed states - dayNames, - displayName, + // constants disabledDates, - firstDayOfWeek, + // states, selectedFiscalYearStart, - } + // computed states + selectedFiscalYearStartValue, + displayFiscalYearStartDate, + firstDayOfWeek, + dayNames + }; } diff --git a/src/components/desktop/FiscalYearStartSelect.vue b/src/components/desktop/FiscalYearStartSelect.vue index b057e0a0..88814875 100644 --- a/src/components/desktop/FiscalYearStartSelect.vue +++ b/src/components/desktop/FiscalYearStartSelect.vue @@ -3,18 +3,16 @@ persistent-placeholder :readonly="readonly" :disabled="disabled" - :clearable="modelValue ? clearable : false" :label="label" :menu-props="{ contentClass: 'fiscal-year-start-select-menu' }" - v-model="selectedDate" + v-model="selectedFiscalYearStartValue" >