553 lines
14 KiB
TypeScript
553 lines
14 KiB
TypeScript
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,
|
|
flexGrow: 1
|
|
},
|
|
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: {
|
|
flex: 1,
|
|
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',
|
|
},
|
|
});
|