government ui complated

This commit is contained in:
Samandar Turgunboyev
2026-02-05 16:09:03 +05:00
parent 5d31fe8ff4
commit 754f11804a
76 changed files with 2459 additions and 672 deletions

View File

@@ -1,190 +1,395 @@
import { useTheme } from '@/components/ThemeContext';
import { ResizeMode, Video } from 'expo-av';
import { useRouter } from 'expo-router';
import { ArrowLeft } from 'lucide-react-native';
import React, { useState } from 'react';
import { router } from 'expo-router';
import { VideoView, useVideoPlayer } from 'expo-video';
import { ArrowLeft, Check, ChevronDown, X } from 'lucide-react-native';
import React, { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
Dimensions,
FlatList,
Image,
Pressable,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import ImageViewer from 'react-native-image-zoom-viewer';
import Modal from 'react-native-modal';
const { width } = Dimensions.get('window');
type ManualStep = {
title: string;
text: string;
image?: any;
image: any;
description?: string;
};
type Language = {
code: 'uz' | 'ru' | 'en';
label: string;
flag: string;
};
const languages: Language[] = [
{ code: 'uz', label: "O'zbekcha", flag: '🇺🇿' },
{ code: 'ru', label: 'Русский', flag: '🇷🇺' },
{ code: 'en', label: 'English', flag: '🇬🇧' },
];
export function ManualTab() {
const { isDark } = useTheme();
const { t } = useTranslation();
const router = useRouter();
const [videoLang, setVideoLang] = useState<'uz' | 'ru' | 'en'>('uz');
const { t, i18n } = useTranslation();
/** 🔹 Modal states */
const [imageVisible, setImageVisible] = useState(false);
const [imageIndex, setImageIndex] = useState(0);
const [langPickerVisible, setLangPickerVisible] = useState(false);
/** 🔹 Video tili (SELECT uchun) */
const userLang = i18n.language.startsWith('ru')
? 'ru'
: i18n.language.startsWith('en')
? 'en'
: 'uz';
const [selectedLang, setSelectedLang] = useState<'uz' | 'ru' | 'en'>(userLang);
/** 🔹 RASM tili (faqat app tili boyicha) */
const imageLang: 'uz' | 'ru' | 'en' = userLang;
/** 🔹 Theme */
const theme = {
background: isDark ? '#0f172a' : '#f8fafc',
cardBg: isDark ? '#1e293b' : '#ffffff',
text: isDark ? '#ffffff' : '#0f172a',
textSecondary: isDark ? '#94a3b8' : '#64748b',
primary: '#3b82f6',
textMuted: isDark ? '#94a3b8' : '#64748b',
border: isDark ? '#334155' : '#e2e8f0',
};
const steps: ManualStep[] = [
{
title: "Foydalanish qo'lanmasi",
text: "Foydalanish qo'lanmasi",
image: require('@/assets/manual/step1.jpg'),
},
{
title: "Ro'yxatdan o'tish (Registratsiya) 1 daqiqa ichida",
text: "Platformaga kirish uchun avval ro'yxatdan o'ting.",
image: require('@/assets/manual/step2.jpg'),
},
{
title: "Profilni to'ldirish va tasdiqlash",
text: "Muhim: Ro'yxatdan o'tgandan keyin profil to'liq bo'lishi kerak — aks holda platforma cheklangan rejimda ishlaydi.",
image: require('@/assets/manual/step3.jpg'),
},
{
title: "Xodimlarni qo'shish",
text: "Profil ichida Xodimlar bo'limiga o'ting va + tugmasi orqali xodim qo'shing.",
image: require('@/assets/manual/step4.jpg'),
},
{
title: "E'lon berish va o'z mahsulot/xizmatlaringizni ko'rsatish",
text: "Pastki menyudan E'lonlar bo'limiga o'ting va yangi e'lon yarating.",
image: require('@/assets/manual/step5.jpg'),
},
{
title: 'Mijozlarni qidirish va topish',
text: 'Filtrdan foydalanib kerakli kompaniyalarni toping va profiliga kirib xabar yuboring.',
image: require('@/assets/manual/step6.jpg'),
},
{
title: 'Muhim maslahatlar va xavfsizlik',
text: 'Faqat tasdiqlangan profillarga ishonch bilan murojaat qiling.',
image: require('@/assets/manual/step7.jpg'),
},
];
const videos: Record<'uz' | 'ru' | 'en', any> = {
/** 🔹 Video manbalari (SELECT ga bogliq) */
const videos = {
uz: require('@/assets/manual/manual_video_uz.mp4'),
ru: require('@/assets/manual/manual_video_ru.mp4'),
en: require('@/assets/manual/manual_video_en.mp4'),
};
const handleVideoChange = (lang: 'uz' | 'ru' | 'en') => {
setVideoLang(lang);
};
const player = useVideoPlayer(videos[selectedLang], (player) => {
player.loop = false;
});
/** 🔹 Rasmlar (FAqat imageLang) */
const steps: ManualStep[] = [
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step1.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step1.jpg')
: require('@/assets/manual/image_en/step1.jpg'),
description: t("Ilovani oching va ro'yxatdan o'ting"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step2.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step2.jpg')
: require('@/assets/manual/image_en/step2.jpg'),
description: t("Profilni to'ldiring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step3.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step3.jpg')
: require('@/assets/manual/image_en/step3.jpg'),
description: t("To'lov usulini qo'shing"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step4.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step4.jpg')
: require('@/assets/manual/image_en/step4.jpg'),
description: t('Xaridni tanlang'),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step5.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step5.jpg')
: require('@/assets/manual/image_en/step5.jpg'),
description: t("To'lovni amalga oshiring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step6.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step6.jpg')
: require('@/assets/manual/image_en/step6.jpg'),
description: t("Natijani ko'ring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step7.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step7.jpg')
: require('@/assets/manual/image_en/step7.jpg'),
description: t("Natijani ko'ring"),
},
{
image:
imageLang === 'uz'
? require('@/assets/manual/image_uz/step8.jpg')
: imageLang === 'ru'
? require('@/assets/manual/image_ru/step8.jpg')
: require('@/assets/manual/image_en/step8.jpg'),
description: t("Natijani ko'ring"),
},
];
/** 🔹 Image viewer */
const images = useMemo(
() => steps.map((step) => ({ url: '', props: { source: step.image } })),
[imageLang]
);
const selectedLanguage = languages.find((l) => l.code === selectedLang);
const renderStep = ({ item, index }: { item: ManualStep; index: number }) => (
<Pressable
onPress={() => {
setImageIndex(index);
setImageVisible(true);
}}
>
<View style={[styles.stepCard, { backgroundColor: theme.cardBg }]}>
<Image source={item.image} style={styles.stepImage} />
</View>
</Pressable>
);
return (
<ScrollView style={[styles.container, { backgroundColor: theme.background }]}>
<View style={[styles.header, { backgroundColor: isDark ? '#0f172a' : '#fff' }]}>
<Pressable onPress={() => router.push('/profile')}>
<ArrowLeft color={isDark ? '#fff' : '#0f172a'} />
</Pressable>
{/* HEADER */}
<View style={styles.hero}>
<View style={styles.topHeader}>
<Pressable onPress={() => router.back()}>
<ArrowLeft color={theme.text} size={24} />
</Pressable>
<Text style={[styles.headerTitle, { color: theme.text }]}>
{t("Foydalanish qo'llanmasi")}
</Text>
</View>
<Text style={[styles.headerTitle, { color: isDark ? '#fff' : '#0f172a' }]}>
{t("Foydalanish qo'lanmasi")}
<Text style={[styles.subtitle, { color: theme.textMuted }]}>
{t("Quyidagi qisqa video yoki rasmlarni ko'rib chiqing")}
</Text>
</View>
{steps.map((step, index) => (
<View key={index} style={[styles.card, { backgroundColor: theme.cardBg }]}>
<Text style={[styles.headerTitle, { color: theme.text }]}>{t(step.title)}</Text>
<Text style={[styles.text, { color: theme.textSecondary, marginTop: 8 }]}>
{t(step.text)}
</Text>
{step.image && <Image source={step.image} style={styles.image} resizeMode="contain" />}
</View>
))}
{/* Video bo'limi */}
<View style={[styles.card, { backgroundColor: theme.cardBg }]}>
<Text style={[styles.headerTitle, { color: theme.text }]}>{t("Qo'llanma video")}</Text>
{/* Til tanlash tugmalari */}
<View style={styles.buttonRow}>
<TouchableOpacity
{/* VIDEO */}
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.text }]}>
{t("Foydalanish video qo'llanma")}
</Text>
<View style={{ alignContent: 'flex-end', alignItems: 'flex-end', paddingHorizontal: 16 }}>
<Pressable
style={[
styles.langButton,
{
backgroundColor: videoLang === 'uz' ? theme.primary : theme.cardBg,
borderColor: theme.primary,
},
styles.langSelector,
{ backgroundColor: theme.cardBg, borderColor: theme.border },
]}
onPress={() => handleVideoChange('uz')}
onPress={() => setLangPickerVisible(true)}
>
<Text style={{ color: videoLang === 'uz' ? '#fff' : theme.text }}>O'zbek</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.langButton,
{
backgroundColor: videoLang === 'ru' ? theme.primary : theme.cardBg,
borderColor: theme.primary,
},
]}
onPress={() => handleVideoChange('ru')}
>
<Text style={{ color: videoLang === 'ru' ? '#fff' : theme.text }}>Русский</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.langButton,
{
backgroundColor: videoLang === 'en' ? theme.primary : theme.cardBg,
borderColor: theme.primary,
},
]}
onPress={() => handleVideoChange('en')}
>
<Text style={{ color: videoLang === 'en' ? '#fff' : theme.text }}>English</Text>
</TouchableOpacity>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ fontSize: 20 }}>{selectedLanguage?.flag}</Text>
</View>
<ChevronDown color={theme.textMuted} />
</Pressable>
</View>
<Video
source={videos[videoLang]}
style={styles.video}
useNativeControls
isLooping
resizeMode={ResizeMode.COVER}
<View style={[styles.videoCard, { backgroundColor: theme.cardBg }]}>
<VideoView
player={player}
style={styles.video}
allowsFullscreen
allowsPictureInPicture
contentFit="cover"
/>
</View>
</View>
{/* RASMLAR */}
<View style={styles.section}>
<Text style={[styles.sectionTitle, { color: theme.text }]}>
{t('Foydalanish rasm qollanma')}
</Text>
<FlatList
horizontal
data={steps}
renderItem={renderStep}
showsHorizontalScrollIndicator={false}
contentContainerStyle={{ paddingHorizontal: 16, gap: 20 }}
/>
</View>
{/* IMAGE MODAL */}
<Modal isVisible={imageVisible} style={{ margin: 0 }}>
<ImageViewer
imageUrls={images}
index={imageIndex}
enableSwipeDown
onSwipeDown={() => setImageVisible(false)}
renderHeader={() => (
<Pressable style={styles.closeButton} onPress={() => setImageVisible(false)}>
<X size={32} color="#fff" />
</Pressable>
)}
/>
</Modal>
{/* LANGUAGE MODAL */}
<Modal isVisible={langPickerVisible} onBackdropPress={() => setLangPickerVisible(false)}>
<View style={[styles.langModalContent, { backgroundColor: theme.cardBg }]}>
<Text style={{ color: 'white', marginBottom: 10, fontSize: 18 }}>
{t('Video tilini tanlang')}
</Text>
{languages.map((lang) => {
const isSelected = selectedLang === lang.code;
return (
<Pressable
key={lang.code}
onPress={() => {
setSelectedLang(lang.code);
setLangPickerVisible(false);
}}
style={[
styles.langOption,
{
borderColor: isSelected ? theme.primary : theme.border,
backgroundColor: isSelected ? theme.primary + '15' : 'transparent',
},
]}
>
<View style={styles.langOptionLeft}>
<Text style={{ fontSize: 26 }}>{lang.flag}</Text>
<Text
style={{
color: isSelected ? theme.primary : theme.text,
fontWeight: isSelected ? '700' : '500',
fontSize: 16,
}}
>
{lang.label}
</Text>
</View>
{isSelected && (
<View style={[styles.checkmark, { backgroundColor: theme.primary }]}>
<Check size={20} color={'white'} />
</View>
)}
</Pressable>
);
})}
</View>
</Modal>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
card: { borderRadius: 16, padding: 16, marginBottom: 16 },
headerTitle: { fontSize: 18, fontWeight: '700', lineHeight: 24 },
text: { fontSize: 14, lineHeight: 20 },
image: { width: width - 64, height: 200, marginTop: 12, borderRadius: 12 },
video: { width: width - 64, height: 320, marginTop: 12, borderRadius: 12 },
buttonRow: { flexDirection: 'row', justifyContent: 'space-around', marginVertical: 12 },
langButton: {
paddingVertical: 8,
paddingHorizontal: 16,
borderRadius: 8,
borderWidth: 1,
container: { flex: 1 },
hero: { padding: 20 },
topHeader: { flexDirection: 'row', alignItems: 'center', gap: 12 },
headerTitle: { fontSize: 22, fontWeight: '700' },
subtitle: { marginTop: 8 },
section: { marginBottom: 28 },
sectionTitle: { fontSize: 20, fontWeight: '700', marginLeft: 16 },
langSelector: {
margin: 5,
width: 80,
gap: 5,
paddingVertical: 5,
borderRadius: 14,
borderWidth: 1.5,
alignContent: 'center',
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'center',
},
header: {
padding: 16,
videoCard: {
marginHorizontal: 16,
borderRadius: 20,
overflow: 'hidden',
position: 'relative',
},
video: { width: '100%', aspectRatio: 9 / 13 },
stepCard: { borderRadius: 16, overflow: 'hidden' },
stepImage: { width: 300, height: 300 },
closeButton: {
position: 'absolute',
top: 50,
right: 20,
padding: 8,
zIndex: 50,
backgroundColor: 'rgba(0,0,0,0.5)',
borderRadius: 20,
},
langModalContent: {
padding: 20,
borderRadius: 20,
},
langOption: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
gap: 10,
elevation: 3,
justifyContent: 'space-between',
paddingVertical: 14,
paddingHorizontal: 16,
borderRadius: 14,
borderWidth: 1.5,
marginBottom: 10,
},
langOptionLeft: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
checkmark: {
width: 24,
height: 24,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
},
checkmarkText: {
color: '#fff',
fontSize: 14,
fontWeight: 'bold',
},
playOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'rgba(0,0,0,0.25)',
},
});