192 lines
9.9 KiB
TypeScript
192 lines
9.9 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, TextInput, Switch, Platform } from 'react-native';
|
|
import { Contract, ContractCategory } from '../../../shared/types';
|
|
import { DatePickerInput } from '../../../shared/components/DatePickerInput';
|
|
|
|
interface AddContractScreenProps {
|
|
onClose: () => void;
|
|
onSave: (contract: Omit<Contract, 'id'>) => void;
|
|
}
|
|
|
|
export const AddContractScreen: React.FC<AddContractScreenProps> = ({ onClose, onSave }) => {
|
|
const [name, setName] = useState('');
|
|
const [provider, setProvider] = useState('');
|
|
const [category, setCategory] = useState<ContractCategory>('other');
|
|
const [amount, setAmount] = useState('0');
|
|
const [billingCycle, setBillingCycle] = useState<'monthly' | 'quarterly' | 'yearly'>('monthly');
|
|
const [startDate, setStartDate] = useState('');
|
|
const [endDate, setEndDate] = useState('');
|
|
const [autoRenewal, setAutoRenewal] = useState(true);
|
|
const [cancellationPeriod, setCancellationPeriod] = useState('');
|
|
const [notes, setNotes] = useState('');
|
|
|
|
const getCategoryIcon = (cat: string) => {
|
|
const icons: Record<string, string> = {
|
|
internet: '🌐',
|
|
mobile: '📱',
|
|
streaming: '📺',
|
|
utilities: '💡',
|
|
insurance: '🛡️',
|
|
other: '📄',
|
|
};
|
|
return icons[cat] || '📄';
|
|
};
|
|
|
|
const handleSave = () => {
|
|
if (!name.trim() || !provider.trim()) {
|
|
alert('Bitte fülle mindestens Name und Anbieter aus!');
|
|
return;
|
|
}
|
|
|
|
const newContract: Omit<Contract, 'id'> = {
|
|
name: name.trim(),
|
|
provider: provider.trim(),
|
|
category,
|
|
amount: parseFloat(amount) || 0,
|
|
currency: 'EUR',
|
|
billingCycle,
|
|
startDate: startDate || new Date().toISOString().split('T')[0],
|
|
endDate: endDate || undefined,
|
|
autoRenewal,
|
|
cancellationPeriod: cancellationPeriod || undefined,
|
|
notes: notes || undefined,
|
|
};
|
|
|
|
onSave(newContract);
|
|
};
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
<View style={styles.header}>
|
|
<TouchableOpacity onPress={onClose} style={styles.closeButton}>
|
|
<Text style={styles.closeIcon}>✕</Text>
|
|
</TouchableOpacity>
|
|
<Text style={styles.headerTitle}>Neuer Vertrag</Text>
|
|
<View style={styles.placeholder} />
|
|
</View>
|
|
|
|
<ScrollView style={styles.content} contentContainerStyle={styles.scrollContent}>
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>Kategorie wählen</Text>
|
|
<View style={styles.categoryGrid}>
|
|
{(['internet', 'mobile', 'streaming', 'utilities', 'insurance', 'other'] as ContractCategory[]).map((cat) => (
|
|
<TouchableOpacity
|
|
key={cat}
|
|
style={[styles.categoryCard, category === cat && styles.categoryCardActive]}
|
|
onPress={() => setCategory(cat)}
|
|
>
|
|
<Text style={styles.categoryIcon}>{getCategoryIcon(cat)}</Text>
|
|
<Text style={styles.categoryLabel}>
|
|
{cat === 'internet' ? 'Internet' :
|
|
cat === 'mobile' ? 'Mobilfunk' :
|
|
cat === 'streaming' ? 'Streaming' :
|
|
cat === 'utilities' ? 'Versorgung' :
|
|
cat === 'insurance' ? 'Versicherung' : 'Sonstiges'}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>Grunddaten</Text>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Vertragsname *</Text>
|
|
<TextInput style={styles.textInput} value={name} onChangeText={setName} placeholder="z.B. Vodafone DSL" placeholderTextColor="#C7C7CC" />
|
|
</View>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Anbieter *</Text>
|
|
<TextInput style={styles.textInput} value={provider} onChangeText={setProvider} placeholder="z.B. Vodafone" placeholderTextColor="#C7C7CC" />
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>Kosten</Text>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Betrag (EUR)</Text>
|
|
<TextInput style={styles.textInput} value={amount} onChangeText={setAmount} placeholder="0.00" keyboardType="decimal-pad" placeholderTextColor="#C7C7CC" />
|
|
</View>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Zahlungsrhythmus</Text>
|
|
<View style={styles.cycleButtons}>
|
|
{[
|
|
{ value: 'monthly', label: 'Monatlich' },
|
|
{ value: 'quarterly', label: 'Vierteljährlich' },
|
|
{ value: 'yearly', label: 'Jährlich' }
|
|
].map((cycle) => (
|
|
<TouchableOpacity key={cycle.value} style={[styles.cycleButton, billingCycle === cycle.value && styles.cycleButtonActive]} onPress={() => setBillingCycle(cycle.value as any)}>
|
|
<Text style={[styles.cycleButtonText, billingCycle === cycle.value && styles.cycleButtonTextActive]}>{cycle.label}</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>Vertragslaufzeit</Text>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Startdatum</Text>
|
|
<DatePickerInput value={startDate} onChange={setStartDate} placeholder="Startdatum wählen" />
|
|
</View>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Enddatum (optional)</Text>
|
|
<DatePickerInput value={endDate} onChange={setEndDate} placeholder="Enddatum wählen (optional)" />
|
|
</View>
|
|
<View style={styles.switchRow}>
|
|
<Text style={styles.inputLabel}>Automatische Verlängerung</Text>
|
|
<Switch value={autoRenewal} onValueChange={setAutoRenewal} trackColor={{ false: '#D1D1D6', true: '#34C75980' }} thumbColor={autoRenewal ? '#34C759' : '#F4F3F4'} />
|
|
</View>
|
|
<View style={styles.inputGroup}>
|
|
<Text style={styles.inputLabel}>Kündigungsfrist (optional)</Text>
|
|
<TextInput style={styles.textInput} value={cancellationPeriod} onChangeText={setCancellationPeriod} placeholder="z.B. 3 Monate" placeholderTextColor="#C7C7CC" />
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>Notizen (optional)</Text>
|
|
<TextInput style={styles.notesInput} value={notes} onChangeText={setNotes} placeholder="Füge zusätzliche Informationen hinzu..." placeholderTextColor="#C7C7CC" multiline numberOfLines={4} />
|
|
</View>
|
|
|
|
<View style={styles.actionButtons}>
|
|
<TouchableOpacity style={styles.cancelButton} onPress={onClose}><Text style={styles.cancelButtonText}>Abbrechen</Text></TouchableOpacity>
|
|
<TouchableOpacity style={styles.saveButton} onPress={handleSave}><Text style={styles.saveButtonText}>Speichern</Text></TouchableOpacity>
|
|
</View>
|
|
<View style={{ height: 100 }} />
|
|
</ScrollView>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const styles = StyleSheet.create({
|
|
container: { flex: 1, backgroundColor: '#F2F2F7' },
|
|
header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingTop: 60, paddingHorizontal: 20, paddingBottom: 16, backgroundColor: '#F2F2F7' },
|
|
closeButton: { width: 40, height: 40, borderRadius: 20, backgroundColor: 'rgba(255, 255, 255, 0.7)', alignItems: 'center', justifyContent: 'center' },
|
|
closeIcon: { fontSize: 24, color: '#007AFF', fontWeight: '300' as any },
|
|
headerTitle: { fontSize: 17, fontWeight: '600' as any, color: '#000000' },
|
|
placeholder: { width: 40 },
|
|
content: { flex: 1 },
|
|
scrollContent: { paddingHorizontal: 20 },
|
|
card: { backgroundColor: Platform.OS === 'ios' ? 'rgba(255, 255, 255, 0.7)' : '#FFFFFF', borderRadius: 20, padding: 20, marginBottom: 16, borderWidth: 1, borderColor: 'rgba(255, 255, 255, 0.5)', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 4 },
|
|
cardTitle: { fontSize: 20, fontWeight: 'bold', color: '#000000', marginBottom: 16 },
|
|
categoryGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12 },
|
|
categoryCard: { width: '30%', aspectRatio: 1, backgroundColor: '#F2F2F7', borderRadius: 16, alignItems: 'center', justifyContent: 'center', padding: 12 },
|
|
categoryCardActive: { backgroundColor: '#007AFF20', borderWidth: 2, borderColor: '#007AFF' },
|
|
categoryIcon: { fontSize: 36, marginBottom: 8 },
|
|
categoryLabel: { fontSize: 12, color: '#000000', textAlign: 'center', fontWeight: '600' as any },
|
|
inputGroup: { marginBottom: 16 },
|
|
inputLabel: { fontSize: 15, color: '#000000', marginBottom: 8, fontWeight: '600' as any },
|
|
textInput: { backgroundColor: '#F2F2F7', borderRadius: 12, padding: 14, fontSize: 17, color: '#000000', borderWidth: 1, borderColor: 'transparent' },
|
|
cycleButtons: { gap: 8 },
|
|
cycleButton: { backgroundColor: '#F2F2F7', padding: 14, borderRadius: 12, alignItems: 'center' },
|
|
cycleButtonActive: { backgroundColor: '#007AFF' },
|
|
cycleButtonText: { fontSize: 17, color: '#000000', fontWeight: '600' as any },
|
|
cycleButtonTextActive: { color: '#FFFFFF' },
|
|
switchRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 },
|
|
notesInput: { backgroundColor: '#F2F2F7', borderRadius: 12, padding: 14, fontSize: 15, color: '#000000', minHeight: 100, textAlignVertical: 'top' },
|
|
actionButtons: { flexDirection: 'row', gap: 12, marginTop: 8 },
|
|
cancelButton: { flex: 1, backgroundColor: '#F2F2F7', padding: 16, borderRadius: 12, alignItems: 'center' },
|
|
cancelButtonText: { fontSize: 17, fontWeight: '600' as any, color: '#000000' },
|
|
saveButton: { flex: 1, backgroundColor: '#007AFF', padding: 16, borderRadius: 12, alignItems: 'center' },
|
|
saveButtonText: { fontSize: 17, fontWeight: '600' as any, color: '#FFFFFF' },
|
|
});
|