complated project
This commit is contained in:
233
screens/profile/ui/CreateReferrals.tsx
Normal file
233
screens/profile/ui/CreateReferrals.tsx
Normal 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 bo‘lishi kerak';
|
||||
if (!form.description || form.description.length < 5)
|
||||
e.description = 'Tavsif kamida 5 ta belgidan iborat bo‘lishi 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 1–5 oralig‘ini 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,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user