0% found this document useful (0 votes)
13 views22 pages

funcionts

The document contains a React component for editing lottery themes, including functions for image uploads and validations to ensure at least one hero and details image is uploaded. It utilizes hooks for form management, state management, and API interactions for fetching and updating lottery themes. Additionally, it handles user feedback through toast messages and includes conditional rendering based on the device type (desktop or mobile).
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views22 pages

funcionts

The document contains a React component for editing lottery themes, including functions for image uploads and validations to ensure at least one hero and details image is uploaded. It utilizes hooks for form management, state management, and API interactions for fetching and updating lottery themes. Additionally, it handles user feedback through toast messages and includes conditional rendering based on the device type (desktop or mobile).
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 22

funcio para verificar que haya subido una imagen de hero o details minimo:

if (
(!uploadedImageUrlHeroDesktop && !uploadedImageUrlHeroMobile) ||
(!uploadedImageUrlDetailsDesktop && !uploadedImageUrlDetailsMobile)
) {
setToastMessage(t('uploadOneImage'));
return;
}

funcion para guardar imsagenes en url modificada:

import {uploadImage} from '@lottob/utils/upload-image';


import type {EditLotterySchemaParams as OriginalEditLotterySchemaParams} from
'src/app/main/lotteries/edit/edit-lottery-schema';

type PrizeWithNewProps = {
enabled: boolean;
hasMultiplier: boolean;
hasRaffle: boolean;
hasFreeBet: boolean;
} & Omit<OriginalEditLotterySchemaParams['prizes'][number], 'risk' |
'taxAndCashDiscount' | 'selfInsuredCost'>;

type EditLotterySchemaParams = Omit<OriginalEditLotterySchemaParams, 'prizes'> & {


prizes: PrizeWithNewProps[];
};

export class LotteryUseCase {


static async toPersistence(input: EditLotterySchemaParams) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
input.prizes.forEach((prize: any) => {
prize.enabled = true;
prize.hasMultiplier = false;
prize.hasRaffle = false;
prize.hasFreeBet = false;

delete prize.risk;
delete prize.taxAndCashDiscount;
delete prize.selfInsuredCost;

if (prize.insuranceProviderCosts) {
prize.insuranceProviderCosts =
prize.insuranceProviderCosts.map((cost) => {
const {totalCost, ...rest} = cost;
return rest;
});
}

return prize;
});

input.countryPricingThresholds.forEach((threshold) => {
threshold.pricingThresholds.reverse();

threshold.pricingThresholds.forEach((pricing) => {
if (pricing.to.amount === null) pricing.to = null;
});
});

input.purchaseStrategiesThresholds.forEach((strategy) => {
if (strategy?.to?.amount === null) strategy.to = null;
});

const newData = {
...input,
metadata: {
preselectedLines: input.preselectedLines,
},
};

const logo = await uploadImage(input.logoUrl, true);

newData.logoUrl = logo;

delete newData.preselectedLines;

return newData;
}
}

condicion para evitar loteria existente se repita

if (currentTheme) {
const err = new Error('Lottery is already exist');

setToastMessage(t('lotteryExist'));

logger.error('Lottery is already exist', {currentTheme}, err);

throw err;
}

return (
<FormProvider {...methods}>
<FusePageCarded
header={<LotteryThemesEditHeader />}
content={
<Box className="p-16 sm:p-24 max-w-lg">
<Select
name="lotteryId"
value={lotteryId}
labelId="lottery-select-label"
disabled
displayEmpty
sx={{width: '400px', mb: '30px'}}
>
<MenuItem id="lottery-select-label" value="">
{t('lottery')}
</MenuItem>

{renderLotteries}
</Select>
<RadioGroup
name="deviceType"
value={deviceType}
onChange={(e) => setDeviceType(e.target.value as
'desktop' | 'mobile')}
sx={{flexDirection: 'row', m: '0px'}}
>
<FormControlLabel
value="desktop"
control={<Radio />}
label={t('desktop')}
labelPlacement="start"
/>
<FormControlLabel
value="mobile"
control={<Radio />}
label={t('mobile')}
labelPlacement="start"
/>
</RadioGroup>

<Typography className="text-16 sm:text-20 font-semibold"


sx={{mt: '30px'}}>
{t('imageAsset')}
</Typography>
<Box sx={{display: 'flex', flexDirection: 'row', gap:
'40px'}}>
<Box sx={{mt: '30px'}}>
<Typography className="text-12" color="primary">
{t('heroBanner')}
</Typography>
<Controller
name="imageHeroBanner"
control={control}
render={() => (
<Button
variant="outlined"
component="label"
sx={{
mt: 2,
borderRadius: '5px',
border: 'none',
backgroundColor: '#f5f5f5',
pt: (
deviceType === 'desktop'
?
uploadedImageUrlHeroDesktop
:
uploadedImageUrlHeroMobile
)
? '90px'
: '30px',
pb: (
deviceType === 'desktop'
?
uploadedImageUrlHeroDesktop
:
uploadedImageUrlHeroMobile
)
? '90px'
: '30px',
width: '300px',
height: '150px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden',
...(deviceType === 'desktop' &&
uploadedImageUrlHeroDesktop &&
{
width: '100%',
height: '100%',
}),
...(deviceType === 'mobile' &&
uploadedImageUrlHeroMobile && {
width: '100%',
height: '100%',
}),
}}
>
{(
deviceType === 'desktop'
? uploadedImageUrlHeroDesktop
: uploadedImageUrlHeroMobile
) ? (
<img
src={
deviceType === 'desktop'
?
uploadedImageUrlHeroDesktop.url
:
uploadedImageUrlHeroMobile.url
}
alt="Uploaded preview"
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: '8px',
minWidth: '150px',
minHeight: '150px',
}}
/>
) : (
t('uploadPicture')
)}
<input
accept="image/*"
className="hidden"
id="button-file"
type="file"
onChange={async (e) => {
const file = e.target.files?.
[0];
if (file) {
const uploadedImageUrl =
await readFileAsync(file);
if (deviceType ===
'desktop') {

setUploadedImageUrlHeroDesktop(uploadedImageUrl);
} else {

setUploadedImageUrlHeroMobile(uploadedImageUrl);
}
}
}}
/>
</Button>
)}
/>
</Box>
<Box sx={{mt: '30px'}}>
<Typography className="text-12" color="primary">
{t('details')}
</Typography>
<Controller
name="imageDetails"
control={control}
render={() => (
<Button
variant="outlined"
component="label"
sx={{
mt: 2,
borderRadius: '5px',
border: 'none',
backgroundColor: '#f5f5f5',
pt: (
deviceType === 'desktop'
?
uploadedImageUrlDetailsDesktop
:
uploadedImageUrlDetailsMobile
)
? '90px'
: '30px',
pb: (
deviceType === 'desktop'
?
uploadedImageUrlDetailsDesktop
:
uploadedImageUrlDetailsMobile
)
? '90px'
: '30px',
width: '300px',
height: '150px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden',
...(deviceType === 'desktop' &&
uploadedImageUrlDetailsDesktop
&& {
width: '100%',
height: '100%',
}),
...(deviceType === 'mobile' &&
uploadedImageUrlDetailsMobile
&& {
width: '100%',
height: '100%',
}),
}}
>
{(
deviceType === 'desktop'
?
uploadedImageUrlDetailsDesktop
: uploadedImageUrlDetailsMobile
) ? (
<img
src={
deviceType === 'desktop'
?
uploadedImageUrlDetailsDesktop.url
:
uploadedImageUrlDetailsMobile.url
}
alt="Uploaded preview"
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: '8px',
minWidth: '150px',
minHeight: '150px',
}}
/>
) : (
t('uploadPicture')
)}
<input
accept="image/*"
className="hidden"
id="button-file"
type="file"
onChange={async (e) => {
const file = e.target.files?.
[0];
if (file) {
const uploadedImageUrl =
await readFileAsync(file);
if (deviceType ===
'desktop') {

setUploadedImageUrlDetailsDesktop(uploadedImageUrl);
} else {

setUploadedImageUrlDetailsMobile(uploadedImageUrl);
}
}
}}
/>
</Button>
)}
/>
</Box>
</Box>

<Button
className="mt-40"
variant="contained"
color="primary"
onClick={handleUpdateTheme}
disabled={isLoading}
sx={{borderRadius: '5px'}}
>
{isLoading ? <CircularProgress size={24}
color="inherit" /> : t('save')}
</Button>
</Box>
}
/>
<Snackbar
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
open={!!toastMessage}
autoHideDuration={3000}
onClose={() => setToastMessage(null)}
message={toastMessage}
/>
</FormProvider>
);
}
import FuseLoading from '@fuse/core/FuseLoading';
import FusePageCarded from '@fuse/core/FusePageCarded';
import {useForm, FormProvider, Controller} from 'react-hook-form';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import {useTranslation} from 'react-i18next';
import {
Snackbar,
Box,
RadioGroup,
FormControlLabel,
Radio,
Button,
Select,
MenuItem,
Typography,
CircularProgress,
} from '@mui/material';
import {useState, useMemo, useEffect} from 'react';
import {zodResolver} from '@hookform/resolvers/zod';
import {lotteryThemeSchema, TUpdateConfigurationForm} from './lottery-theme-edit-
schema';
import {fetchLotteries} from '@lottob/graphql/queries/lotteries-id';
import {updateTheme} from '@lottob/graphql/mutations/update-lottery-theme';
import {UpdateConfigurationDto} from '@lottob/gql/graphql';
import {LotteryThemesEditHeader} from './components/LotteryThemesEditHeader';
import FuseUtils from '@fuse/utils';
import {fetchLotteryThemeByName} from '@lottob/graphql/queries/lottery-value-theme-
by-name';
import {ILotteryThemeDatum} from '../list/LotteryThemesTable';
import {useNavigate, useParams} from 'react-router';
import {uploadImageBase64} from '@lottob/utils/upload-image';
import {readFileAsync, TUploadedImage} from '@lottob/utils/convertFileToBase64';

type TErrorResponse = {
response?: {
errors: {message: string}[];
};
};
interface IThemeDeviceConfig {
heroBanner: {
src: string | null;
title: string;
};
detailsImage: {
src: string | null;
title: string;
};
}
interface ITheme {
lotteryId: string;
desktop?: IThemeDeviceConfig;
mobile?: IThemeDeviceConfig;
}

export default function EditThemePage() {


const {t} = useTranslation('lotteryThemePage');
const navigate = useNavigate();
const params = useParams();
const {lotteryId} = params;

const queryClient = useQueryClient();


const [toastMessage, setToastMessage] = useState<string | null>(null);
const [deviceType, setDeviceType] = useState<'desktop' | 'mobile'>('desktop');
const [uploadedImageUrlHeroDesktop, setUploadedImageUrlHeroDesktop] =
useState<TUploadedImage | null>(null);
const [uploadedImageUrlDetailsDesktop, setUploadedImageUrlDetailsDesktop] =
useState<TUploadedImage | null>(null);
const [uploadedImageUrlHeroMobile, setUploadedImageUrlHeroMobile] =
useState<TUploadedImage | null>(null);
const [uploadedImageUrlDetailsMobile, setUploadedImageUrlDetailsMobile] =
useState<TUploadedImage | null>(null);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [currentTheme, setCurrentTheme] = useState<ITheme | undefined>();
const [isSaved, setIsSaved] = useState(false);

const methods = useForm<TUpdateConfigurationForm>({


mode: 'onSubmit',
resolver: zodResolver(lotteryThemeSchema),
});

const {control} = methods;

// Fetch themes for the select dropdown


const {data: lotteries, isLoading: loadingLotteries} = useQuery({
queryKey: ['lotteries'],
queryFn: async () => {
const response = await fetchLotteries();
return response;
},
});

// Mutation to update a theme


const {mutateAsync: createThemeMutation, isPending: isLoading} = useMutation({
mutationFn: async (variables: UpdateConfigurationDto) => await
updateTheme({dto: variables}),
onSuccess: () => {
queryClient.invalidateQueries({queryKey: ['lotteryTheme']});
},
onError: (error: TErrorResponse) => {
const message = (error?.response?.errors[0]?.message as string) ||
t('somethingWrong');
setToastMessage(message);
},
});

const fetchTheme = async () => await fetchLotteryThemeByName({dto: {name:


'lottery_themes'}});

const {data: lotteryThemes} = useQuery({


queryKey: ['lotteryTheme'],
queryFn: () => fetchTheme(),
select: (data) => {
const values = JSON.parse(data.configuration.value) as
ILotteryThemeDatum[];

return values.map((value) => {


return {configurationName: data.configuration.name, ...value};
});
},
});

useEffect(() => {
const theme = lotteryThemes?.find((t) => t.lotteryId === lotteryId);
if (theme) {
setCurrentTheme({
...theme,
desktop: {
heroBanner: {
src: theme.desktop?.heroBanner?.src || null,
title: theme.desktop?.heroBanner?.title || 'filename',
},
detailsImage: {
src: theme.desktop?.detailsImage?.src || null,
title: theme.desktop?.detailsImage?.title || 'filename',
},
},
mobile: {
heroBanner: {
src: theme.mobile?.heroBanner?.src || null,
title: theme.mobile?.heroBanner?.title || 'filename',
},
detailsImage: {
src: theme.mobile?.detailsImage?.src || null,
title: theme.mobile?.detailsImage?.title || 'filename',
},
},
});

const heroBanner = theme[deviceType]?.heroBanner?.src || null;


const detailsImage = theme[deviceType]?.detailsImage?.src || null;

if (deviceType === 'desktop') {


setUploadedImageUrlHeroDesktop(
heroBanner
? {
configurationName: 'heroBanner',
url: heroBanner,
type: 'image',
base64: '',
fileName: 'filename',
}
: null
);
setUploadedImageUrlDetailsDesktop(
detailsImage
? {
configurationName: 'detailsImage',
url: detailsImage,
type: 'image',
base64: '',
fileName: 'filename',
}
: null
);
} else {
setUploadedImageUrlHeroMobile(
heroBanner
? {
configurationName: 'heroBanner',
url: heroBanner,
type: 'image',
base64: '',
fileName: 'filename',
}
: null
);
setUploadedImageUrlDetailsMobile(
detailsImage
? {
configurationName: 'detailsImage',
url: detailsImage,
type: 'image',
base64: '',
fileName: 'filename',
}
: null
);
}
}
}, [lotteryId, lotteryThemes, deviceType]);

const handleUpdateTheme = async () => {


if (!lotteryId) {
setToastMessage(t('lotterySelect'));
return;
}

const currentHeroImage = deviceType === 'desktop' ?


uploadedImageUrlHeroDesktop : uploadedImageUrlHeroMobile;
const currentDetailsImage =
deviceType === 'desktop' ? uploadedImageUrlDetailsDesktop :
uploadedImageUrlDetailsMobile;

// Check if the images are available


if (!currentHeroImage && !currentDetailsImage) {
setToastMessage(t('uploadOneImage'));
return;
}

try {
// Only upload images that are available
const imageHeroDesktop = uploadedImageUrlHeroDesktop
? await uploadImageBase64(uploadedImageUrlHeroDesktop.base64,
uploadedImageUrlHeroDesktop.fileName)
: null;

const imageHeroMobile = uploadedImageUrlHeroMobile


? await uploadImageBase64(uploadedImageUrlHeroMobile.base64,
uploadedImageUrlHeroMobile.fileName)
: null;

const imageDetailsDesktop = uploadedImageUrlDetailsDesktop


? await uploadImageBase64(
uploadedImageUrlDetailsDesktop.base64,
uploadedImageUrlDetailsDesktop.fileName
)
: null;

const imageDetailsMobile = uploadedImageUrlDetailsMobile


? await uploadImageBase64(uploadedImageUrlDetailsMobile.base64,
uploadedImageUrlDetailsMobile.fileName)
: null;

// Get current configuration


const configuration = await fetchTheme();

// Review current values


const rawConfigurationValue: string = configuration.configuration.value
|| '[]';
let lotteriesThemesList: ITheme[];
try {
lotteriesThemesList = JSON.parse(rawConfigurationValue);
if (!Array.isArray(lotteriesThemesList)) {
console.error('lotteriesThemesList is not an array:',
lotteriesThemesList);
lotteriesThemesList = [];
}
} catch (parseError) {
console.error('Error parsing configuration value:', parseError,
rawConfigurationValue);
lotteriesThemesList = [];
}

// Find and update current theme


const themeIndex = lotteriesThemesList.findIndex((theme) =>
theme.lotteryId === lotteryId);
let currentTheme: ITheme;

if (themeIndex !== -1) {


currentTheme = lotteriesThemesList[themeIndex];
} else {
currentTheme = {
lotteryId,
desktop: {
heroBanner: {src: '', title: ''},
detailsImage: {src: '', title: ''},
},
mobile: {
heroBanner: {src: '', title: ''},
detailsImage: {src: '', title: ''},
},
};
lotteriesThemesList.push(currentTheme);
}

// Keep existing images for both devices


const updatedDesktop = {
heroBanner: {
src: imageHeroDesktop || currentTheme.desktop?.heroBanner?.src
|| '',
title: '',
},
detailsImage: {
src: imageDetailsDesktop ||
currentTheme.desktop?.detailsImage?.src || '',
title: '',
},
};

const updatedMobile = {
heroBanner: {
src: imageHeroMobile || currentTheme.mobile?.heroBanner?.src ||
'',
title: '',
},
detailsImage: {
src: imageDetailsMobile ||
currentTheme.mobile?.detailsImage?.src || '',
title: '',
},
};

//lotteriesThemesList[themeIndex] = currentTheme;
// Update the theme with new images
currentTheme.desktop = updatedDesktop;
currentTheme.mobile = updatedMobile;

// Create payload with new values


const payload: UpdateConfigurationDto = {
configurationName: 'lottery_themes',
payload: {
value: JSON.stringify(lotteriesThemesList),
},
};
console.log('Payload being sent:', payload);

// Save current configuration


await createThemeMutation(payload);
setToastMessage(t('updatedSuccessfully'));
setIsSaved(true);
} catch {
setToastMessage(t('failedTheme'));
}
};

useEffect(() => {
if (isSaved) {
setTimeout(() => {
navigate('/lottery-theme');
}, 500);
}
}, [isSaved]);

const renderLotteries = useMemo(() => {


if (!lotteries?.lotteries?.data || lotteries.lotteries.data.length === 0) {
return (
<MenuItem disabled value="">
{t('noLotteriesAvailable')}
</MenuItem>
);
}

return lotteries?.lotteries.data.map((theme: {id: string; label: string})


=> (
<MenuItem key={theme.id} value={theme.id}>
{theme.label}
</MenuItem>
));
}, [lotteries, t]);

if (loadingLotteries) {
return <FuseLoading />;
}

return (
<FormProvider {...methods}>
<FusePageCarded
header={<LotteryThemesEditHeader />}
content={
<Box className="p-16 sm:p-24 max-w-lg">
<Select
name="lotteryId"
value={lotteryId}
labelId="lottery-select-label"
disabled
displayEmpty
sx={{width: '400px', mb: '30px'}}
>
<MenuItem id="lottery-select-label" value="">
{t('lottery')}
</MenuItem>

{renderLotteries}
</Select>
<RadioGroup
name="deviceType"
value={deviceType}
onChange={(e) => setDeviceType(e.target.value as
'desktop' | 'mobile')}
sx={{flexDirection: 'row', m: '0px'}}
>
<FormControlLabel
value="desktop"
control={<Radio />}
label={t('desktop')}
labelPlacement="start"
/>
<FormControlLabel
value="mobile"
control={<Radio />}
label={t('mobile')}
labelPlacement="start"
/>
</RadioGroup>

<Typography className="text-16 sm:text-20 font-semibold"


sx={{mt: '30px'}}>
{t('imageAsset')}
</Typography>
<Box sx={{display: 'flex', flexDirection: 'row', gap:
'40px'}}>
<Box sx={{mt: '30px'}}>
<Typography className="text-12" color="primary">
{t('heroBanner')}
</Typography>
<Controller
name="imageHeroBanner"
control={control}
render={() => (
<Button
variant="outlined"
component="label"
sx={{
mt: 2,
borderRadius: '5px',
border: 'none',
backgroundColor: '#f5f5f5',
pt: (
deviceType === 'desktop'
?
uploadedImageUrlHeroDesktop
:
uploadedImageUrlHeroMobile
)
? '90px'
: '30px',
pb: (
deviceType === 'desktop'
?
uploadedImageUrlHeroDesktop
:
uploadedImageUrlHeroMobile
)
? '90px'
: '30px',
width: '300px',
height: '150px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden',
...(deviceType === 'desktop' &&
uploadedImageUrlHeroDesktop &&
{
width: '100%',
height: '100%',
}),
...(deviceType === 'mobile' &&
uploadedImageUrlHeroMobile && {
width: '100%',
height: '100%',
}),
}}
>
{deviceType === 'desktop' ? (
uploadedImageUrlHeroDesktop ? (
<img

src={uploadedImageUrlHeroDesktop.url}
alt="Uploaded preview"
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: '8px',
minWidth: '150px',
minHeight: '150px',
}}
/>
) : (
t('uploadPicture')
)
) : uploadedImageUrlHeroMobile ? (
<img

src={uploadedImageUrlHeroMobile.url}
alt="Uploaded preview"
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: '8px',
minWidth: '150px',
minHeight: '150px',
}}
/>
) : (
t('uploadPicture')
)}
<input
accept="image/*"
className="hidden"
id="button-file"
type="file"
onChange={async (e) => {
const file = e?.target?.files?.
[0];
if (!file) return;

const uploadedImageUrl = await


readFileAsync(file);
if (deviceType === 'desktop') {

setUploadedImageUrlHeroDesktop(uploadedImageUrl);
} else {
setUploadedImageUrlHeroMobile(uploadedImageUrl);
}
}}
/>
</Button>
)}
/>
</Box>
<Box sx={{mt: '30px'}}>
<Typography className="text-12" color="primary">
{t('details')}
</Typography>
<Controller
name="imageDetails"
control={control}
render={() => (
<Button
variant="outlined"
component="label"
sx={{
mt: 2,
borderRadius: '5px',
border: 'none',
backgroundColor: '#f5f5f5',
pt: (
deviceType === 'desktop'
?
uploadedImageUrlDetailsDesktop
:
uploadedImageUrlDetailsMobile
)
? '90px'
: '30px',
pb: (
deviceType === 'desktop'
?
uploadedImageUrlDetailsDesktop
:
uploadedImageUrlDetailsMobile
)
? '90px'
: '30px',
width: '300px',
height: '150px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden',
...(deviceType === 'desktop' &&
uploadedImageUrlDetailsDesktop
&& {
width: '100%',
height: '100%',
}),
...(deviceType === 'mobile' &&
uploadedImageUrlDetailsMobile
&& {
width: '100%',
height: '100%',
}),
}}
>
{deviceType === 'desktop' ? (
uploadedImageUrlDetailsDesktop ? (
<img

src={uploadedImageUrlDetailsDesktop.url}
alt="Uploaded preview"
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: '8px',
minWidth: '150px',
minHeight: '150px',
}}
/>
) : (
t('uploadPicture')
)
) : uploadedImageUrlDetailsMobile ? (
<img

src={uploadedImageUrlDetailsMobile.url}
alt="Uploaded preview"
style={{
maxWidth: '100%',
maxHeight: '100%',
objectFit: 'contain',
borderRadius: '8px',
minWidth: '150px',
minHeight: '150px',
}}
/>
) : (
t('uploadPicture')
)}
<input
accept="image/*"
className="hidden"
id="button-file"
type="file"
onChange={async (e) => {
const file = e?.target?.files?.
[0];
if (!file) return;

const uploadedImageUrl = await


readFileAsync(file);
if (deviceType === 'desktop') {

setUploadedImageUrlDetailsDesktop(uploadedImageUrl);
} else {

setUploadedImageUrlDetailsMobile(uploadedImageUrl);
}
}}
/>
</Button>
)}
/>
</Box>
</Box>

<Button
className="mt-40"
variant="contained"
color="primary"
onClick={handleUpdateTheme}
disabled={isLoading}
sx={{borderRadius: '5px'}}
>
{isLoading ? <CircularProgress size={24}
color="inherit" /> : t('save')}
</Button>
</Box>
}
/>
<Snackbar
anchorOrigin={{vertical: 'bottom', horizontal: 'center'}}
open={!!toastMessage}
autoHideDuration={3000}
onClose={() => setToastMessage(null)}
message={toastMessage}
/>
</FormProvider>
);
}

CHATGPT

1 Validar lotteryId desde react-hook-form


1️⃣

En lugar de verificar si lotteryId está vacío dentro de handleCreateTheme,


configura la validación dentro de useForm:

const methods = useForm({


defaultValues: {
lotteryId: '',
},
mode: 'onSubmit',
resolver: yupResolver(
yup.object().shape({
lotteryId: yup.string().required(t('lotterySelect')),
})
),
});

2️⃣ Validar imágenes antes de ejecutar handleCreateTheme

Otra opción es crear una función de validación antes de llamar a handleCreateTheme,


en lugar de manejarlo con if dentro de la función principal:

const validateImages = () => {


if (
!uploadedImageUrlHeroDesktop &&
!uploadedImageUrlDetailsDesktop &&
!uploadedImageUrlHeroMobile &&
!uploadedImageUrlDetailsMobile
) {
setToastMessage(t('uploadOneImage'));
return false;
}
return true;
};

Y luego, en handleCreateTheme, simplemente llamas a esta función:

const handleCreateTheme = methods.handleSubmit(async (data) => {


if (!validateImages()) return;

try {
// Continuar con el flujo normal
} catch (error) {
logger.error('Error saving theme:', error);
setToastMessage(t('somethingWrong'));
}
});

Evitar la creación de temas duplicados

En lugar de manejar if (currentTheme) dentro de handleCreateTheme, puedes verificar


esto antes de ejecutar la función:

const doesThemeExist = (lotteriesThemesList: ITheme[], lotteryId: string) => {


return lotteriesThemesList.some((theme) => theme.lotteryId === lotteryId);
};

Y en handleCreateTheme:

const handleCreateTheme = methods.handleSubmit(async (data) => {


if (!validateImages()) return;

try {
const configuration = await fetchTheme();
const rawConfigurationValue: string = configuration.configuration.value ||
'[]';
let lotteriesThemesList: ITheme[] = JSON.parse(rawConfigurationValue) ||
[];

if (doesThemeExist(lotteriesThemesList, lotteryId)) {
setToastMessage(t('lotteryExist'));
return;
}

// Continuar con la lógica normal


} catch (error) {
logger.error('Error saving theme:', error);
setToastMessage(t('somethingWrong'));
}
});

Resultado Final

Con estos cambios:

React Hook Form se encargará de validar lotteryId, evitando el if manual.


Una función validateImages validará las imágenes antes de llamar a
handleCreateTheme.
Una función doesThemeExist verificará si el tema ya existe antes de continuar.

Esto hace que handleCreateTheme sea más limpio y fácil de leer. 🚀

Puedes eliminar los if dentro de handleCreateTheme aprovechando las validaciones


nativas de react-hook-form. Para lograrlo sin usar Yup, puedes usar las opciones de
validación de los inputs en react-hook-form, como required, y aprovechar
formState.errors para manejar los mensajes de error.
✨ Pasos para eliminar los if sin usar Yup

Configura validaciones en los inputs usando register de react-hook-form.


Accede a errors de formState y muestra mensajes de error.
Elimina las validaciones manuales (if) en handleCreateTheme porque react-hook-
form previene la ejecución de la función si hay errores.

🔹 1. Agregar validaciones en register

Modifica los campos en Select y RadioGroup para agregar validaciones en register:

<Select
{...register("lotteryId", { required: t("lotterySelect") })}
error={!!errors.lotteryId}
helperText={errors.lotteryId?.message}
onChange={(e) => setLotteryId(e.target.value)}
displayEmpty
>
<MenuItem value="">{t("lottery")}</MenuItem>
{renderLotteries}
</Select>

Para RadioGroup:

<RadioGroup {...register("deviceType", { required: t("selectDeviceType") })}>


<FormControlLabel value="desktop" control={<Radio />} label={t('desktop')} />
<FormControlLabel value="mobile" control={<Radio />} label={t('mobile')} />
</RadioGroup>
{errors.deviceType && <Typography
color="error">{errors.deviceType.message}</Typography>}
🔹 2. Validar imágenes antes de enviar el formulario

Dado que las imágenes no forman parte de un input registrado en react-hook-form,


puedes hacer la validación dentro de handleSubmit antes de llamar a la API:

const handleCreateTheme = methods.handleSubmit(async (data) => {


if (!uploadedImageUrlHeroDesktop &&
!uploadedImageUrlDetailsDesktop &&
!uploadedImageUrlHeroMobile &&
!uploadedImageUrlDetailsMobile) {
setToastMessage(t("uploadOneImage"));
return;
}

try {
// Procesamiento de imágenes y creación de tema...
} catch (error) {
setToastMessage(t("somethingWrong"));
}
});

🔹 3. Elimina los if en handleCreateTheme

Gracias a react-hook-form, no es necesario validar lotteryId ni deviceType dentro


de handleCreateTheme, ya que el formulario no se enviará si hay errores.
Antes (con if):

if (!lotteryId) {
setToastMessage(t("lotterySelect"));
return;
}

Después (sin if):

🚀 Ya no es necesario porque react-hook-form evita que la función se ejecute si el


campo está vacío.
🎯 Beneficios de esta solución

✅ El código es más limpio sin if redundantes.


✅ React-hook-form maneja los errores automáticamente y evita el envío.
✅ Los errores se muestran en la interfaz con helperText.

Si necesitas validaciones más avanzadas, puedes considerar Yup, pero con esta
solución puedes evitarlo. 🚀

You might also like