fitst commit
This commit is contained in:
200
screens/profile/ui/EditServices.tsx
Normal file
200
screens/profile/ui/EditServices.tsx
Normal file
@@ -0,0 +1,200 @@
|
||||
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 { SafeAreaView } from 'react-native-safe-area-context';
|
||||
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 (
|
||||
<SafeAreaView 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' }]}>
|
||||
{step === 1 ? t('Xizmatni tahrirlash (1/2)') : t('Xizmatni tahrirlash (2/2)')}
|
||||
</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>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
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 },
|
||||
});
|
||||
Reference in New Issue
Block a user