228 lines
8.9 KiB
TypeScript
228 lines
8.9 KiB
TypeScript
import ChallengeCard from "@/components/ChallengeCard"
|
|
import { useAddChallengeMutation, useDeleteChallengeMutation, useEditChallengeMutation } from "@/hooks/mutations/useChallengeMutation"
|
|
import { useAuth } from "@/hooks/useAuth"
|
|
import { useChallenges } from "@/hooks/useChallenges"
|
|
import { Challenge } from "@/utils/features/challenges/challengesSlice"
|
|
import { FontAwesome6 } from "@expo/vector-icons"
|
|
import { useQueryClient } from "@tanstack/react-query"
|
|
import { useRouter } from "expo-router"
|
|
import { useState } from "react"
|
|
import { FlatList, StyleSheet } from "react-native"
|
|
import { Appbar, Button, Dialog, Divider, FAB, List, MD3Colors, Modal, Portal, Snackbar, Surface, Text, TextInput } from "react-native-paper"
|
|
|
|
export default function ChallengesList() {
|
|
const router = useRouter()
|
|
const queryClient = useQueryClient()
|
|
const auth = useAuth()
|
|
const challenges = useChallenges()
|
|
|
|
const [editChallengeVisible, setEditChallengeVisible] = useState(false)
|
|
const [editChallengeTitle, setEditChallengeTitle] = useState("")
|
|
const [editChallengeDescription, setEditChallengeDescription] = useState("")
|
|
const [editChallengeReward, setEditChallengeReward] = useState(0)
|
|
const [editChallengeId, setEditChallengeId] = useState<number |null>(null)
|
|
const [displayedChallenge, setDisplayedChallenge] = useState<Challenge | null>(null)
|
|
const [confirmDeletedVisible, setConfirmDeleteVisible] = useState(false)
|
|
const [successSnackbarVisible, setSuccessSnackbarVisible] = useState(false)
|
|
const [successMessage, setSuccessMessage] = useState("")
|
|
const [errorVisible, setErrorVisible] = useState(false)
|
|
const [error, setError] = useState([200, ""])
|
|
|
|
const addChallengeMutation = useAddChallengeMutation({
|
|
auth,
|
|
onPostSuccess: () => {
|
|
setSuccessMessage("Le défi a bien été ajouté !")
|
|
setSuccessSnackbarVisible(true)
|
|
queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === 'get-challenges' })
|
|
},
|
|
onError: ({ response, error }) => {
|
|
setErrorVisible(true)
|
|
if (response)
|
|
setError([response.statusCode, response.message])
|
|
else if (error)
|
|
setError([400, error.message])
|
|
},
|
|
})
|
|
const editChallengeMutation = useEditChallengeMutation({
|
|
auth,
|
|
onPostSuccess: () => {
|
|
setSuccessMessage("Le défi a bien été modifié !")
|
|
setSuccessSnackbarVisible(true)
|
|
setEditChallengeVisible(false)
|
|
setDisplayedChallenge(null)
|
|
queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === 'get-challenges' })
|
|
},
|
|
onError: ({ response, error }) => {
|
|
setErrorVisible(true)
|
|
if (response)
|
|
setError([response.statusCode, response.message])
|
|
else if (error)
|
|
setError([400, error.message])
|
|
},
|
|
})
|
|
const deleteChallengeMutation = useDeleteChallengeMutation({
|
|
auth,
|
|
onPostSuccess: () => {
|
|
setSuccessMessage("Le défi a bien été supprimé !")
|
|
setSuccessSnackbarVisible(true)
|
|
setEditChallengeVisible(false)
|
|
queryClient.invalidateQueries({ predicate: (query) => query.queryKey[0] === 'get-challenges' })
|
|
},
|
|
onError: ({ response, error }) => {
|
|
setErrorVisible(true)
|
|
if (response)
|
|
setError([response.statusCode, response.message])
|
|
else if (error)
|
|
setError([400, error.message])
|
|
},
|
|
})
|
|
|
|
function sendEditChallenge() {
|
|
if (editChallengeId) {
|
|
editChallengeMutation.mutate({
|
|
id: editChallengeId,
|
|
title: editChallengeTitle,
|
|
description: editChallengeDescription,
|
|
reward: editChallengeReward,
|
|
})
|
|
}
|
|
else {
|
|
addChallengeMutation.mutate({
|
|
title: editChallengeTitle,
|
|
description: editChallengeDescription,
|
|
reward: editChallengeReward,
|
|
})
|
|
}
|
|
}
|
|
|
|
function sendDeleteChallenge() {
|
|
displayedChallenge && deleteChallengeMutation.mutate(displayedChallenge)
|
|
}
|
|
|
|
return (
|
|
<Surface style={{ flex: 1 }}>
|
|
<Appbar.Header>
|
|
{router.canGoBack() ? <Appbar.BackAction onPress={() => router.back()} /> : undefined}
|
|
<Appbar.Content title={"Liste des défis"} />
|
|
</Appbar.Header>
|
|
<FlatList
|
|
data={challenges}
|
|
keyExtractor={(challenge) => `challenge-list-item-${challenge.id}`}
|
|
ItemSeparatorComponent={() => <Divider />}
|
|
renderItem={(item) => <ChallengeListItem challenge={item.item} onPress={() => setDisplayedChallenge(item.item)} />} />
|
|
<Snackbar
|
|
key='success-snackbar'
|
|
visible={successSnackbarVisible}
|
|
icon={'close'}
|
|
onDismiss={() => setSuccessSnackbarVisible(false)}
|
|
onIconPress={() => setSuccessSnackbarVisible(false)}>
|
|
<Text variant='bodyMedium' style={{ color: MD3Colors.secondary0 }}>
|
|
{successMessage}
|
|
</Text>
|
|
</Snackbar>
|
|
<Snackbar
|
|
key='error-snackbar'
|
|
visible={errorVisible}
|
|
icon={'close'}
|
|
onDismiss={() => setErrorVisible(false)}
|
|
onIconPress={() => setErrorVisible(false)}>
|
|
<Text variant='bodyMedium' style={{ color: MD3Colors.secondary0 }}>
|
|
Erreur {error[0]} : {error[1]}
|
|
</Text>
|
|
</Snackbar>
|
|
<FAB
|
|
icon='plus'
|
|
style={styles.addButton}
|
|
onPress={() => {
|
|
if (editChallengeId) {
|
|
setEditChallengeTitle("")
|
|
setEditChallengeDescription("")
|
|
setEditChallengeReward(0)
|
|
setEditChallengeId(null)
|
|
}
|
|
setEditChallengeVisible(true)
|
|
}} />
|
|
<Portal>
|
|
<Modal
|
|
visible={displayedChallenge !== null}
|
|
onDismiss={() => setDisplayedChallenge(null)}
|
|
contentContainerStyle={{ flex: 1, marginHorizontal: 20, marginVertical: 100 }}>
|
|
<ChallengeCard
|
|
challenge={displayedChallenge}
|
|
onEdit={() => {
|
|
setEditChallengeTitle(displayedChallenge?.title ?? "")
|
|
setEditChallengeDescription(displayedChallenge?.description ?? "")
|
|
setEditChallengeReward(displayedChallenge?.reward ?? 0)
|
|
setEditChallengeId(displayedChallenge?.id ?? null)
|
|
setEditChallengeVisible(true)
|
|
}}
|
|
onDelete={() => setConfirmDeleteVisible(true)}
|
|
style={{ flexGrow: 1 }} />
|
|
</Modal>
|
|
<Dialog visible={editChallengeVisible} onDismiss={() => setEditChallengeVisible(false)}>
|
|
<Dialog.Title>{editChallengeId ? "Modification d'un défi" : "Ajout d'un défi"}</Dialog.Title>
|
|
<Dialog.Content>
|
|
<TextInput
|
|
label="Titre"
|
|
defaultValue={editChallengeTitle}
|
|
onChangeText={setEditChallengeTitle}
|
|
error={!editChallengeTitle} />
|
|
<TextInput
|
|
label="Description"
|
|
defaultValue={editChallengeDescription}
|
|
multiline={true}
|
|
onChangeText={setEditChallengeDescription}
|
|
error={!editChallengeDescription} />
|
|
<TextInput
|
|
label="Récompense"
|
|
defaultValue={editChallengeReward ? editChallengeReward.toString() : ""}
|
|
inputMode='numeric'
|
|
onChangeText={(text) => setEditChallengeReward(+text)}
|
|
error={!editChallengeReward}
|
|
onEndEditing={() => !addChallengeMutation.isPending && editChallengeMutation.isPending && sendEditChallenge()} />
|
|
</Dialog.Content>
|
|
<Dialog.Actions>
|
|
<Button onPress={() => setEditChallengeVisible(false)}>Annuler</Button>
|
|
<Button
|
|
onPress={sendEditChallenge}
|
|
disabled={!editChallengeTitle || !editChallengeDescription || !editChallengeReward || addChallengeMutation.isPending || editChallengeMutation.isPending}>
|
|
{editChallengeId ? "Modifier" : "Ajouter"}
|
|
</Button>
|
|
</Dialog.Actions>
|
|
</Dialog>
|
|
<Dialog visible={confirmDeletedVisible} onDismiss={() => setConfirmDeleteVisible(false)}>
|
|
<Dialog.Title>Êtes-vous sûre ?</Dialog.Title>
|
|
<Dialog.Content>
|
|
<Text variant='bodyMedium'>
|
|
Voulez-vous vraiment supprimer le défi « {displayedChallenge?.title} » ? Cette opération est irréversible !
|
|
</Text>
|
|
</Dialog.Content>
|
|
<Dialog.Actions>
|
|
<Button onPress={() => setConfirmDeleteVisible(false)}>Annuler</Button>
|
|
<Button onPress={sendDeleteChallenge} disabled={deleteChallengeMutation.isPending}>Confirmer</Button>
|
|
</Dialog.Actions>
|
|
</Dialog>
|
|
</Portal>
|
|
</Surface>
|
|
)
|
|
}
|
|
|
|
function ChallengeListItem({ challenge, onPress }: { challenge: Challenge, onPress?: () => void }) {
|
|
const description = <Text>Récompense : {challenge.reward} <FontAwesome6 name='coins' /></Text>
|
|
return (
|
|
<List.Item
|
|
title={challenge.title}
|
|
description={description}
|
|
onPress={onPress} />
|
|
)
|
|
}
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
addButton: {
|
|
position: 'absolute',
|
|
right: 25,
|
|
bottom: 25,
|
|
}
|
|
})
|