try to do better #18
@@ -1,4 +1,5 @@
|
||||
import React, { Component, ReactNode } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import { Button } from './ui';
|
||||
|
||||
@@ -22,7 +23,7 @@ export class ErrorBoundary extends Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): Partial<State> {
|
||||
static getDerivedStateFromError(_error: Error): Partial<State> {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ export class ErrorBoundary extends Component<Props, State> {
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
We're sorry, but something unexpected happened. Please try refreshing the page or going back to the home page.
|
||||
</p>
|
||||
{process.env.NODE_ENV === 'development' && this.state.error && (
|
||||
{import.meta.env.DEV && this.state.error && (
|
||||
<div className="mb-6 p-4 bg-red-50 dark:bg-red-900/10 rounded-lg text-left">
|
||||
<p className="text-sm font-mono text-red-800 dark:text-red-300 break-all">
|
||||
{this.state.error.toString()}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ShoppingCart, CheckCheck, Trash2 } from 'lucide-react';
|
||||
import { useShoppingList, useConfirm } from '../hooks';
|
||||
@@ -16,7 +15,6 @@ export default function ShoppingListModal({ familyId, onClose }: ShoppingListMod
|
||||
const { t } = useTranslation();
|
||||
const { items, loading, createItem, deleteItem, togglePurchased, markAllAsPurchased, clearAll } = useShoppingList(familyId);
|
||||
const { confirmState, confirm, cancel } = useConfirm();
|
||||
const [pendingAction, setPendingAction] = useState<{ type: 'delete' | 'markAll' | 'clearAll'; itemId?: number } | null>(null);
|
||||
|
||||
const stats = shoppingService.getStats(items);
|
||||
|
||||
@@ -25,24 +23,18 @@ export default function ShoppingListModal({ familyId, onClose }: ShoppingListMod
|
||||
};
|
||||
|
||||
const handleDelete = async (itemId: number) => {
|
||||
setPendingAction({ type: 'delete', itemId });
|
||||
await confirm(t('shopping.deleteConfirm'), t('shopping.deleteMessage'));
|
||||
await deleteItem(itemId);
|
||||
setPendingAction(null);
|
||||
};
|
||||
|
||||
const handleMarkAll = async () => {
|
||||
setPendingAction({ type: 'markAll' });
|
||||
await confirm(t('shopping.markAllConfirm'), t('shopping.markAllMessage'));
|
||||
await markAllAsPurchased();
|
||||
setPendingAction(null);
|
||||
};
|
||||
|
||||
const handleClearAll = async () => {
|
||||
setPendingAction({ type: 'clearAll' });
|
||||
await confirm(t('shopping.clearAllConfirm'), t('shopping.clearAllMessage'));
|
||||
await clearAll();
|
||||
setPendingAction(null);
|
||||
};
|
||||
|
||||
const handleUpdate = async (itemId: number, name: string) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Plus, X } from 'lucide-react';
|
||||
import { Button, Input } from '../ui';
|
||||
import { CreateCategoryRequest } from '../../types';
|
||||
import type { CreateCategoryRequest } from '../../types';
|
||||
|
||||
interface AddCategorySectionProps {
|
||||
showForm: boolean;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tag, TrendingDown, Plus, Trash2, RotateCcw, History, X, DollarSign, MessageSquare, Calendar } from 'lucide-react';
|
||||
import { CategoryWithRemaining } from '../../services';
|
||||
import type { CategoryWithRemaining } from '../../services';
|
||||
import { categoryService, expenseService } from '../../services';
|
||||
import { useExpenses } from '../../hooks';
|
||||
import { format } from '../../utils/format';
|
||||
import { Button, Input, Badge } from '../ui';
|
||||
import { Button, Input } from '../ui';
|
||||
|
||||
interface CategoryCardProps {
|
||||
category: CategoryWithRemaining;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CategoryWithRemaining } from '../../services';
|
||||
import type { CategoryWithRemaining } from '../../services';
|
||||
import { CategoryCard } from './CategoryCard';
|
||||
|
||||
interface CategoryListProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Wallet, ShoppingCart } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CategoryWithRemaining } from '../../services';
|
||||
import type { CategoryWithRemaining } from '../../services';
|
||||
import { format } from '../../utils/format';
|
||||
|
||||
interface FamilySummaryProps {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Copy, Check, Loader2 } from 'lucide-react';
|
||||
import { Modal, Button } from '../ui';
|
||||
import { useInviteLink } from '../../hooks';
|
||||
import { InviteLinkResponse } from '../../types';
|
||||
import type { InviteLinkResponse } from '../../types';
|
||||
|
||||
interface InviteModalProps {
|
||||
onClose: () => void;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Users, Edit3, Save, X, AlertTriangle, Loader2 } from 'lucide-react';
|
||||
import { Family } from '../../types';
|
||||
import type { Family } from '../../types';
|
||||
import { Card, Button, Input } from '../ui';
|
||||
import { familyService } from '../../services';
|
||||
import { showToast } from '../../utils/toast';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { FamilyMember, User } from '../../types';
|
||||
import type { FamilyMember, User } from '../../types';
|
||||
import { Badge, LoadingSpinner } from '../ui';
|
||||
|
||||
interface MembersSectionProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Settings, Palette, Languages } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Theme } from '../../types';
|
||||
import type { Theme } from '../../types';
|
||||
import { Card } from '../ui';
|
||||
import { ThemeSelector } from './ThemeSelector';
|
||||
import { LanguageSelector } from './LanguageSelector';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Check } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Theme } from '../../types';
|
||||
import type { Theme } from '../../types';
|
||||
import { THEMES } from '../../constants';
|
||||
|
||||
interface ThemeSelectorProps {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { User as UserIcon, LogOut } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { User } from '../../types';
|
||||
import type { User } from '../../types';
|
||||
import { Button, Card } from '../ui';
|
||||
|
||||
interface UserInfoProps {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Trash2, Check, Pencil, X } from 'lucide-react';
|
||||
import { ShoppingItem } from '../../types';
|
||||
import type { ShoppingItem } from '../../types';
|
||||
import { Button, Input } from '../ui';
|
||||
|
||||
interface ShoppingItemCardProps {
|
||||
@@ -12,7 +11,6 @@ interface ShoppingItemCardProps {
|
||||
}
|
||||
|
||||
export function ShoppingItemCard({ item, onToggle, onDelete, onUpdate }: ShoppingItemCardProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [editName, setEditName] = useState(item.name);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, KeyboardEvent } from 'react';
|
||||
import { useState } from 'react';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { Button, Input } from '../ui';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ShoppingItem } from '../../types';
|
||||
import type { ShoppingItem } from '../../types';
|
||||
import { ShoppingItemCard } from './ShoppingItemCard';
|
||||
import { shoppingService } from '../../services';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
interface BadgeProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
|
||||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'primary' | 'success' | 'danger' | 'secondary' | 'ghost';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
interface CardProps {
|
||||
children: ReactNode;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { InputHTMLAttributes, forwardRef } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import type { InputHTMLAttributes } from 'react';
|
||||
|
||||
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReactNode, useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
interface ModalProps {
|
||||
isOpen: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Theme } from '../types';
|
||||
import type { Theme } from '../types';
|
||||
|
||||
export const THEMES: { id: Theme; gradient: string; name: string }[] = [
|
||||
{ id: 'light', gradient: 'bg-gradient-to-r from-gray-100 to-gray-200', name: 'Light' },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { categoryService, CategoryWithRemaining } from '../services';
|
||||
import { CreateCategoryRequest } from '../types';
|
||||
import { categoryService } from '../services';
|
||||
import type { CategoryWithRemaining } from '../services';
|
||||
import type { CreateCategoryRequest } from '../types';
|
||||
import { showToast } from '../utils/toast';
|
||||
import { showErrorToast } from '../utils/errorHandler';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { expenseService } from '../services';
|
||||
import { Expense, CreateExpenseRequest } from '../types';
|
||||
import type { Expense, CreateExpenseRequest } from '../types';
|
||||
import { showToast } from '../utils/toast';
|
||||
import { showErrorToast } from '../utils/errorHandler';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { familyService } from '../services';
|
||||
import { FamilyMember } from '../types';
|
||||
import type { FamilyMember } from '../types';
|
||||
import { showErrorToast } from '../utils/errorHandler';
|
||||
|
||||
export function useFamilyMembers(familyId: number | null) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { inviteService } from '../services';
|
||||
import { InviteLinkResponse, CreateInviteLinkRequest } from '../types';
|
||||
import type { InviteLinkResponse, CreateInviteLinkRequest } from '../types';
|
||||
import { showToast } from '../utils/toast';
|
||||
import { showErrorToast } from '../utils/errorHandler';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { shoppingService } from '../services';
|
||||
import { ShoppingItem, CreateShoppingItemRequest } from '../types';
|
||||
import type { ShoppingItem, CreateShoppingItemRequest } from '../types';
|
||||
import { showToast } from '../utils/toast';
|
||||
import { showErrorToast } from '../utils/errorHandler';
|
||||
|
||||
|
||||
@@ -5,9 +5,8 @@ import { User as UserIcon } from 'lucide-react';
|
||||
import { familyApi, authApi } from '../api/client';
|
||||
import { useStore } from '../store/useStore';
|
||||
import { useFamilyMembers, useConfirm } from '../hooks';
|
||||
import { Theme } from '../types';
|
||||
import type { Theme } from '../types';
|
||||
import { ProfileHeader } from '../components/profile/ProfileHeader';
|
||||
import { UserInfo } from '../components/profile/UserInfo';
|
||||
import { FamilySection } from '../components/profile/FamilySection';
|
||||
import { MembersSection } from '../components/profile/MembersSection';
|
||||
import { SettingsSection } from '../components/profile/SettingsSection';
|
||||
@@ -19,7 +18,7 @@ export default function Profile() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { user, selectedFamily, setSelectedFamily, setUser, preferences, setPreferences } = useStore();
|
||||
const { members, loading: membersLoading, loadMembers } = useFamilyMembers(user?.family_id || null);
|
||||
const { members, loading: membersLoading } = useFamilyMembers(user?.family_id || null);
|
||||
const { confirmState, confirm, cancel } = useConfirm();
|
||||
|
||||
const [leavingFamily, setLeavingFamily] = useState(false);
|
||||
@@ -61,17 +60,6 @@ export default function Profile() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await authApi.logout();
|
||||
setUser(null);
|
||||
setSelectedFamily(null);
|
||||
navigate('/login');
|
||||
} catch (error) {
|
||||
showErrorToast(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleThemeChange = (theme: Theme) => {
|
||||
setPreferences({ ...preferences, theme });
|
||||
showToast.success(t('profile.themeChanged'));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { categoryApi, expenseApi } from '../api/client';
|
||||
import { Category, CreateCategoryRequest, RemainingLimit } from '../types';
|
||||
import type { Category, CreateCategoryRequest } from '../types';
|
||||
import { handleApiError } from '../utils/errorHandler';
|
||||
|
||||
export interface CategoryWithRemaining extends Category {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expenseApi } from '../api/client';
|
||||
import { Expense, CreateExpenseRequest } from '../types';
|
||||
import type { Expense, CreateExpenseRequest } from '../types';
|
||||
import { handleApiError } from '../utils/errorHandler';
|
||||
|
||||
export const expenseService = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { familyApi } from '../api/client';
|
||||
import { Family, CreateFamilyRequest, CreateMyFamilyRequest, CreateMyFamilyResponse, VerifyFamilyPasswordRequest, FamilyMember } from '../types';
|
||||
import type { Family, CreateFamilyRequest, CreateMyFamilyRequest, CreateMyFamilyResponse, VerifyFamilyPasswordRequest, FamilyMember } from '../types';
|
||||
import { handleApiError } from '../utils/errorHandler';
|
||||
|
||||
export const familyService = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { inviteLinkApi } from '../api/client';
|
||||
import { CreateInviteLinkRequest, InviteLinkResponse, ValidateInviteResponse, JoinFamilyResponse } from '../types';
|
||||
import type { CreateInviteLinkRequest, InviteLinkResponse, ValidateInviteResponse, JoinFamilyResponse } from '../types';
|
||||
import { handleApiError } from '../utils/errorHandler';
|
||||
|
||||
export const inviteService = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { shoppingItemApi } from '../api/client';
|
||||
import { ShoppingItem, CreateShoppingItemRequest, UpdateShoppingItemRequest, MarkAsPurchasedRequest } from '../types';
|
||||
import type { ShoppingItem, CreateShoppingItemRequest, UpdateShoppingItemRequest, MarkAsPurchasedRequest } from '../types';
|
||||
import { handleApiError } from '../utils/errorHandler';
|
||||
|
||||
export const shoppingService = {
|
||||
|
||||
@@ -124,7 +124,7 @@ export const useStore = create<AppState>((set, get) => ({
|
||||
},
|
||||
|
||||
clearCache: () => {
|
||||
set((state) => ({
|
||||
set(() => ({
|
||||
cache: {
|
||||
categories: new Map(),
|
||||
members: new Map(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AxiosError } from 'axios';
|
||||
import { ApiError, AppError } from '../types/errors';
|
||||
import { AppError } from '../types/errors';
|
||||
import { showToast } from './toast';
|
||||
|
||||
export function handleApiError(error: unknown): never {
|
||||
|
||||
Reference in New Issue
Block a user