complated project

This commit is contained in:
Samandar Turgunboyev
2026-02-02 18:51:53 +05:00
parent f0183e4573
commit a7419929f8
57 changed files with 3035 additions and 477 deletions

View File

@@ -0,0 +1,233 @@
import { useTheme } from '@/components/ThemeContext';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'expo-router';
import { ArrowLeft } from 'lucide-react-native';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
ActivityIndicator,
Pressable,
ScrollView,
StyleSheet,
Switch,
Text,
TextInput,
ToastAndroid,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { user_api } from '../lib/api';
type FormType = {
code: string;
referral_share: string;
description: string;
is_agent: boolean;
};
export default function CreateReferrals() {
const { isDark } = useTheme();
const { t } = useTranslation();
const router = useRouter();
const queryClient = useQueryClient();
const [form, setForm] = useState<FormType>({
code: '',
referral_share: '',
description: '',
is_agent: false,
});
const { mutate, isPending } = useMutation({
mutationFn: (body: {
code: string;
referral_share: number;
description: string;
is_agent: boolean;
}) => user_api.create_referral(body),
onSuccess: () => {
ToastAndroid.show(t('Referral yaratildi'), ToastAndroid.SHORT);
queryClient.refetchQueries({ queryKey: ['my_referrals'] });
router.back();
},
onError: () => {
ToastAndroid.show(t('Xatolik yuz berdi'), ToastAndroid.SHORT);
},
});
const [errors, setErrors] = useState<any>({});
const update = (key: keyof FormType, value: any) => setForm((p) => ({ ...p, [key]: value }));
const validate = () => {
const e: any = {};
if (!form.code || form.code.length !== 9)
e.code = 'Kod aynan 9 ta belgidan iborat bolishi kerak';
if (!form.description || form.description.length < 5)
e.description = 'Tavsif kamida 5 ta belgidan iborat bolishi kerak';
if (form.is_agent) {
if (!form.referral_share || Number(form.referral_share) <= 0)
e.referral_share = 'Agent uchun foiz majburiy';
}
setErrors(e);
return Object.keys(e).length === 0;
};
const handleSave = () => {
if (!validate()) return;
const payload = {
code: form.code,
referral_share: form.is_agent ? Number(form.referral_share) : 0,
description: form.description,
is_agent: form.is_agent,
};
mutate(payload);
};
return (
<SafeAreaView style={{ flex: 1, backgroundColor: isDark ? '#0f172a' : '#f8fafc' }}>
{/* HEADER */}
<View style={[styles.header, { backgroundColor: isDark ? '#0f172a' : '#fff' }]}>
<Pressable onPress={() => router.back()}>
<ArrowLeft color={isDark ? '#fff' : '#0f172a'} />
</Pressable>
<Text style={[styles.headerTitle, { color: isDark ? '#fff' : '#0f172a' }]}>
{t('Referral yaratish')}
</Text>
<Pressable onPress={handleSave}>
{isPending ? (
<ActivityIndicator size={'small'} />
) : (
<Text style={styles.save}>{t('Saqlash')}</Text>
)}
</Pressable>
</View>
<ScrollView contentContainerStyle={styles.container}>
{/* NOM */}
<Text style={[styles.label, { color: isDark ? '#fff' : '#0f172a' }]}>
{t('Referral nomi')}
</Text>
<View style={[styles.inputBox, theme(isDark)]}>
<TextInput
maxLength={9}
style={[styles.input, { color: isDark ? '#fff' : '#0f172a' }]}
placeholder="ABC123"
placeholderTextColor="#94a3b8"
value={form.code}
onChangeText={(v) => update('code', v)}
/>
</View>
{errors.code && <Text style={styles.error}>{t(errors.code)}</Text>}
{/* TAVSIF */}
<Text style={[styles.label, { color: isDark ? '#fff' : '#0f172a' }]}>{t('Tavsif')}</Text>
<View style={[styles.inputBox, styles.textArea, theme(isDark)]}>
<TextInput
multiline
textAlignVertical="top"
style={[styles.input, { color: isDark ? '#fff' : '#0f172a' }]}
placeholder={t('Batafsil yozing...')}
placeholderTextColor="#94a3b8"
value={form.description}
onChangeText={(v) => update('description', v)}
/>
</View>
{errors.description && <Text style={styles.error}>{t(errors.description)}</Text>}
{/* AGENT SWITCH */}
<View style={styles.switchRow}>
<Text style={[styles.label, { color: isDark ? '#fff' : '#0f172a' }]}>
{t('Agentmi?')}
</Text>
<Switch
value={form.is_agent}
onValueChange={(v) => {
update('is_agent', v);
if (!v) update('referral_share', '');
}}
/>
</View>
{/* 👉 FOIZ FAQAT AGENT YOQILGANDA */}
{form.is_agent && (
<>
<Text style={[styles.label, { color: isDark ? '#fff' : '#0f172a' }]}>
{t('Referral foizi (%)')}
</Text>
<View style={[styles.inputBox, theme(isDark)]}>
<TextInput
maxLength={1}
keyboardType="numeric"
style={[styles.input, { color: isDark ? '#fff' : '#0f172a' }]}
placeholder="5"
placeholderTextColor="#94a3b8"
value={form.referral_share}
onChangeText={(v) => {
// faqat 15 oraligini qabul qiladi
if (v === '') {
update('referral_share', '');
return;
}
const num = Number(v);
if (num >= 1 && num <= 5) {
update('referral_share', v);
}
}}
/>
</View>
{errors.referral_share && <Text style={styles.error}>{t(errors.referral_share)}</Text>}
</>
)}
</ScrollView>
</SafeAreaView>
);
}
const theme = (isDark: boolean) => ({
backgroundColor: isDark ? '#1e293b' : '#fff',
borderColor: isDark ? '#334155' : '#e2e8f0',
});
const styles = StyleSheet.create({
header: {
padding: 16,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
elevation: 3,
},
headerTitle: { fontSize: 18, fontWeight: '700' },
save: { color: '#3b82f6', fontSize: 16, fontWeight: '600' },
container: { padding: 16, gap: 10 },
label: { fontSize: 15, fontWeight: '700' },
error: { color: '#ef4444', fontSize: 13, marginLeft: 6 },
inputBox: {
flexDirection: 'row',
borderRadius: 16,
borderWidth: 1,
paddingHorizontal: 6,
height: 56,
},
textArea: { height: 120, alignItems: 'flex-start' },
input: {
flex: 1,
fontSize: 16,
},
switchRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 10,
},
});