ueberpruefen
This commit is contained in:
311
src/screens/GlazePickerScreen.tsx
Normal file
311
src/screens/GlazePickerScreen.tsx
Normal file
@@ -0,0 +1,311 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
FlatList,
|
||||
TouchableOpacity,
|
||||
TextInput,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { RootStackParamList } from '../navigation/types';
|
||||
import { Glaze } from '../types';
|
||||
import { getAllGlazes, searchGlazes } from '../lib/db/repositories';
|
||||
import { Button, Card } from '../components';
|
||||
import { colors, spacing, typography, borderRadius } from '../lib/theme';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
type NavigationProp = NativeStackNavigationProp<RootStackParamList, 'GlazePicker'>;
|
||||
type RouteProps = RouteProp<RootStackParamList, 'GlazePicker'>;
|
||||
|
||||
export const GlazePickerScreen: React.FC = () => {
|
||||
const navigation = useNavigation<NavigationProp>();
|
||||
const route = useRoute<RouteProps>();
|
||||
const { user } = useAuth();
|
||||
const { selectedGlazeIds = [] } = route.params || {};
|
||||
|
||||
const [glazes, setGlazes] = useState<Glaze[]>([]);
|
||||
const [selected, setSelected] = useState<string[]>(selectedGlazeIds);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
loadGlazes();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchQuery.trim()) {
|
||||
searchGlazesHandler();
|
||||
} else {
|
||||
loadGlazes();
|
||||
}
|
||||
}, [searchQuery]);
|
||||
|
||||
const loadGlazes = async () => {
|
||||
if (!user) return;
|
||||
|
||||
try {
|
||||
const all = await getAllGlazes(user.id);
|
||||
setGlazes(all);
|
||||
} catch (error) {
|
||||
console.error('Failed to load glazes:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const searchGlazesHandler = async () => {
|
||||
if (!user) return;
|
||||
|
||||
try {
|
||||
const results = await searchGlazes(searchQuery, user.id);
|
||||
setGlazes(results);
|
||||
} catch (error) {
|
||||
console.error('Failed to search glazes:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleGlaze = (glazeId: string) => {
|
||||
if (selected.includes(glazeId)) {
|
||||
setSelected(selected.filter(id => id !== glazeId));
|
||||
} else {
|
||||
setSelected([...selected, glazeId]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
// Navigate back to the EXACT StepEditor instance we came from
|
||||
// Using the same _editorKey ensures React Navigation finds the correct instance
|
||||
console.log('GlazePicker: Navigating back to StepEditor with glazes:', selected);
|
||||
|
||||
navigation.navigate({
|
||||
name: 'StepEditor',
|
||||
params: {
|
||||
projectId: route.params.projectId,
|
||||
stepId: route.params.stepId,
|
||||
selectedGlazeIds: selected,
|
||||
_editorKey: route.params._editorKey, // Same key = same instance!
|
||||
_timestamp: Date.now(),
|
||||
},
|
||||
merge: true, // Merge params with existing screen instead of creating new one
|
||||
} as any);
|
||||
};
|
||||
|
||||
const renderGlaze = ({ item }: { item: Glaze }) => {
|
||||
const isSelected = selected.includes(item.id);
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={() => toggleGlaze(item.id)}>
|
||||
<Card style={[styles.glazeCard, isSelected ? styles.glazeCardSelected : null]}>
|
||||
<View style={styles.glazeMainContent}>
|
||||
{item.color && (
|
||||
<View style={[styles.colorPreview, { backgroundColor: item.color }]} />
|
||||
)}
|
||||
<View style={styles.glazeInfo}>
|
||||
<View style={styles.glazeHeader}>
|
||||
<Text style={styles.glazeBrand}>{item.brand}</Text>
|
||||
<View style={styles.badges}>
|
||||
{item.isMix && <Text style={styles.mixBadge}>Mix</Text>}
|
||||
{item.isCustom && !item.isMix && <Text style={styles.customBadge}>Custom</Text>}
|
||||
</View>
|
||||
</View>
|
||||
<Text style={styles.glazeName}>{item.name}</Text>
|
||||
{item.code && <Text style={styles.glazeCode}>Code: {item.code}</Text>}
|
||||
{item.finish && (
|
||||
<Text style={styles.glazeFinish}>
|
||||
{item.finish.charAt(0).toUpperCase() + item.finish.slice(1)}
|
||||
</Text>
|
||||
)}
|
||||
{item.mixRatio && (
|
||||
<Text style={styles.glazeMixRatio}>Ratio: {item.mixRatio}</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Card>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<View style={styles.header}>
|
||||
<TextInput
|
||||
style={styles.searchInput}
|
||||
placeholder="Search glazes..."
|
||||
value={searchQuery}
|
||||
onChangeText={setSearchQuery}
|
||||
placeholderTextColor={colors.textSecondary}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Text style={styles.selectedCount}>
|
||||
Selected: {selected.length} glaze{selected.length !== 1 ? 's' : ''}
|
||||
</Text>
|
||||
|
||||
<FlatList
|
||||
data={glazes}
|
||||
renderItem={renderGlaze}
|
||||
keyExtractor={(item) => item.id}
|
||||
contentContainerStyle={styles.listContent}
|
||||
ListEmptyComponent={
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyText}>
|
||||
{searchQuery ? 'No glazes found' : 'No glazes in catalog'}
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
|
||||
<View style={styles.footer}>
|
||||
<Button
|
||||
title="Mix Glazes"
|
||||
onPress={() => navigation.navigate('GlazeMixer', {
|
||||
projectId: route.params.projectId,
|
||||
stepId: route.params.stepId,
|
||||
_editorKey: route.params._editorKey, // Pass editor key through
|
||||
})}
|
||||
variant="outline"
|
||||
style={styles.mixButton}
|
||||
/>
|
||||
<Button
|
||||
title="Save Selection"
|
||||
onPress={handleSave}
|
||||
disabled={selected.length === 0}
|
||||
/>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.backgroundSecondary,
|
||||
},
|
||||
header: {
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingTop: spacing.md,
|
||||
paddingBottom: spacing.md,
|
||||
backgroundColor: colors.background,
|
||||
},
|
||||
searchInput: {
|
||||
backgroundColor: colors.backgroundSecondary,
|
||||
borderRadius: borderRadius.md,
|
||||
borderWidth: 2,
|
||||
borderColor: colors.border,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.md,
|
||||
fontSize: typography.fontSize.md,
|
||||
color: colors.text,
|
||||
},
|
||||
selectedCount: {
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingVertical: spacing.sm,
|
||||
fontSize: typography.fontSize.sm,
|
||||
fontWeight: typography.fontWeight.bold,
|
||||
color: colors.textSecondary,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
listContent: {
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingBottom: spacing.xl,
|
||||
},
|
||||
glazeCard: {
|
||||
marginBottom: spacing.md,
|
||||
},
|
||||
glazeCardSelected: {
|
||||
borderWidth: 3,
|
||||
borderColor: colors.primary,
|
||||
backgroundColor: colors.primaryLight + '20',
|
||||
},
|
||||
glazeMainContent: {
|
||||
flexDirection: 'row',
|
||||
gap: spacing.md,
|
||||
},
|
||||
colorPreview: {
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: borderRadius.md,
|
||||
borderWidth: 2,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
glazeInfo: {
|
||||
flex: 1,
|
||||
},
|
||||
glazeHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
glazeBrand: {
|
||||
fontSize: typography.fontSize.sm,
|
||||
fontWeight: typography.fontWeight.bold,
|
||||
color: colors.primary,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
badges: {
|
||||
flexDirection: 'row',
|
||||
gap: spacing.xs,
|
||||
},
|
||||
customBadge: {
|
||||
fontSize: typography.fontSize.xs,
|
||||
fontWeight: typography.fontWeight.bold,
|
||||
color: colors.background,
|
||||
backgroundColor: colors.info,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingVertical: spacing.xs,
|
||||
borderRadius: borderRadius.full,
|
||||
},
|
||||
mixBadge: {
|
||||
fontSize: typography.fontSize.xs,
|
||||
fontWeight: typography.fontWeight.bold,
|
||||
color: colors.background,
|
||||
backgroundColor: colors.primary,
|
||||
paddingHorizontal: spacing.sm,
|
||||
paddingVertical: spacing.xs,
|
||||
borderRadius: borderRadius.full,
|
||||
},
|
||||
glazeName: {
|
||||
fontSize: typography.fontSize.md,
|
||||
fontWeight: typography.fontWeight.bold,
|
||||
color: colors.text,
|
||||
marginBottom: spacing.xs,
|
||||
},
|
||||
glazeCode: {
|
||||
fontSize: typography.fontSize.sm,
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
glazeFinish: {
|
||||
fontSize: typography.fontSize.sm,
|
||||
color: colors.textSecondary,
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
glazeMixRatio: {
|
||||
fontSize: typography.fontSize.sm,
|
||||
color: colors.textSecondary,
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
emptyContainer: {
|
||||
padding: spacing.xl,
|
||||
alignItems: 'center',
|
||||
},
|
||||
emptyText: {
|
||||
fontSize: typography.fontSize.md,
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
footer: {
|
||||
padding: spacing.md,
|
||||
backgroundColor: colors.background,
|
||||
borderTopWidth: 2,
|
||||
borderTopColor: colors.border,
|
||||
flexDirection: 'row',
|
||||
gap: spacing.md,
|
||||
},
|
||||
mixButton: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user