Add lock files for package management and update architecture documentation

- Introduced bun.lock and package-lock.json to manage dependencies for the project.
- Enhanced backend API architecture documentation with additional security and documentation guidelines.
- Made minor formatting adjustments across various files for consistency and clarity.
This commit is contained in:
2025-12-11 02:11:43 -05:00
parent 4911b5d125
commit 40210c454e
74 changed files with 2599 additions and 1386 deletions

View File

@@ -4,7 +4,7 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/c
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, addAsset} from '@/store';
import {useAppDispatch, createAsset} from '@/store';
import {validatePositiveNumber, validateRequired, sanitizeString} from '@/lib/validation';
interface Props {
@@ -46,12 +46,10 @@ export default function AddAssetDialog({open, onOpenChange}: Props) {
if (valueNum === null) return;
dispatch(
addAsset({
id: crypto.randomUUID(),
createAsset({
name: sanitizeString(form.name),
type: form.type as (typeof assetTypes)[number],
type: form.type.toUpperCase() as 'CASH' | 'INVESTMENT' | 'PROPERTY' | 'VEHICLE' | 'OTHER',
value: valueNum,
updatedAt: new Date().toISOString()
})
);
onOpenChange(false);

View File

@@ -4,7 +4,7 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/c
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, addLiability} from '@/store';
import {useAppDispatch, createLiability} from '@/store';
interface Props {
open: boolean;
@@ -20,12 +20,10 @@ export default function AddLiabilityDialog({open, onOpenChange}: Props) {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
dispatch(
addLiability({
id: crypto.randomUUID(),
createLiability({
name: form.name,
type: form.type as (typeof liabilityTypes)[number],
balance: parseFloat(form.balance) || 0,
updatedAt: new Date().toISOString()
type: form.type.toUpperCase() as 'CREDIT_CARD' | 'LOAN' | 'MORTGAGE' | 'OTHER',
currentBalance: parseFloat(form.balance) || 0,
})
);
onOpenChange(false);

View File

@@ -4,7 +4,7 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/c
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, updateAsset, removeAsset, type Asset} from '@/store';
import {useAppDispatch, updateAsset, deleteAsset, type Asset} from '@/store';
import {validatePositiveNumber, validateRequired} from '@/lib/validation';
interface Props {
@@ -60,10 +60,11 @@ export default function EditAssetDialog({open, onOpenChange, asset}: Props) {
dispatch(
updateAsset({
id: asset.id,
name: form.name.trim(),
type: form.type as (typeof assetTypes)[number],
value: valueNum,
updatedAt: new Date().toISOString()
data: {
name: form.name.trim(),
type: form.type.toUpperCase() as 'CASH' | 'INVESTMENT' | 'PROPERTY' | 'VEHICLE' | 'OTHER',
value: valueNum,
}
})
);
onOpenChange(false);
@@ -72,7 +73,7 @@ export default function EditAssetDialog({open, onOpenChange, asset}: Props) {
const handleDelete = () => {
if (!asset) return;
if (confirm(`Are you sure you want to delete "${asset.name}"?`)) {
dispatch(removeAsset(asset.id));
dispatch(deleteAsset(asset.id));
onOpenChange(false);
}
};

View File

@@ -4,7 +4,7 @@ import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/c
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, updateLiability, removeLiability, type Liability} from '@/store';
import {useAppDispatch, updateLiability, deleteLiability, type Liability} from '@/store';
import {validatePositiveNumber, validateRequired} from '@/lib/validation';
interface Props {
@@ -60,10 +60,11 @@ export default function EditLiabilityDialog({open, onOpenChange, liability}: Pro
dispatch(
updateLiability({
id: liability.id,
name: form.name.trim(),
type: form.type as (typeof liabilityTypes)[number],
balance: balanceNum,
updatedAt: new Date().toISOString()
data: {
name: form.name.trim(),
type: form.type.toUpperCase() as 'CREDIT_CARD' | 'LOAN' | 'MORTGAGE' | 'OTHER',
currentBalance: balanceNum,
}
})
);
onOpenChange(false);
@@ -72,7 +73,7 @@ export default function EditLiabilityDialog({open, onOpenChange, liability}: Pro
const handleDelete = () => {
if (!liability) return;
if (confirm(`Are you sure you want to delete "${liability.name}"?`)) {
dispatch(removeLiability(liability.id));
dispatch(deleteLiability(liability.id));
onOpenChange(false);
}
};

View File

@@ -3,7 +3,8 @@ import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, Di
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, setUser} from '@/store';
import {useAppDispatch} from '@/store';
import {loginUser} from '@/store/slices/userSlice';
interface Props {
open: boolean;
@@ -18,26 +19,33 @@ export default function LoginDialog({open, onOpenChange, onSwitchToSignUp}: Prop
password: '',
});
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
// Mock login - in production this would validate against an API
if (!form.email || !form.password) {
setError('Please enter your email and password');
return;
}
// Mock successful login
dispatch(setUser({
id: crypto.randomUUID(),
email: form.email,
name: form.email.split('@')[0],
}));
setIsLoading(true);
try {
await dispatch(
loginUser({
email: form.email,
password: form.password,
})
).unwrap();
onOpenChange(false);
setForm({email: '', password: ''});
onOpenChange(false);
setForm({email: '', password: ''});
} catch (err: any) {
setError(err || 'Login failed. Please check your credentials.');
} finally {
setIsLoading(false);
}
};
return (
@@ -76,8 +84,10 @@ export default function LoginDialog({open, onOpenChange, onSwitchToSignUp}: Prop
{error && <p className="text-sm text-red-400">{error}</p>}
</div>
<DialogFooter className="flex-col gap-2 sm:flex-col">
<Button type="submit" className="w-full">Log in</Button>
<Button type="button" variant="ghost" className="w-full" onClick={onSwitchToSignUp}>
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? 'Logging in...' : 'Log in'}
</Button>
<Button type="button" variant="ghost" className="w-full" onClick={onSwitchToSignUp} disabled={isLoading}>
Don't have an account? Sign up
</Button>
</DialogFooter>

View File

@@ -3,7 +3,8 @@ import {Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, Di
import {Button} from '@/components/ui/button';
import {Input} from '@/components/ui/input';
import {Label} from '@/components/ui/label';
import {useAppDispatch, setUser} from '@/store';
import {useAppDispatch} from '@/store';
import {registerUser} from '@/store/slices/userSlice';
interface Props {
open: boolean;
@@ -20,8 +21,9 @@ export default function SignUpDialog({open, onOpenChange, onSwitchToLogin}: Prop
confirmPassword: '',
});
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
@@ -30,20 +32,28 @@ export default function SignUpDialog({open, onOpenChange, onSwitchToLogin}: Prop
return;
}
if (form.password.length < 6) {
setError('Password must be at least 6 characters');
if (form.password.length < 8) {
setError('Password must be at least 8 characters');
return;
}
// Mock sign up - in production this would call an API
dispatch(setUser({
id: crypto.randomUUID(),
email: form.email,
name: form.name,
}));
setIsLoading(true);
try {
await dispatch(
registerUser({
email: form.email,
password: form.password,
name: form.name,
})
).unwrap();
onOpenChange(false);
setForm({name: '', email: '', password: '', confirmPassword: ''});
onOpenChange(false);
setForm({name: '', email: '', password: '', confirmPassword: ''});
} catch (err: any) {
setError(err || 'Registration failed. Please try again.');
} finally {
setIsLoading(false);
}
};
return (
@@ -109,8 +119,10 @@ export default function SignUpDialog({open, onOpenChange, onSwitchToLogin}: Prop
</p>
</div>
<DialogFooter className="flex-col gap-2 sm:flex-col">
<Button type="submit" className="w-full">Create account</Button>
<Button type="button" variant="ghost" className="w-full" onClick={onSwitchToLogin}>
<Button type="submit" className="w-full" disabled={isLoading}>
{isLoading ? 'Creating account...' : 'Create account'}
</Button>
<Button type="button" variant="ghost" className="w-full" onClick={onSwitchToLogin} disabled={isLoading}>
Already have an account? Log in
</Button>
</DialogFooter>