Files
info-target-mobile/screens/profile/ui/EditServices.tsx
Samandar Turgunboyev ab363ca3b9 bug fixed
2026-03-02 13:22:55 +05:00

200 lines
5.8 KiB
TypeScript

import { useTheme } from '@/components/ThemeContext';
import StepTwo from '@/screens/create-ads/ui/StepTwo';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { ArrowLeft, Loader } from 'lucide-react-native';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Alert, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import { user_api } from '../lib/api';
import StepOneServices from './StepOneService';
type MediaFile = { id?: number; uri: string; type: 'image' | 'video'; name?: string };
interface formDataType {
title: string;
description: string;
media: MediaFile[];
category: any[];
}
const getMimeType = (uri: string) => {
const ext = uri.split('.').pop()?.toLowerCase();
switch (ext) {
case 'jpg':
case 'jpeg':
return 'image/jpeg';
case 'png':
return 'image/png';
case 'webp':
return 'image/webp';
case 'heic':
return 'image/heic';
case 'mp4':
return 'video/mp4';
default:
return 'application/octet-stream';
}
};
export default function EditService() {
const router = useRouter();
const queryClient = useQueryClient();
const { id } = useLocalSearchParams<{ id: string }>();
const { isDark } = useTheme();
const stepOneRef = useRef<any>(null);
const stepTwoRef = useRef<any>(null);
const [step, setStep] = useState(1);
const { t } = useTranslation();
const [formData, setFormData] = useState<formDataType>({
title: '',
description: '',
media: [],
category: [],
});
const [removedFileIds, setRemovedFileIds] = useState<number[]>([]);
const updateForm = (key: string, value: any) =>
setFormData((prev) => ({ ...prev, [key]: value }));
const { data: product, isLoading } = useQuery({
queryKey: ['service_detail', id],
queryFn: () => user_api.detail_service(Number(id)),
select(data) {
return data.data.data;
},
});
useEffect(() => {
if (product) {
setFormData({
title: product.title,
description: product.description,
media: product.files.map((f: any) => ({
id: f.id,
uri: f.file,
type: f.type,
name: f.name || '',
})),
category: product.category,
});
}
}, [product]);
const { mutate, isPending } = useMutation({
mutationFn: (body: FormData) => user_api.update_service({ body, id: Number(id) }),
onSuccess: (res) => {
console.log(res);
queryClient.invalidateQueries({ queryKey: ['my_services'] });
queryClient.invalidateQueries({ queryKey: ['service_detail'] });
router.back();
},
onError: (err: any) => {
console.log(err);
Alert.alert(t('Xatolik yuz berdi'), err?.message || t('Yangilashda xato yuz berdi'));
},
});
const handleNext = () => {
if (step === 1) {
const valid = stepOneRef.current?.validate();
if (!valid) return;
setStep(2);
}
};
const handleBack = () => {
if (step === 2) setStep(1);
else router.back();
};
const handleSave = () => {
const valid = stepTwoRef.current?.validate();
if (!valid) return;
const form = new FormData();
form.append('title', formData.title);
form.append('description', formData.description);
formData.media.forEach((file, i) => {
if (!file.id) {
form.append(`files`, {
uri: file.uri,
type: getMimeType(file.uri),
name: file.uri.split('/').pop(),
} as any);
}
});
removedFileIds.forEach((id) => form.append(`delete_files`, id.toString()));
formData.category.forEach((cat) => form.append(`category`, cat.id));
mutate(form);
};
const removeMedia = (index: number) => {
const file = formData.media[index];
if (file.id) setRemovedFileIds((prev) => [...prev, file.id!]);
updateForm(
'media',
formData.media.filter((_, i) => i !== index)
);
};
return (
<View style={{ flex: 1, backgroundColor: isDark ? '#0f172a' : '#f8fafc' }}>
<View style={[styles.header, { backgroundColor: isDark ? '#0f172a' : '#ffffff' }]}>
<View style={{ flexDirection: 'row', gap: 10, alignItems: 'center' }}>
<Pressable onPress={handleBack}>
<ArrowLeft color={isDark ? '#fff' : '#0f172a'} />
</Pressable>
<Text style={[styles.headerTitle, { color: isDark ? '#fff' : '#0f172a' }]}>
{t('Xizmatni tahrirlash')}
</Text>
</View>
<Pressable
onPress={step === 2 ? handleSave : handleNext}
style={({ pressed }) => ({ opacity: pressed || isPending ? 0.6 : 1 })}
disabled={isPending}
>
{isPending ? (
<Loader color="#3b82f6" size={20} />
) : (
<Text style={styles.saveButton}>{step === 2 ? t('Saqlash') : t('Keyingi')}</Text>
)}
</Pressable>
</View>
<ScrollView contentContainerStyle={styles.container}>
{step === 1 && (
<StepOneServices
ref={stepOneRef}
formData={formData}
updateForm={updateForm}
removeMedia={removeMedia}
/>
)}
{step === 2 && <StepTwo ref={stepTwoRef} formData={formData} updateForm={updateForm} />}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
header: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 16,
alignItems: 'center',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
headerTitle: { fontSize: 18, fontWeight: '700' },
saveButton: { color: '#3b82f6', fontSize: 16, fontWeight: '600' },
container: { padding: 16, paddingBottom: 10 },
});