import { computed, ref, shallowRef, watch, type Ref } from 'vue' import { ElMessage } from 'element-plus' import { languageValues, regionValues, roleValues, type LanguageValue, type RegionValue, type RoleValue, type SecurityDraft, type SettingsData } from './types' type Translate = (key: string, ...args: unknown[]) => string interface UseSettingsFormOptions { t: Translate locale: Ref } const languageLocaleMap: Record = { english: 'ENGLISH', chinese: 'CHINESE_SIMPLIFIED' } const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ const createDefaultData = (): SettingsData => ({ firstName: 'Alexandra', lastName: 'Chen', email: 'alex.chen@gmail.com', username: '@alexandra_chen', role: ['student', 'graphicDesigner'], language: 'english', region: 'hongKongSar' }) const cloneSettingsData = (data: SettingsData): SettingsData => ({ firstName: data.firstName, lastName: data.lastName, email: data.email, username: data.username, role: [...data.role], language: data.language, region: data.region }) const createEmptySecurityDraft = (): SecurityDraft => ({ newEmail: '', newPassword: '', currentPassword: '' }) export function useSettingsForm({ t, locale }: UseSettingsFormOptions) { const sourceData = ref(createDefaultData()) const draftData = ref(cloneSettingsData(sourceData.value)) const securityDraft = ref(createEmptySecurityDraft()) const isEditing = shallowRef(false) const saving = shallowRef(false) const isVerificationDialogVisible = shallowRef(false) const verificationTargetEmail = shallowRef('') const verifiedEmail = shallowRef('') const roleList = computed(() => roleValues.map((value) => ({ name: t(`Settings.roles.${value}`), value })) ) const languageList = computed(() => languageValues.map((value) => ({ label: t(`Settings.languages.${value}`), value })) ) const regionList = computed(() => regionValues.map((value) => ({ label: t(`Settings.regions.${value}`), value })) ) const displayData = computed(() => (isEditing.value ? draftData.value : sourceData.value)) const normalizedNewEmail = computed(() => securityDraft.value.newEmail.trim()) const hasNewEmailChange = computed( () => normalizedNewEmail.value.length > 0 && normalizedNewEmail.value !== sourceData.value.email ) const isEmailVerified = computed( () => hasNewEmailChange.value && verifiedEmail.value === normalizedNewEmail.value ) const displayLanguageLabel = computed(() => t(`Settings.languages.${displayData.value.language}`)) const displayRegionLabel = computed(() => t(`Settings.regions.${displayData.value.region}`)) const fullName = computed(() => { const data = displayData.value return `${data.firstName} ${data.lastName}`.trim() }) const roleModel = computed({ get: () => displayData.value.role, set: (value) => { if (isEditing.value) { draftData.value.role = value return } sourceData.value.role = value } }) const resetEmailVerificationState = () => { isVerificationDialogVisible.value = false verificationTargetEmail.value = '' verifiedEmail.value = '' } const syncAppLanguage = (language: LanguageValue) => { const nextLocale = languageLocaleMap[language] locale.value = nextLocale localStorage.setItem('language', nextLocale) } const resetDraftState = () => { draftData.value = cloneSettingsData(sourceData.value) securityDraft.value = createEmptySecurityDraft() resetEmailVerificationState() } const handleEdit = () => { resetDraftState() isEditing.value = true } const resetSecurityEmail = () => { securityDraft.value.newEmail = '' resetEmailVerificationState() } const resetSecurityPassword = () => { securityDraft.value.newPassword = '' securityDraft.value.currentPassword = '' } const handleDiscard = () => { resetDraftState() isEditing.value = false } const closeVerificationDialog = () => { isVerificationDialogVisible.value = false verificationTargetEmail.value = '' } const handleVerifyEmail = () => { const nextEmail = normalizedNewEmail.value if (!nextEmail) { ElMessage.warning(t('Settings.messages.enterNewEmailFirst')) return } if (!emailPattern.test(nextEmail)) { ElMessage.warning(t('Settings.messages.invalidEmail')) return } if (nextEmail === sourceData.value.email) { ElMessage.warning(t('Settings.messages.sameEmail')) return } if (verifiedEmail.value === nextEmail) { ElMessage.success(t('Settings.messages.alreadyVerified')) return } verificationTargetEmail.value = nextEmail handleSendVerifyCode() isVerificationDialogVisible.value = true } const handleSendVerifyCode = () => { ElMessage.success(t('Settings.messages.verificationCodeSent')) } const handleVerificationSubmit = (code: string) => { if (code.length !== 6) { ElMessage.warning(t('Settings.messages.enterVerificationCode')) return } verifiedEmail.value = verificationTargetEmail.value closeVerificationDialog() ElMessage.success(t('Settings.messages.verificationCompleted')) } const buildNextData = (): SettingsData => { const nextEmail = securityDraft.value.newEmail.trim() || draftData.value.email return { firstName: draftData.value.firstName.trim(), lastName: draftData.value.lastName.trim(), username: draftData.value.username.trim(), email: nextEmail, role: [...draftData.value.role], language: draftData.value.language, region: draftData.value.region } } const handleSave = async () => { if (hasNewEmailChange.value && !isEmailVerified.value) { ElMessage.warning(t('Settings.messages.verifyEmailBeforeSave')) return } const nextData = buildNextData() const previousLanguage = sourceData.value.language saving.value = true try { sourceData.value = cloneSettingsData(nextData) if (nextData.language !== previousLanguage) { syncAppLanguage(nextData.language) } draftData.value = cloneSettingsData(sourceData.value) securityDraft.value = createEmptySecurityDraft() resetEmailVerificationState() isEditing.value = false ElMessage.success(t('Settings.messages.settingsUpdated')) } catch (error) { console.warn(error) } finally { saving.value = false } } watch( () => securityDraft.value.newEmail, (value) => { const trimmedValue = value.trim() if (verifiedEmail.value && trimmedValue !== verifiedEmail.value) { verifiedEmail.value = '' } if ( isVerificationDialogVisible.value && verificationTargetEmail.value && trimmedValue !== verificationTargetEmail.value ) { closeVerificationDialog() } } ) return { sourceData, draftData, securityDraft, isEditing, saving, isVerificationDialogVisible, verificationTargetEmail, roleList, languageList, regionList, displayData, isEmailVerified, displayLanguageLabel, displayRegionLabel, fullName, roleModel, handleEdit, handleDiscard, handleSave, resetSecurityEmail, resetSecurityPassword, handleVerifyEmail, handleSendVerifyCode, handleVerificationSubmit, closeVerificationDialog } }