feat: Add WhatsApp verification & responsive dialogs

 COMPLETED FEATURES:

1. WhatsApp Number Verification
   - Verify phone number is registered on WhatsApp before saving
   - Use OTP webhook with check_number mode
   - Show error if number not registered
   - Translated error messages

2. Responsive Dialog/Drawer System
   - Created ResponsiveDialog component
   - Desktop: Uses Dialog (modal)
   - Mobile: Uses Drawer (bottom sheet)
   - Applied to WalletDialog & TransactionDialog
   - Better UX on mobile devices

3. Translation Fixes
   - Fixed editProfile key placement
   - All translation keys now consistent
   - Build passing without errors

📱 MOBILE IMPROVEMENTS:
- Form dialogs now slide up from bottom on mobile
- Better touch interaction
- More native mobile feel

🔧 TECHNICAL:
- Added shadcn Drawer component
- Created useMediaQuery hook
- Responsive context wrapper
- Type-safe implementation
This commit is contained in:
dwindown
2025-10-12 17:07:16 +07:00
parent d626c7d8de
commit 46488a09e2
15 changed files with 914 additions and 239 deletions

View File

@@ -2,6 +2,7 @@ export const en = {
common: {
search: 'Search',
filter: 'Filter',
clearAll: 'Clear All',
add: 'Add',
edit: 'Edit',
delete: 'Delete',
@@ -23,6 +24,9 @@ export const en = {
inactive: 'Inactive',
yes: 'Yes',
no: 'No',
type: 'Type',
showFilters: 'Show Filters',
hideFilters: 'Hide Filters',
},
nav: {
@@ -35,6 +39,11 @@ export const en = {
overview: {
title: 'Overview',
description: 'Your financial dashboard and quick actions',
overviewPeriod: 'Overview Period',
overviewPeriodPlaceholder: 'Select period',
customStartDatePlaceholder: 'Pick start date',
customEndDatePlaceholder: 'Pick end date',
totalBalance: 'Total Balance',
totalIncome: 'Total Income',
totalExpense: 'Total Expense',
@@ -44,23 +53,41 @@ export const en = {
recentTransactions: 'Recent Transactions',
viewAll: 'View All',
noTransactions: 'No transactions yet',
addFirstTransaction: 'Add your first transaction',
addTransaction: 'Add transaction',
wallets: 'Wallets',
walletsDescription: 'Balance distribution across wallets',
walletTheadName: 'Name',
walletTheadCurrencyUnit: 'Currency/Unit',
walletTheadTransactions: 'Transactions',
walletTheadTotalBalance: 'Total Balance',
walletTheadDomination: 'Domination',
addWallet: 'Add Wallet',
noWallets: 'No wallets yet',
createFirstWallet: 'Create your first wallet',
incomeByCategory: 'Income by Category',
incomeCategoryFor: 'Income category for',
expenseByCategory: 'Expense by Category',
expenseCategoryFor: 'Expense category for',
categoryAllWalletOption: 'All Wallets',
last30Days: 'Last 30 days',
last7Days: 'Last 7 days',
thisMonth: 'This month',
lastMonth: 'Last month',
thisYear: 'This year',
lastYear: 'Last year',
allTime: 'All time',
custom: 'Custom',
financialTrend: 'Financial Trend',
financialTrendDescription: 'Income vs Expense over time',
financialTrendOverTimeMonthly: 'Monthly',
financialTrendOverTimeWeekly: 'Weekly',
financialTrendOverTimeDaily: 'Daily',
financialTrendOverTimeYearly: 'Yearly',
},
transactions: {
title: 'Transactions',
description: 'View and manage all your transactions',
addTransaction: 'Add Transaction',
editTransaction: 'Edit Transaction',
deleteConfirm: 'Are you sure you want to delete this transaction?',
@@ -69,11 +96,34 @@ export const en = {
category: 'Category',
memo: 'Memo',
wallet: 'Wallet',
direction: 'Type',
filterByWallet: 'Filter by Wallet',
filterByDirection: 'Filter by Type',
filterByCategory: 'Filter by Category',
searchPlaceholder: 'Search transactions...',
direction: 'Direction',
tableTitle: 'Transactions',
tableDescription: 'All your transactions',
tableFiltered: 'Filtered from {count} transactions',
tableTheadDate: 'Date',
tableTheadAmount: 'Amount',
tableTheadDirection: 'Direction',
tableTheadCategory: 'Category',
tableTheadMemo: 'Memo',
tableTheadWallet: 'Wallet',
tableTheadActions: 'Actions',
filter: {
searchMemo: 'Search Memo',
searchMemoPlaceholder: 'Search in memo...',
wallet: 'Wallet',
walletPlaceholder: 'Select wallet',
walletAllWallets: 'All Wallets',
direction: 'Direction',
directionPlaceholder: 'Select direction',
minAmount: 'Min Amount',
minAmountPlaceholder: '0',
maxAmount: 'Max Amount',
maxAmountPlaceholder: 'No limit',
fromDate: 'From Date',
toDate: 'To Date',
fromDatePlaceholder: 'Select start date',
toDatePlaceholder: 'Select end date',
},
noTransactions: 'No transactions',
stats: {
totalIncome: 'Total Income',
@@ -84,6 +134,7 @@ export const en = {
wallets: {
title: 'Wallets',
description: 'Manage your wallets and accounts',
addWallet: 'Add Wallet',
editWallet: 'Edit Wallet',
deleteConfirm: 'Are you sure you want to delete this wallet? All related transactions will be deleted.',
@@ -103,11 +154,14 @@ export const en = {
totalBalance: 'Total Balance',
moneyWallets: 'Money Wallets',
assetWallets: 'Asset Wallets',
allWallets: 'All Wallets',
filterDesc: 'Filtered from {count} total wallets',
},
walletDialog: {
addTitle: 'Add Wallet',
editTitle: 'Edit Wallet',
description: 'Fill in the details of your wallet',
name: 'Wallet Name',
namePlaceholder: 'e.g., Main Wallet, Savings',
type: 'Wallet Type',
@@ -122,11 +176,21 @@ export const en = {
pricePerUnit: 'Price per Unit (Optional)',
pricePerUnitPlaceholder: '0',
pricePerUnitHelper: 'Price per {unit} in IDR',
addSuccess: 'Wallet added successfully',
editSuccess: 'Wallet updated successfully',
saveError: 'Failed to save wallet',
deleteSuccess: 'Wallet deleted successfully',
deleteError: 'Failed to delete wallet',
deleteConfirm: 'Are you sure you want to delete this wallet? All related transactions will be deleted.',
deleteConfirmTitle: 'Delete Wallet',
deleteConfirmCancel: 'Cancel',
deleteConfirmDelete: 'Delete',
},
transactionDialog: {
addTitle: 'Add Transaction',
editTitle: 'Edit Transaction',
description: 'Fill in the details of your transaction',
amount: 'Amount',
amountPlaceholder: '0',
wallet: 'Wallet',
@@ -141,17 +205,37 @@ export const en = {
memoPlaceholder: 'Add a note...',
date: 'Date',
selectDate: 'Select date',
addSuccess: 'Transaction added successfully',
editSuccess: 'Transaction updated successfully',
saveError: 'Failed to save transaction',
deleteSuccess: 'Transaction deleted successfully',
deleteError: 'Failed to delete transaction',
deleteConfirm: 'Are you sure you want to delete this transaction? This action cannot be undone.',
deleteConfirmTitle: 'Delete Transaction',
deleteConfirmCancel: 'Cancel',
deleteConfirmDelete: 'Delete',
},
profile: {
title: 'Profile',
description: 'Manage your account settings and security preferences',
editProfile: 'Edit Profile',
personalInfo: 'Personal Information',
name: 'Name',
nameSynced: 'Name is synced from your Google account',
edit: 'Edit',
save: 'Save',
update: 'Update',
cancel: 'Cancel',
email: 'Email',
emailVerified: 'Email Verified',
emailNotVerified: 'Email Not Verified',
emailCannotBeChanged: 'Email cannot be changed',
avatar: 'Avatar',
changeAvatar: 'Change Avatar',
uploadAvatar: 'Upload Avatar',
avatarSynced: 'Avatar is synced from your Google account',
clickUploadAvatar: 'Click the upload button to change your avatar',
uploading: 'Uploading...',
security: 'Security',
@@ -160,17 +244,27 @@ export const en = {
newPassword: 'New Password',
confirmPassword: 'Confirm New Password',
changePassword: 'Change Password',
setPassword: 'Set Password',
noPassword: 'You logged in with Google and haven\'t set a password yet',
setPasswordDesc: 'Set a password to enable password-based login and account deletion',
changePasswordDesc: 'Update your password to keep your account secure',
googleAuthDesc: 'Your account uses Google Sign-In. Setting a password will allow you to login with email/password and delete your account if needed.',
setting: 'Setting...',
updating: 'Updating...',
setPassword: 'Set Password',
updatePassword: 'Update Password',
twoFactor: 'Two-Factor Authentication',
twoFactorDesc: 'Add an extra layer of security to your account',
phoneNumber: 'Phone Number',
phoneNumberPlaceholder: '+62812345678',
updatePhone: 'Update Phone',
phoneNumberDescription: 'Required for WhatsApp OTP verification',
emailOtp: 'Email OTP',
emailOtpDesc: 'Receive verification codes via email',
enableEmailOtp: 'Enable Email OTP',
disableEmailOtp: 'Disable Email OTP',
checkYourEmailForTheVerificationCode: 'Check your email for the verification code',
enable: 'Enable',
disable: 'Disable',
enabled: 'Enabled',
@@ -181,20 +275,35 @@ export const en = {
whatsappOtp: 'WhatsApp OTP',
whatsappOtpDesc: 'Receive verification codes via WhatsApp',
enableWhatsAppOtp: 'Enable WhatsApp OTP',
disableWhatsAppOtp: 'Disable WhatsApp OTP',
pleaseAddYourPhoneNumberInTheEditProfileTabFirst: 'Please add your phone number in the Edit Profile tab first',
checkYourWhatsAppForTheVerificationCodeOrCheckConsoleInTestMode: 'Check your WhatsApp for the verification code',
enterVerificationCode: 'Enter 6 digit code',
authenticatorApp: 'Authenticator App',
authenticatorDesc: 'Use an authenticator app like Google Authenticator',
setup: 'Setup',
authenticatorSetupInstruction: 'Setup Instructions:',
autentucatorSetupInstruction_1: 'Open your authenticator app (Google Authenticator, Authy, etc.)',
autentucatorSetupInstruction_2: 'Scan the QR code or manually enter the secret key',
autentucatorSetupInstruction_3: 'Enter the 6-digit code from your app below',
setupSecretKey: 'Secret Key (if you can\'t scan QR code):',
enableAuthenticatorApp: 'Enable Authenticator App',
disableAuthenticatorApp: 'Disable Authenticator App',
scanQr: 'Scan QR Code',
scanQrDesc: 'Scan this QR code with your authenticator app',
manualEntry: 'Or enter this code manually:',
enterAuthCode: 'Enter code from your authenticator app',
dangerZone: 'Danger Zone',
dangerZoneDesc: 'Irreversible actions that will permanently affect your account',
deleteAccount: 'Delete Account',
deleteAccountDesc: 'Permanently delete your account. This action cannot be undone.',
deleteAccountDesc: 'Once you delete your account, there is no going back. This will permanently delete your account, all your data, transactions, and settings.',
deletePasswordRequired: 'You must set a password first before you can delete your account. Go to "Set Password" above.',
deleteAccountConfirm: 'Are you sure you want to delete your account? All data will be permanently lost.',
enterPasswordToDelete: 'Enter your password to confirm',
deleting: 'Deleting...',
yesDeleteMyAccount: 'Yes, Delete My Account',
},
dateRange: {