fitst commit
This commit is contained in:
550
screens/profile/ui/ProductServicesTab.tsx
Normal file
550
screens/profile/ui/ProductServicesTab.tsx
Normal file
@@ -0,0 +1,550 @@
|
||||
import { Image as ExpoImage } from 'expo-image';
|
||||
import { FileVideo, Image as ImageIcon, Plus } from 'lucide-react-native';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Alert,
|
||||
FlatList,
|
||||
Modal,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { useProfileData } from '../lib/ProfileDataContext';
|
||||
|
||||
const CATEGORIES = [
|
||||
'Qurilish',
|
||||
'Savdo',
|
||||
'IT',
|
||||
'Dizayn',
|
||||
'Transport',
|
||||
"Ta'lim",
|
||||
"Sog'liqni saqlash",
|
||||
'Restoran',
|
||||
'Turizm',
|
||||
'Sport',
|
||||
];
|
||||
|
||||
export function ProductServicesTab() {
|
||||
const { productServices, addProductService } = useProfileData();
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [step, setStep] = useState<1 | 2>(1);
|
||||
|
||||
const [title, setTitle] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [mediaUrl, setMediaUrl] = useState('');
|
||||
const [mediaType, setMediaType] = useState<'image' | 'video'>('image');
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
||||
|
||||
const resetForm = () => {
|
||||
setTitle('');
|
||||
setDescription('');
|
||||
setMediaUrl('');
|
||||
setMediaType('image');
|
||||
setSelectedCategories([]);
|
||||
setStep(1);
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
if (!title.trim() || !description.trim() || !mediaUrl.trim()) {
|
||||
Alert.alert('Xato', "Barcha maydonlarni to'ldiring");
|
||||
return;
|
||||
}
|
||||
setStep(2);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
if (selectedCategories.length === 0) {
|
||||
Alert.alert('Xato', 'Kamida bitta kategoriya tanlang');
|
||||
return;
|
||||
}
|
||||
|
||||
addProductService({
|
||||
title: title.trim(),
|
||||
description: description.trim(),
|
||||
mediaUrl: mediaUrl.trim(),
|
||||
mediaType,
|
||||
categories: selectedCategories,
|
||||
});
|
||||
|
||||
resetForm();
|
||||
setModalVisible(false);
|
||||
Alert.alert('Muvaffaqiyat', "Xizmat muvaffaqiyatli qo'shildi");
|
||||
};
|
||||
|
||||
const toggleCategory = (category: string) => {
|
||||
setSelectedCategories((prev) =>
|
||||
prev.includes(category) ? prev.filter((c) => c !== category) : [...prev, category]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Pressable onPress={() => setModalVisible(true)} style={styles.addButton}>
|
||||
<Plus size={20} color="#ffffff" />
|
||||
<Text style={styles.addButtonText}>Xizmat qo'shish</Text>
|
||||
</Pressable>
|
||||
|
||||
<FlatList
|
||||
data={productServices}
|
||||
keyExtractor={(item) => item.id}
|
||||
contentContainerStyle={styles.list}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.card}>
|
||||
<View style={styles.mediaContainer}>
|
||||
{item.mediaType === 'image' ? (
|
||||
<ExpoImage
|
||||
source={{ uri: item.mediaUrl }}
|
||||
style={styles.media}
|
||||
contentFit="cover"
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.videoPlaceholder}>
|
||||
<FileVideo size={40} color="#64748b" />
|
||||
<Text style={styles.videoText}>Video</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View style={styles.contentContainer}>
|
||||
<Text style={styles.title}>{item.title}</Text>
|
||||
<Text style={styles.description} numberOfLines={2}>
|
||||
{item.description}
|
||||
</Text>
|
||||
|
||||
<View style={styles.categoriesContainer}>
|
||||
{item.categories.map((category, index) => (
|
||||
<View key={index} style={styles.categoryChip}>
|
||||
<Text style={styles.categoryText}>{category}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
ListEmptyComponent={
|
||||
<View style={styles.emptyContainer}>
|
||||
<ImageIcon size={48} color="#475569" />
|
||||
<Text style={styles.emptyText}>Hozircha xizmatlar yo'q</Text>
|
||||
<Text style={styles.emptySubtext}>
|
||||
Yangi xizmat qo'shish uchun yuqoridagi tugmani bosing
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
visible={modalVisible}
|
||||
transparent
|
||||
animationType="slide"
|
||||
onRequestClose={() => {
|
||||
resetForm();
|
||||
setModalVisible(false);
|
||||
}}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<View style={styles.modalHeader}>
|
||||
<Text style={styles.modalTitle}>
|
||||
{step === 1 ? "Xizmat ma'lumotlari" : 'Kategoriyalarni tanlang'}
|
||||
</Text>
|
||||
<View style={styles.stepIndicator}>
|
||||
<View style={[styles.stepDot, step >= 1 && styles.stepDotActive]} />
|
||||
<View style={[styles.stepLine, step >= 2 && styles.stepLineActive]} />
|
||||
<View style={[styles.stepDot, step >= 2 && styles.stepDotActive]} />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{step === 1 ? (
|
||||
<ScrollView showsVerticalScrollIndicator={false}>
|
||||
<Text style={styles.inputLabel}>Nomi</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={title}
|
||||
onChangeText={setTitle}
|
||||
placeholder="Xizmat nomi"
|
||||
placeholderTextColor="#64748b"
|
||||
/>
|
||||
|
||||
<Text style={styles.inputLabel}>Tavsif</Text>
|
||||
<TextInput
|
||||
style={[styles.input, styles.textArea]}
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
placeholder="Xizmat haqida batafsil ma'lumot"
|
||||
placeholderTextColor="#64748b"
|
||||
multiline
|
||||
numberOfLines={4}
|
||||
textAlignVertical="top"
|
||||
/>
|
||||
|
||||
<Text style={styles.inputLabel}>Media turi</Text>
|
||||
<View style={styles.mediaTypeContainer}>
|
||||
<Pressable
|
||||
onPress={() => setMediaType('image')}
|
||||
style={[
|
||||
styles.mediaTypeButton,
|
||||
mediaType === 'image' && styles.mediaTypeButtonActive,
|
||||
]}
|
||||
>
|
||||
<ImageIcon size={20} color={mediaType === 'image' ? '#3b82f6' : '#64748b'} />
|
||||
<Text
|
||||
style={[
|
||||
styles.mediaTypeText,
|
||||
mediaType === 'image' && styles.mediaTypeTextActive,
|
||||
]}
|
||||
>
|
||||
Rasm
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
onPress={() => setMediaType('video')}
|
||||
style={[
|
||||
styles.mediaTypeButton,
|
||||
mediaType === 'video' && styles.mediaTypeButtonActive,
|
||||
]}
|
||||
>
|
||||
<FileVideo size={20} color={mediaType === 'video' ? '#3b82f6' : '#64748b'} />
|
||||
<Text
|
||||
style={[
|
||||
styles.mediaTypeText,
|
||||
mediaType === 'video' && styles.mediaTypeTextActive,
|
||||
]}
|
||||
>
|
||||
Video
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<Text style={styles.inputLabel}>Media URL</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={mediaUrl}
|
||||
onChangeText={setMediaUrl}
|
||||
placeholder="https://..."
|
||||
placeholderTextColor="#64748b"
|
||||
keyboardType="url"
|
||||
/>
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<Pressable
|
||||
onPress={() => {
|
||||
resetForm();
|
||||
setModalVisible(false);
|
||||
}}
|
||||
style={[styles.modalButton, styles.cancelButton]}
|
||||
>
|
||||
<Text style={styles.cancelButtonText}>Bekor qilish</Text>
|
||||
</Pressable>
|
||||
<Pressable onPress={handleNext} style={[styles.modalButton, styles.nextButton]}>
|
||||
<Text style={styles.nextButtonText}>Keyingisi</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</ScrollView>
|
||||
) : (
|
||||
<>
|
||||
<ScrollView showsVerticalScrollIndicator={false}>
|
||||
<View style={styles.categoriesGrid}>
|
||||
{CATEGORIES.map((category) => (
|
||||
<Pressable
|
||||
key={category}
|
||||
onPress={() => toggleCategory(category)}
|
||||
style={[
|
||||
styles.categoryOption,
|
||||
selectedCategories.includes(category) && styles.categoryOptionSelected,
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.categoryOptionText,
|
||||
selectedCategories.includes(category) &&
|
||||
styles.categoryOptionTextSelected,
|
||||
]}
|
||||
>
|
||||
{category}
|
||||
</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
<View style={styles.modalButtons}>
|
||||
<Pressable
|
||||
onPress={() => setStep(1)}
|
||||
style={[styles.modalButton, styles.backButton]}
|
||||
>
|
||||
<Text style={styles.backButtonText}>Ortga</Text>
|
||||
</Pressable>
|
||||
<Pressable onPress={handleAdd} style={[styles.modalButton, styles.saveButton]}>
|
||||
<Text style={styles.saveButtonText}>Qo'shish</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
paddingBottom: 100,
|
||||
},
|
||||
addButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
backgroundColor: '#3b82f6',
|
||||
paddingVertical: 14,
|
||||
borderRadius: 12,
|
||||
marginBottom: 16,
|
||||
},
|
||||
addButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700' as const,
|
||||
color: '#ffffff',
|
||||
},
|
||||
list: {
|
||||
gap: 16,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#1e293b',
|
||||
borderRadius: 16,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
mediaContainer: {
|
||||
width: '100%',
|
||||
height: 200,
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
media: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
},
|
||||
videoPlaceholder: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
},
|
||||
videoText: {
|
||||
fontSize: 14,
|
||||
color: '#64748b',
|
||||
fontWeight: '600' as const,
|
||||
},
|
||||
contentContainer: {
|
||||
padding: 16,
|
||||
gap: 8,
|
||||
},
|
||||
title: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700' as const,
|
||||
color: '#f8fafc',
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
color: '#94a3b8',
|
||||
lineHeight: 20,
|
||||
},
|
||||
categoriesContainer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
marginTop: 4,
|
||||
},
|
||||
categoryChip: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
categoryText: {
|
||||
fontSize: 12,
|
||||
color: '#94a3b8',
|
||||
fontWeight: '500' as const,
|
||||
},
|
||||
emptyContainer: {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 80,
|
||||
gap: 12,
|
||||
},
|
||||
emptyText: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600' as const,
|
||||
color: '#64748b',
|
||||
},
|
||||
emptySubtext: {
|
||||
fontSize: 14,
|
||||
color: '#475569',
|
||||
textAlign: 'center',
|
||||
paddingHorizontal: 40,
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: '#00000090',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
modalContent: {
|
||||
maxHeight: '90%',
|
||||
backgroundColor: '#1e293b',
|
||||
borderTopLeftRadius: 24,
|
||||
borderTopRightRadius: 24,
|
||||
padding: 24,
|
||||
gap: 20,
|
||||
},
|
||||
modalHeader: {
|
||||
gap: 12,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 22,
|
||||
fontWeight: '700' as const,
|
||||
color: '#f8fafc',
|
||||
},
|
||||
stepIndicator: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
},
|
||||
stepDot: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: 5,
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
stepDotActive: {
|
||||
backgroundColor: '#3b82f6',
|
||||
},
|
||||
stepLine: {
|
||||
flex: 1,
|
||||
height: 2,
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
stepLineActive: {
|
||||
backgroundColor: '#3b82f6',
|
||||
},
|
||||
inputLabel: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
color: '#94a3b8',
|
||||
marginBottom: 8,
|
||||
marginTop: 8,
|
||||
},
|
||||
input: {
|
||||
backgroundColor: '#0f172a',
|
||||
borderRadius: 12,
|
||||
padding: 14,
|
||||
fontSize: 16,
|
||||
color: '#f8fafc',
|
||||
borderWidth: 1,
|
||||
borderColor: '#334155',
|
||||
},
|
||||
textArea: {
|
||||
height: 100,
|
||||
paddingTop: 14,
|
||||
},
|
||||
mediaTypeContainer: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
},
|
||||
mediaTypeButton: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 8,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#0f172a',
|
||||
borderWidth: 1,
|
||||
borderColor: '#334155',
|
||||
},
|
||||
mediaTypeButtonActive: {
|
||||
borderColor: '#3b82f6',
|
||||
backgroundColor: '#1e40af20',
|
||||
},
|
||||
mediaTypeText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
color: '#64748b',
|
||||
},
|
||||
mediaTypeTextActive: {
|
||||
color: '#3b82f6',
|
||||
},
|
||||
categoriesGrid: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 12,
|
||||
},
|
||||
categoryOption: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
borderRadius: 12,
|
||||
backgroundColor: '#0f172a',
|
||||
borderWidth: 1,
|
||||
borderColor: '#334155',
|
||||
},
|
||||
categoryOptionSelected: {
|
||||
backgroundColor: '#1e40af20',
|
||||
borderColor: '#3b82f6',
|
||||
},
|
||||
categoryOptionText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600' as const,
|
||||
color: '#94a3b8',
|
||||
},
|
||||
categoryOptionTextSelected: {
|
||||
color: '#3b82f6',
|
||||
},
|
||||
modalButtons: {
|
||||
flexDirection: 'row',
|
||||
gap: 12,
|
||||
marginTop: 8,
|
||||
},
|
||||
modalButton: {
|
||||
flex: 1,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 12,
|
||||
alignItems: 'center',
|
||||
},
|
||||
cancelButton: {
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
backButton: {
|
||||
backgroundColor: '#334155',
|
||||
},
|
||||
nextButton: {
|
||||
backgroundColor: '#3b82f6',
|
||||
},
|
||||
saveButton: {
|
||||
backgroundColor: '#10b981',
|
||||
},
|
||||
cancelButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600' as const,
|
||||
color: '#f8fafc',
|
||||
},
|
||||
backButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600' as const,
|
||||
color: '#f8fafc',
|
||||
},
|
||||
nextButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600' as const,
|
||||
color: '#ffffff',
|
||||
},
|
||||
saveButtonText: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600' as const,
|
||||
color: '#ffffff',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user