refactoring: TypeScript migration, security fixes,

This commit is contained in:
Maurice
2026-03-27 18:40:18 +01:00
parent 510475a46f
commit 8396a75223
150 changed files with 8116 additions and 8467 deletions
@@ -1,10 +1,31 @@
import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'
import ReactDOM from 'react-dom'
import { useState, useEffect, useRef, useMemo, useCallback } from 'react'
import DOM from 'react-dom'
import { useTripStore } from '../../store/tripStore'
import { useTranslation } from '../../i18n'
import { Plus, Trash2, Calculator, Wallet, Pencil, Users, Check } from 'lucide-react'
import CustomSelect from '../shared/CustomSelect'
import { budgetApi } from '../../api/client'
import type { BudgetItem, BudgetMember } from '../../types'
interface TripMember {
id: number
username: string
avatar_url?: string | null
}
interface PieSegment {
label: string
value: number
color: string
}
interface PerPersonSummaryEntry {
user_id: number
username: string
avatar_url: string | null
total_assigned: number
}
// ── Helpers ──────────────────────────────────────────────────────────────────
const CURRENCIES = ['EUR', 'USD', 'GBP', 'JPY', 'CHF', 'CZK', 'PLN', 'SEK', 'NOK', 'DKK', 'TRY', 'THB', 'AUD', 'CAD']
@@ -60,7 +81,12 @@ function InlineEditCell({ value, onSave, type = 'text', style = {}, placeholder
}
// ── Add Item Row ─────────────────────────────────────────────────────────────
function AddItemRow({ onAdd, t }) {
interface AddItemRowProps {
onAdd: (data: { name: string; total_price: number; persons: number | null; days: number | null; note: string | null }) => void
t: (key: string) => string
}
function AddItemRow({ onAdd, t }: AddItemRowProps) {
const [name, setName] = useState('')
const [price, setPrice] = useState('')
const [persons, setPersons] = useState('')
@@ -113,7 +139,13 @@ function AddItemRow({ onAdd, t }) {
}
// ── Chip with custom tooltip ─────────────────────────────────────────────────
function ChipWithTooltip({ label, avatarUrl, size = 20 }) {
interface ChipWithTooltipProps {
label: string
avatarUrl: string | null
size?: number
}
function ChipWithTooltip({ label, avatarUrl, size = 20 }: ChipWithTooltipProps) {
const [hover, setHover] = useState(false)
const [pos, setPos] = useState({ top: 0, left: 0 })
const ref = useRef(null)
@@ -156,7 +188,14 @@ function ChipWithTooltip({ label, avatarUrl, size = 20 }) {
}
// ── Budget Member Chips (for Persons column) ────────────────────────────────
function BudgetMemberChips({ members = [], tripMembers = [], onSetMembers, compact = true }) {
interface BudgetMemberChipsProps {
members?: BudgetMember[]
tripMembers?: TripMember[]
onSetMembers: (memberIds: number[]) => void
compact?: boolean
}
function BudgetMemberChips({ members = [], tripMembers = [], onSetMembers, compact = true }: BudgetMemberChipsProps) {
const chipSize = compact ? 20 : 30
const btnSize = compact ? 18 : 28
const iconSize = compact ? (members.length > 0 ? 8 : 9) : (members.length > 0 ? 12 : 14)
@@ -246,7 +285,14 @@ function BudgetMemberChips({ members = [], tripMembers = [], onSetMembers, compa
}
// ── Per-Person Inline (inside total card) ────────────────────────────────────
function PerPersonInline({ tripId, budgetItems, currency, locale }) {
interface PerPersonInlineProps {
tripId: number
budgetItems: BudgetItem[]
currency: string
locale: string
}
function PerPersonInline({ tripId, budgetItems, currency, locale }: PerPersonInlineProps) {
const [data, setData] = useState(null)
const fmt = (v) => fmtNum(v, locale, currency)
@@ -279,7 +325,13 @@ function PerPersonInline({ tripId, budgetItems, currency, locale }) {
}
// ── Pie Chart (pure CSS conic-gradient) ──────────────────────────────────────
function PieChart({ segments, size = 200, totalLabel }) {
interface PieChartProps {
segments: PieSegment[]
size?: number
totalLabel: string
}
function PieChart({ segments, size = 200, totalLabel }: PieChartProps) {
if (!segments.length) return null
const total = segments.reduce((s, x) => s + x.value, 0)
@@ -316,7 +368,12 @@ function PieChart({ segments, size = 200, totalLabel }) {
}
// ── Main Component ───────────────────────────────────────────────────────────
export default function BudgetPanel({ tripId, tripMembers = [] }) {
interface BudgetPanelProps {
tripId: number
tripMembers?: TripMember[]
}
export default function BudgetPanel({ tripId, tripMembers = [] }: BudgetPanelProps) {
const { trip, budgetItems, addBudgetItem, updateBudgetItem, deleteBudgetItem, loadBudgetItems, updateTrip, setBudgetItemMembers } = useTripStore()
const { t, locale } = useTranslation()
const [newCategoryName, setNewCategoryName] = useState('')
@@ -355,12 +412,12 @@ export default function BudgetPanel({ tripId, tripMembers = [] }) {
const handleDeleteItem = async (id) => { try { await deleteBudgetItem(tripId, id) } catch {} }
const handleDeleteCategory = async (cat) => {
const items = grouped[cat] || []
for (const item of items) await deleteBudgetItem(tripId, item.id)
for (const item of Array.from(items)) await deleteBudgetItem(tripId, item.id)
}
const handleRenameCategory = async (oldName, newName) => {
if (!newName.trim() || newName.trim() === oldName) return
const items = grouped[oldName] || []
for (const item of items) await updateBudgetItem(tripId, item.id, { category: newName.trim() })
for (const item of Array.from(items)) await updateBudgetItem(tripId, item.id, { category: newName.trim() })
}
const handleAddCategory = () => {
if (!newCategoryName.trim()) return