From 27d13ace9d05712a1c12a717852f7faca72ff371 Mon Sep 17 00:00:00 2001 From: Rochas Date: Sun, 14 Dec 2025 03:04:12 +0100 Subject: [PATCH 1/3] test reservation todo corriger getChantier avec la nouvelelle structure --- app/(tabs)/addScreen.tsx | 4 + app/(tabs)/gestion_ouvrier.tsx | 16 +-- app/(tabs)/gestionnaire_ressource.tsx | 7 +- class/class.tsx | 11 +- components/add/addChantier.tsx | 105 ++++++++++++++++--- components/add/select/selectChefChantier.tsx | 1 + components/chantierSummary.tsx | 6 +- components/selectChantier.tsx | 2 +- data/concerts.json | 45 -------- services/ressourcesService.ts | 93 ++++++++++++++-- 10 files changed, 190 insertions(+), 100 deletions(-) delete mode 100644 data/concerts.json diff --git a/app/(tabs)/addScreen.tsx b/app/(tabs)/addScreen.tsx index a2ecf04..0cbe68a 100644 --- a/app/(tabs)/addScreen.tsx +++ b/app/(tabs)/addScreen.tsx @@ -9,7 +9,11 @@ import { ThemedView } from "@/components/theme/themed-view"; export default function AddScreen() { const [typeAdd, setTypeAdd] = useState(''); + const [editMode, setEditMode] = useState(false); + function onPressSwitchMode(){ + setEditMode(!editMode); + } return( diff --git a/app/(tabs)/gestion_ouvrier.tsx b/app/(tabs)/gestion_ouvrier.tsx index 0e8f8a0..e69e0f7 100644 --- a/app/(tabs)/gestion_ouvrier.tsx +++ b/app/(tabs)/gestion_ouvrier.tsx @@ -5,7 +5,6 @@ import Constants from "expo-constants"; //pour connaître la taille de la barre import { useLocalSearchParams, useRouter } from "expo-router"; import React, { useEffect, useMemo, useState } from "react"; import { FlatList, Image, StyleSheet, Text, View } from "react-native"; -import rawConcerts from "../../data/concerts.json"; import { getUsers } from "@/services/ressourcesService"; import { useChantier } from "../ContextChantier"; import SelectChantier from "@/components/selectChantier"; @@ -34,7 +33,7 @@ export default function GestionOuvrier() { async function loadData() { try { //Nous ne gardons que les Ouvriers, qui peuvent être assignés à un chantier - const data = (await getRessources()).filter(u => u.type === "ouvrier"); + const data = (await getRessources()).filter(u => u.type === "Ouvrier"); setRessources(data); } catch (error) { console.error("Erreur lors du chargement :", error); @@ -43,19 +42,6 @@ export default function GestionOuvrier() { loadData(); }, []); - const concertsData: Concert[] = Array.isArray(rawConcerts) - ? (rawConcerts as Concert[]) - : []; - - const filteredData = useMemo(() => { - if (!Array.isArray(concertsData)) return []; - const q = search.trim().toLowerCase(); - if (!q) return concertsData; - return concertsData.filter( - (item) => !!item && (item.group ?? "").toLowerCase().includes(q) - ); - }, [concertsData, search]); - const renderItem = ({ item, index }: { item?: Ressources; index: number }) => { if (!item) { return null; diff --git a/app/(tabs)/gestionnaire_ressource.tsx b/app/(tabs)/gestionnaire_ressource.tsx index 6f19aef..f0df52c 100644 --- a/app/(tabs)/gestionnaire_ressource.tsx +++ b/app/(tabs)/gestionnaire_ressource.tsx @@ -15,7 +15,7 @@ import { useRessources } from "../ContextRessource"; export default function GestionnaireRessource() { const [search, setSearch] = useState(""); const {ressources, setRessources} = useRessources(); - const [filterType, setFilterType] = useState("tout"); + const [filterType, setFilterType] = useState("Tout"); const [showFilterMenu, setShowFilterMenu] = useState(false); const router = useRouter(); @@ -33,7 +33,7 @@ export default function GestionnaireRessource() { const filteredData = ressources.filter((r) => { const matchName = r.name.toLowerCase().includes(search.toLowerCase()); - const matchType = filterType === "tout" || r.type === filterType; + const matchType = filterType === "Tout" || r.type === filterType; return matchName && matchType; }); @@ -43,6 +43,7 @@ export default function GestionnaireRessource() { + Id : {item.id} Nom : {item.name} Type : {item.type} Quantité totale : {item.quantity} @@ -68,7 +69,7 @@ export default function GestionnaireRessource() { Filtrer par type - {["tout", "Outil", "Machine"].map((t) => ( + {["Tout", "Outil", "Machine","Ouvrier"].map((t) => ( (); const [ouvriers, setOuviers] = useState(); + const [outils, setOutils] = useState(); const [showDateSelect,setSowDateSelect] = useState(false); const [openConfirmation,setOpenConfirmation] = useState(false); @@ -52,7 +52,6 @@ export default function AddChantier() { const [ressourcesSelect, setRessourcesSelect] = useState([]); async function handleAddChantier() { - setLoading(true); setOpenConfirmation(true); } @@ -64,15 +63,89 @@ export default function AddChantier() { } }; async function onConfirm(): Promise { - if (!isValidChantier() || !chefChantier) return; - try { - setLoading(true); + if (!isValidChantier() || !chefChantier){ + alert("Choisir un chef de Chantier"); + return; + } + setOpenConfirmation(false); + var latitude=0; + var longitude=0; + try { //verification de l'adresse + const coords = await geocodeAddress(adresse); + if (coords) { + latitude=coords.latitude; + longitude=coords.longitude; + } + else{ + console.error("Impossible de géocoder l'adresse"); + alert("Adresse introuvable. Veuillez vérifier l'adresse."); + } + } catch (error) { + console.error("Erreur lors de la création du chantier:", error); + alert("Erreur lors de la création du chantier"); + } + + + + var chantier: Chantier = { + id:"0", + name: objet, + adresse: adresse, + etat: 'En cours', + contact: contact, + chef: chefChantier, + dateDep: date, + tempsEst: Number(duree), + anomalies: [], + latitude: latitude, + longitude: longitude, + equipe: [], + materiel: [], + vehicules: [] + } + + if(machines){ + machines.forEach(item => { + chantier.vehicules.push({ + id:"0", + chantier: chantier, + ressource: item[0], + quantity: item[1], + }) + }); + } + if(ouvriers){ + ouvriers.forEach(item => { + chantier.equipe.push({ + id:"0", + chantier: chantier, + ressource: item[0], + quantity: item[1], + }) + }); + } + if(outils){ + outils.forEach(item => { + chantier.materiel.push({ + id:"0", + chantier: chantier, + ressource: item[0], + quantity: item[1], + }) + }); + } + + sendNewChantier(chantier); + + + + + /*try { const coords = await geocodeAddress(adresse); if (!coords) { console.error("Impossible de géocoder l'adresse"); alert("Adresse introuvable. Veuillez vérifier l'adresse."); - setLoading(false); return; } const chantierDate = new Date(date); @@ -85,17 +158,17 @@ export default function AddChantier() { contact, chef: doc(db, "user", chefChantier.id), equipe: [], - /*materiel: materiels - ? [doc(db, "ressources", String(materiels.id))] - : [],*/ + //materiel: materiels + // ? [doc(db, "ressources", String(materiels.id))] + // : [], vehicules: machines?.map(e => doc(db, "ressources", String(e[0].id)) ) || [], anomalies: [], dateDep: chantierDate, tempsEst: parseInt(duree) || 1, - latitude: coords.latitude, //TODO - longitude: coords.longitude, //TODO + latitude: coords.latitude, + longitude: coords.longitude, }; const id = await addChantier(chantierFirestore as any); if (id) { @@ -113,9 +186,7 @@ export default function AddChantier() { } catch (error) { console.error("Erreur lors de la création du chantier:", error); alert("Erreur lors de la création du chantier"); - } finally { - setLoading(false); - } + }*/ } function onCancel(): void { @@ -231,7 +302,7 @@ export default function AddChantier() { Outils: - + diff --git a/components/add/select/selectChefChantier.tsx b/components/add/select/selectChefChantier.tsx index 7b03594..f4d10e2 100644 --- a/components/add/select/selectChefChantier.tsx +++ b/components/add/select/selectChefChantier.tsx @@ -53,6 +53,7 @@ export default function SelectChafChantier({style,sendChefChantier , ...otherPro return( {onPressUser(item)}}> + {item.id} {item.name} {item.last_name} {item.role} diff --git a/components/chantierSummary.tsx b/components/chantierSummary.tsx index 3d649cd..b44e544 100644 --- a/components/chantierSummary.tsx +++ b/components/chantierSummary.tsx @@ -20,9 +20,9 @@ export default function ChantierSummary({data,style , ...otherProps }: Props) { - Adresse: {data.chantier.adresse} - Chef de chantier: {data.chantier.chef.last_name}{" "}{data.chantier.chef.name} - État: {data.chantier.etat} + Adresse: {data.chantier.adresse} + Chef de chantier: {data.chantier.chef.last_name}{" "}{data.chantier.chef.name} + État: {data.chantier.etat} ) : diff --git a/components/selectChantier.tsx b/components/selectChantier.tsx index 5447035..a7d32a5 100644 --- a/components/selectChantier.tsx +++ b/components/selectChantier.tsx @@ -78,7 +78,7 @@ export default function SelectChantier() { var keyWords:string[] = search.toLowerCase().split(" ") ; var containsAllKeyWord:boolean = true; keyWords.forEach(keyWord => { - containsAllKeyWord = containsAllKeyWord && (chantier.adresse.toLowerCase().includes(keyWord)) + containsAllKeyWord = containsAllKeyWord && (chantier.adresse.toLowerCase().includes(keyWord) || chantier.name.toLowerCase().includes(keyWord)) }); return containsAllKeyWord }); diff --git a/data/concerts.json b/data/concerts.json deleted file mode 100644 index a2fa93c..0000000 --- a/data/concerts.json +++ /dev/null @@ -1,45 +0,0 @@ -[ - { - "group": "Bernard DupYEEd", - "date":"Rennes", - "nationality": "French", - "location": "PlombYEEr", - "price": 20, - "ticketsLeft": 36, - "Image": "https://media.discordapp.net/attachments/1415267028201246812/1424825038657425518/a06e3304-86ca-4b4f-8016-c4ae9844b0df.png?ex=68e9f879&is=68e8a6f9&hm=b6ff1f540d5c382930b56bd6f90565f517ee179347d6ee6aebd5254b10cf4c88&=&format=webp&quality=lossless&width=579&height=579", - "favorite": false - }, - { - "group": "MYEEchel Câble", - "date":"Nantes", - "nationality": "French", - "location": "ElectrYEEcien", - "price": 22, - "ticketsLeft": 400, - "Image": "https://media.discordapp.net/attachments/1415267028201246812/1424826240090509332/7fdbfe06-8300-441e-81ac-87851d004dc3.png?ex=68e9f997&is=68e8a817&hm=cc71621c3e7c3c1aaeda5555e9dd4204d43414cd0332c2b116b10d009b68df3c&=&format=webp&quality=lossless&width=579&height=579", - "favorite": false - - }, - { - "group": "PYEErre soulever", - "date":"Redon", - "nationality": "French", - "location": "GrutYEEr", - "price": 32, - "ticketsLeft": 0, - "Image": "https://media.discordapp.net/attachments/1425108443571945644/1427207643180826757/raw.png?ex=68ee0632&is=68ecb4b2&hm=1efc51065c6abfb1af75b8382f9924c2eb177c7d7672f7ed9837e96ef3076d16&=&format=webp&quality=lossless&width=233&height=350", - "favorite": false - - }, - { - "group": "Greg NegatYEEf", - "date":"Pacé", - "nationality": "French", - "location": "ElectrYEEcien", - "price": 20, - "ticketsLeft": 36, - "Image": "https://media.discordapp.net/attachments/1415267028201246812/1424826240090509332/7fdbfe06-8300-441e-81ac-87851d004dc3.png?ex=68e9f997&is=68e8a817&hm=cc71621c3e7c3c1aaeda5555e9dd4204d43414cd0332c2b116b10d009b68df3c&=&format=webp&quality=lossless&width=579&height=579", - "favorite": true - - } -] \ No newline at end of file diff --git a/services/ressourcesService.ts b/services/ressourcesService.ts index daae994..eb9b13a 100644 --- a/services/ressourcesService.ts +++ b/services/ressourcesService.ts @@ -1,4 +1,4 @@ -import { addDoc, arrayUnion, collection, doc, getDoc, getDocs, Timestamp, updateDoc } from "firebase/firestore"; +import { addDoc, arrayUnion, collection, doc, Firestore, getDoc, getDocs, Timestamp, updateDoc, DocumentReference } from "firebase/firestore"; import { Chantier, Reservation, Ressources, User } from "../class/class"; import { db } from "../firebase_config"; @@ -28,6 +28,7 @@ export async function getRessources(): Promise { return snapshot.docs.map((doc) => { const data = doc.data(); return { + id: doc.id, ...data, allocation: data.allocation?.map(convertReservation) || [], } as Ressources; @@ -66,22 +67,46 @@ export async function getChantiers(): Promise { chef = chefSnap.data() as User; } } - let equipe: User[] = []; + + let equipe: Reservation[] = []; if (Array.isArray(data.equipe)) { equipe = await Promise.all( data.equipe.map(async (ref: any) => { const snap = await getDoc(ref); - return snap.exists() ? (snap.data() as User) : null; + return snap.exists() ? (snap.data() as Reservation) : null; }) - ).then(list => list.filter(x => x !== null)) as User[]; + ).then(list => list.filter(x => x !== null)) as Reservation[]; } + let vehicules: Reservation[] = []; + if (Array.isArray(data.vehicules)) { + vehicules = await Promise.all( + data.vehicules.map(async (ref: any) => { + const snap = await getDoc(ref); + return snap.exists() ? (snap.data() as Reservation) : null; + }) + ).then(list => list.filter(x => x !== null)) as Reservation[]; + } + + let materiel: Reservation[] = []; + if (Array.isArray(data.materiel)) { + materiel = await Promise.all( + data.materiel.map(async (ref: any) => { + const snap = await getDoc(ref); + return snap.exists() ? (snap.data() as Reservation) : null; + }) + ).then(list => list.filter(x => x !== null)) as Reservation[]; + } + + chantiers.push({ ...data, id: docSnap.id, dateDep, chef, - equipe + equipe, + vehicules, + materiel, } as Chantier); } return chantiers; @@ -144,12 +169,58 @@ export async function deleteAnomalie(chantierId: string, anomalie_String: string } } -function convertReservation(res: any): Reservation { +type ReservationFirestore = { + chantier: DocumentReference; + ressource: DocumentReference; + quantity: number; +}; + +async function convertReservation(res: any): Promise { + + const data = res.data() as ReservationFirestore; + const chantierSnap = await getDoc(data.chantier as DocumentReference); + const ressourceSnap = await getDoc(data.ressource as DocumentReference); + return { id: res.id, - dateChantier: - res.dateChantier instanceof Timestamp ? res.dateChantier.toDate() : new Date(res.dateChantier), - dateFin: - res.dateFin instanceof Timestamp ? res.dateFin.toDate() : new Date(res.dateFin), + chantier: chantierSnap.data() as Chantier, + ressource: ressourceSnap.data() as Ressources, + quantity: data.quantity, }; -} \ No newline at end of file +} + + + +//ENVOYER CHANTIER +export async function sendNewChantier(chantier:Chantier): Promise { + + const chantierRef = await addDoc(collection(db, "chantier"), { + name:chantier.name, + adresse:chantier.adresse, + etat:chantier.etat, + contact:chantier.contact, + chef: doc(db, "users", chantier.chef.id), //un objet déjà dans la base de donné + date: Timestamp.fromDate(chantier.dateDep), + tempsEst: chantier.tempsEst, + anomalies: chantier.anomalies ?? [], //strings[] + latitude: chantier.latitude, + longitude: chantier.longitude, + }) + await Promise.all([ + sendNewReservation(chantier.equipe, chantierRef.id), + sendNewReservation(chantier.materiel, chantierRef.id), + sendNewReservation(chantier.vehicules, chantierRef.id), + ]); +} + +export async function sendNewReservation(list: Reservation[],chantierId:string): Promise { + const promises = list.map((reservation) => + addDoc(collection(db,"Reservation"),{ + chantier: doc(db, "chantier", chantierId), + ressource: doc(db, "ressources", reservation.ressource.id), + quantity: reservation.quantity, + }) + ); + + await Promise.all(promises); +} From a2e5b1e9cffceb8596463c58f1d4fb9357b9b867 Mon Sep 17 00:00:00 2001 From: Rochas Date: Sun, 14 Dec 2025 10:58:30 +0100 Subject: [PATCH 2/3] fix temporaire en cours --- class/class.tsx | 2 +- services/ressourcesService.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/class/class.tsx b/class/class.tsx index 885a4c3..0bb40f2 100644 --- a/class/class.tsx +++ b/class/class.tsx @@ -19,7 +19,7 @@ export type User = { id: string; name: string; last_name: string; - allocation: Reservation[]; + allocation?: Reservation[]; role: string; qualifications: string; }; diff --git a/services/ressourcesService.ts b/services/ressourcesService.ts index eb9b13a..ac7ed0d 100644 --- a/services/ressourcesService.ts +++ b/services/ressourcesService.ts @@ -12,7 +12,7 @@ export async function getUsers(): Promise { return { id: doc.id, ...data, - allocation: data.allocation?.map(convertReservation) || [], + //allocation: data.allocation?.map(convertReservation) || [], } as User; }); } catch (err) { @@ -30,7 +30,7 @@ export async function getRessources(): Promise { return { id: doc.id, ...data, - allocation: data.allocation?.map(convertReservation) || [], + //allocation: data.allocation?.map(convertReservation) || [], } as Ressources; }); } catch (err) { @@ -67,7 +67,7 @@ export async function getChantiers(): Promise { chef = chefSnap.data() as User; } } - + /* let equipe: Reservation[] = []; if (Array.isArray(data.equipe)) { equipe = await Promise.all( @@ -97,8 +97,10 @@ export async function getChantiers(): Promise { }) ).then(list => list.filter(x => x !== null)) as Reservation[]; } - - + */ + var equipe:Reservation[] = []; + var vehicules:Reservation[] = []; + var materiel:Reservation[] = []; chantiers.push({ ...data, id: docSnap.id, From 7b4e2c1130d3c4bf175162c6cfa51a0959f90502 Mon Sep 17 00:00:00 2001 From: Rochas Date: Sun, 14 Dec 2025 15:00:11 +0100 Subject: [PATCH 3/3] gestion des ressources fonctionnelle, mais pas fini --- README.md | 34 ++++- app/(tabs)/home.tsx | 2 + app/ContextReservation.tsx | 33 ++++ app/_layout.tsx | 28 ++-- class/class.tsx | 2 +- class/utils.tsx | 31 ++++ components/add/addRessource.tsx | 107 ++++++------- components/add/select/ressourceSummary.tsx | 27 +++- components/anomaly.tsx | 6 +- components/chantierSummary.tsx | 14 +- components/selectChantier.tsx | 4 +- services/ressourcesService.ts | 168 +++++++++++++-------- 12 files changed, 308 insertions(+), 148 deletions(-) create mode 100644 app/ContextReservation.tsx create mode 100644 class/utils.tsx diff --git a/README.md b/README.md index 552565d..59ae7d6 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ Lien du git : gitlab2.istic.univ-rennes1.fr/trochas/mmm-projet -Différentes commandes a effectuer pour lancer le projet: + +#### Différentes commandes a effectuer pour lancer le projet: npx expo install react-native-maps@1.9.0 npm install react-native-maps @react-navigation/native @react-navigation/bottom-tabs react-native-safe-area-context react-native-screens firebase @@ -20,4 +21,33 @@ npm install @react-native-community/datetimepicker npx expo install expo-image-picker npx expo install expo-location -npx expo start \ No newline at end of file +npx expo start + + +#### Présentation de l'application : + +##### 5 écrans : +Accuil : +- Affiche le chantier sélectionné : + - Résumé du chantier + - état éditable par l'utilisateur + - Liste d'anomalies, possibilité d'en ajouter ou de les supprimer +- Sélectionner un chantier via le bouton en haut à gauche. + +Ressources : + +Ouvriers : + +MapScreen : + +Ajouter : +Permet d'ajouter un chantier ou une ressource (ouvrier,véhicule,outil) +##### Fonctionnalité manquante : + +Par manque de temps nous n'avons pas peu finnalité certaine fonctionnalité + +- possibilité de modifier les ressources d'un chantier (ex: réajustement des besoins) +- modifier la quantité totale d'une ressource (ex: restock de ressources) +- gestion des stocks non finalisée : + Un chantier comptabilise du stock uniquement quand il est "En cours", s'il est dans un autre été, ses réservations ne sont pas comptabilisées, donc les autres chantiers peuvent utiliser le stock libéré. Si on le remet l'état à "En cours" et que le stock n'est pas suffisant, alors la quantité disponible du stock passe en négatif. + Ce problème peut être corrigé en bloquant le changement d'état si la quantité de stock n'est pas suffisante, mais aurait besoin de la possibilité de modifier les ressources du chantier, ou la quantité des ressources. \ No newline at end of file diff --git a/app/(tabs)/home.tsx b/app/(tabs)/home.tsx index a9af9b7..fd502d8 100644 --- a/app/(tabs)/home.tsx +++ b/app/(tabs)/home.tsx @@ -23,9 +23,11 @@ export default function Home() { + {chantier&& + } diff --git a/app/ContextReservation.tsx b/app/ContextReservation.tsx new file mode 100644 index 0000000..c0805e4 --- /dev/null +++ b/app/ContextReservation.tsx @@ -0,0 +1,33 @@ +import { Reservation } from "@/class/class"; +import { createContext, ReactNode, useContext, useMemo, useState } from "react"; + +type ReservationContextType = { + reservations: Reservation[]; + setReservations: (list: Reservation[]) => void; +}; + +const ReservationsContext = createContext(null); + +type ReservationsProviderProps = { + children: ReactNode; +}; + +export const ReservationsProvider = ({ children }: ReservationsProviderProps) => { + const [reservations, setReservations] = useState([]); + + const value = useMemo(() => ({ reservations, setReservations }), [reservations]); + + return ( + + {children} + + ); +}; + +export const useReservations = () => { + const context = useContext(ReservationsContext); + if (!context) { + throw new Error("useRessources doit être utilisé dans "); + } + return context; +}; \ No newline at end of file diff --git a/app/_layout.tsx b/app/_layout.tsx index 6f6e328..a036b39 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -16,9 +16,11 @@ import { Platform, UIManager } from 'react-native'; import { ChantierProvider } from "./ContextChantier"; import { UserProvider } from "./ContextUser"; import { RessourcesProvider } from "./ContextRessource"; +import { ReservationsProvider } from "./ContextReservation"; import LoginScreen from "./login/login"; + export const unstable_settings = { anchor: "(tabs)", }; @@ -60,18 +62,20 @@ export default function RootLayout() { - - - - - - - - - + + + + + + + + + + + diff --git a/class/class.tsx b/class/class.tsx index 0bb40f2..e25ef99 100644 --- a/class/class.tsx +++ b/class/class.tsx @@ -27,7 +27,7 @@ export type User = { export type Ressources = { id: string; name: string; - type: string; //"machine","outil","ouvrier" + type: string; //"Machine","Outil","Ouvrier" Image: string; quantity: number; available_quantity: number; diff --git a/class/utils.tsx b/class/utils.tsx new file mode 100644 index 0000000..e06ad30 --- /dev/null +++ b/class/utils.tsx @@ -0,0 +1,31 @@ +import { Reservation,Chantier,User, Ressources } from "./class"; + +export function getNbItemReservation(reservations:Reservation[]):number{ + var res = 0; + reservations.forEach(reserv => { + res += reserv.quantity; + }); + return res; +} + + +export function getReservationOfRessource(ressource:Ressources, allReservations:Reservation[]):Reservation[]{ + const res:Reservation[] = []; + allReservations.forEach(reserv => { + if(reserv.ressource.name===ressource.name){ + res.push(reserv); + } + }); + return res; +} + + +export function getNbUseRessources(ressource:Ressources, allReservations:Reservation[]):number{ + var res:number = 0; + getReservationOfRessource(ressource,allReservations).forEach(reserv => { + if(reserv.chantier.etat==="En cours"){ + res+=reserv.quantity; + } + }) + return res; +} \ No newline at end of file diff --git a/components/add/addRessource.tsx b/components/add/addRessource.tsx index 7962432..50d94c8 100644 --- a/components/add/addRessource.tsx +++ b/components/add/addRessource.tsx @@ -52,73 +52,74 @@ export default function AddRessource({ressourceType, ...otherProps }: Props) { const [quantiteDisponible,setQuantiteDisponible] = useState(''); const [openConfirmation,setOpenConfirmation] = useState(false); - async function handleAddRessource() { + async function handleAddRessource() { setLoading(true); setOpenConfirmation(true); } - async function onConfirm(): Promise { - if(isValidRessource()){ - try{ - setLoading(true); - const nouvelleRessource : Ressources = { - id : '', - name: nom, - type : ressourceType, - quantity : parseInt(quantite), - available_quantity : parseInt(quantite), - Image : "", - allocation : [], - }; - const id = await addRessources(nouvelleRessource); + async function onConfirm(): Promise { + if(isValidRessource()){ + try{ + setLoading(true); + const nouvelleRessource : Ressources = { + id : '', + name: nom, + type : ressourceType, + quantity : parseInt(quantite), + available_quantity : parseInt(quantite), + Image : "", + allocation : [], + }; + const id = await addRessources(nouvelleRessource); - if(id){ - setRessources([...ressources,{...nouvelleRessource, id}]); - setOpenConfirmation(false); - setNom(''); - setQuantite(''); - setQuantiteDisponible(''); - } - }catch(error){ - }finally{ + if(id){ + setRessources([...ressources,{...nouvelleRessource, id}]); setOpenConfirmation(false); - setLoading(false); + setNom(''); + setQuantite(''); + setQuantiteDisponible(''); } + }catch(error){ + }finally{ + setOpenConfirmation(false); + setLoading(false); } } + } + + function onCancel(): void { + setOpenConfirmation(false); + } - function onCancel(): void { - setOpenConfirmation(false); - } function isValidRessource():Boolean{ return nom!= "" && quantite != "" } - const renderValidationScreen = () => { - return( - - - - Créer la nouvelle ressource {ressourceType} suivante ? : - - Nom: {nom===''?"NONE":nom} - Quantité Total: {quantite===''?"0":quantite} - - - onConfirm()}> - Confirmer - - - - onCancel()}> - Annuler - - + const renderValidationScreen = () => { + return( + + + + Créer la nouvelle ressource {ressourceType} suivante ? : + + Nom: {nom===''?"NONE":nom} + Quantité Total: {quantite===''?"0":quantite} - - - ) - } + + onConfirm()}> + Confirmer + + + + onCancel()}> + Annuler + + + + + + ) + } const renderInut = (name : string, preFill : string, value : string, setValue : ((text:string) => void),numeric:boolean) => { return ( @@ -129,7 +130,7 @@ export default function AddRessource({ressourceType, ...otherProps }: Props) { ); }; -return ( + return ( {editMode && diff --git a/components/add/select/ressourceSummary.tsx b/components/add/select/ressourceSummary.tsx index 2013e06..96a3b24 100644 --- a/components/add/select/ressourceSummary.tsx +++ b/components/add/select/ressourceSummary.tsx @@ -1,9 +1,12 @@ import { Chantier, Ressources } from '@/class/class'; import { ThemedView, } from '@/components/theme/themed-view'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Image, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { ThemedText } from '@/components/theme/themed-text'; import { ThemedButton } from '@/components/theme/themed-button'; +import { getNbUseRessources } from '@/class/utils'; +import { useReservations } from '@/app/ContextReservation'; +import { getReservations } from '@/services/ressourcesService'; type RessourcesQte = [Ressources, number]; @@ -16,11 +19,11 @@ type Props = { export default function RessourceSummary({ressource: ressource,qte,style,sendRessource: sendRessource, ...otherProps }: Props) { - + const { reservations, setReservations } = useReservations(); const [count,setCount] = useState(qte); function onPressAdd(ressource: Ressources): void { - if(count { + async function loadReservations() { + const list = await getReservations(); + setReservations(list); + } + + loadReservations(); + }, []); + + return( - {ressource.id} - {ressource.name} - {ressource.quantity} - {ressource.type} + Nom : {ressource.name} + Restant : {ressource.quantity-getNbUseRessources(ressource,reservations)}/{ressource.quantity} onPressAdd(ressource)}> + - {count}/{ressource.quantity} + {count}/{ressource.quantity-getNbUseRessources(ressource,reservations)} onPressSub(ressource)}> - diff --git a/components/anomaly.tsx b/components/anomaly.tsx index 6d2be82..64b6506 100644 --- a/components/anomaly.tsx +++ b/components/anomaly.tsx @@ -67,10 +67,10 @@ export default function Anomaly({data,style}: Props) { - Ajouter + Ajouter - Choisir une image + Choisir une image {imageUri && ( @@ -151,13 +151,11 @@ const styles = StyleSheet.create({ marginRight: 8, }, addButton: { - color: "white", paddingVertical: 8, paddingHorizontal: 12, borderRadius: 8, }, addButtonText: { - color: "white", fontWeight: "bold", }, image: { width: 200, height: 200, borderRadius: 10 } diff --git a/components/chantierSummary.tsx b/components/chantierSummary.tsx index b44e544..e49c425 100644 --- a/components/chantierSummary.tsx +++ b/components/chantierSummary.tsx @@ -3,6 +3,7 @@ import { ThemedView, } from '@/components/theme/themed-view'; import React from 'react'; import { Image, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'; import { ThemedText } from './theme/themed-text'; +import { getNbItemReservation } from '@/class/utils'; type Props = { data: { @@ -20,9 +21,20 @@ export default function ChantierSummary({data,style , ...otherProps }: Props) { + Id: {data.chantier.id} + Objet: {data.chantier.name} Adresse: {data.chantier.adresse} Chef de chantier: {data.chantier.chef.last_name}{" "}{data.chantier.chef.name} État: {data.chantier.etat} + equipe: + {getNbItemReservation(data.chantier.equipe)} ({data.chantier.equipe.length} type{data.chantier.equipe.length>1&&"s"}) + + materiel: + {getNbItemReservation(data.chantier.materiel)} ({data.chantier.materiel.length} type{data.chantier.materiel.length>1&&"s"}) + + vehicules: + {getNbItemReservation(data.chantier.vehicules)} ({data.chantier.vehicules.length} type{data.chantier.vehicules.length>1&&"s"}) + ) : @@ -42,7 +54,7 @@ const styles = StyleSheet.create({ borderRadius: 10, //borderWidth: 1, flexDirection: 'row', - height: 150, + //height: 150, gap: 10, }, image:{ diff --git a/components/selectChantier.tsx b/components/selectChantier.tsx index a7d32a5..c02f8db 100644 --- a/components/selectChantier.tsx +++ b/components/selectChantier.tsx @@ -65,14 +65,14 @@ export default function SelectChantier() { open.value = withTiming(isOpen ? 1 : 0); }, [isOpen]); - useEffect(() => { + /*useEffect(() => { async function loadChantiers() { const list = await getChantiers(); setChantiers(list); } loadChantiers(); - }, []); + }, []);*/ const filteredChantiers = chantiers.filter((chantier) => { var keyWords:string[] = search.toLowerCase().split(" ") ; diff --git a/services/ressourcesService.ts b/services/ressourcesService.ts index ac7ed0d..67f2a76 100644 --- a/services/ressourcesService.ts +++ b/services/ressourcesService.ts @@ -1,4 +1,4 @@ -import { addDoc, arrayUnion, collection, doc, Firestore, getDoc, getDocs, Timestamp, updateDoc, DocumentReference } from "firebase/firestore"; +import { addDoc, arrayUnion, collection, doc, Firestore, getDoc, getDocs, Timestamp, updateDoc, DocumentReference, query, where } from "firebase/firestore"; import { Chantier, Reservation, Ressources, User } from "../class/class"; import { db } from "../firebase_config"; @@ -53,67 +53,100 @@ export async function addRessources(ressourceData: Omit): Prom } ///////////////////////////////////CHANTIER///////////////////////////////// export async function getChantiers(): Promise { - const snap = await getDocs(collection(db, "chantier")); const chantiers: Chantier[] = []; + try { + const snap = await getDocs(collection(db, "chantier")); - for (const docSnap of snap.docs) { - const data = docSnap.data(); - //Faut convertir les Timestamp en Date ( merci à firebase :) ) - const dateDep = data.dateDep instanceof Timestamp ? data.dateDep.toDate() : new Date(data.dateDep); - let chef: User | null = null; - if (data.chef) { - const chefSnap = await getDoc(data.chef); - if (chefSnap.exists()) { - chef = chefSnap.data() as User; - } - } - /* - let equipe: Reservation[] = []; - if (Array.isArray(data.equipe)) { - equipe = await Promise.all( - data.equipe.map(async (ref: any) => { - const snap = await getDoc(ref); - return snap.exists() ? (snap.data() as Reservation) : null; - }) - ).then(list => list.filter(x => x !== null)) as Reservation[]; - } + - let vehicules: Reservation[] = []; - if (Array.isArray(data.vehicules)) { - vehicules = await Promise.all( - data.vehicules.map(async (ref: any) => { - const snap = await getDoc(ref); - return snap.exists() ? (snap.data() as Reservation) : null; - }) - ).then(list => list.filter(x => x !== null)) as Reservation[]; - } + for (const docSnap of snap.docs) { + try { + const data = docSnap.data(); + //Faut convertir les Timestamp en Date ( merci à firebase :) ) + const dateDep = data.dateDep instanceof Timestamp ? data.dateDep.toDate() : new Date(data.dateDep); + let chef: User | null = null; + if (data.chef) { + const chefSnap = await getDoc(data.chef); + if (chefSnap.exists()) { + chef = chefSnap.data() as User; + } + } + const equipe:Reservation[] = []; + const vehicules:Reservation[] = []; + const materiel:Reservation[] = []; + const all:Reservation[] = await getReservationsByChantier(docSnap.id); - let materiel: Reservation[] = []; - if (Array.isArray(data.materiel)) { - materiel = await Promise.all( - data.materiel.map(async (ref: any) => { - const snap = await getDoc(ref); - return snap.exists() ? (snap.data() as Reservation) : null; - }) - ).then(list => list.filter(x => x !== null)) as Reservation[]; - } - */ - var equipe:Reservation[] = []; - var vehicules:Reservation[] = []; - var materiel:Reservation[] = []; - chantiers.push({ - ...data, - id: docSnap.id, - dateDep, - chef, - equipe, - vehicules, - materiel, - } as Chantier); + all.forEach(element => { + if(element.ressource.type==="Ouvrier"){ + equipe.push(element) + } + else if(element.ressource.type==="Machine"){ + vehicules.push(element) + } + else if(element.ressource.type==="Outil"){ + materiel.push(element) + } + }); + + chantiers.push({ + ...data, + id: docSnap.id, + dateDep, + chef, + equipe, + vehicules, + materiel, + } as Chantier); + } catch (error) { + console.error("Erreur lors de la lecture d'un chantiers : " + error); + //alert("Erreur lors de la lecture d'un chantiers : " + error); } + } return chantiers; + } catch (error) { + alert("Erreur lors de la lecture des chantiers : " + error); + } + return chantiers } +//récupère les reservations d'un chantier +export async function getReservationsByChantier(chantierId: string): Promise { + const q = query( + collection(db, "Reservation"), + where("chantier", "==", doc(db, "chantier", chantierId)), + ); + + const snap = await getDocs(q); + + const results = await Promise.all( + snap.docs.map(convertReservation) + ); + + return results.filter( + (r): r is Reservation => r !== null + ); +} + +///////////////////////////////////RESERVATION///////////////////////////////// +export async function getReservations(): Promise { + try { + const snap = await getDocs(collection(db, "Reservation")); + + const results = await Promise.all( + snap.docs.map(convertReservation) + ); + + return results.filter( + (r): r is Reservation => r !== null + ); + + } catch (error) { + console.error("Erreur lors de la lecture des Reservations : " + error); + return []; + } +} + + //CHANGE CHANTIER STATUS export async function changeChantierStatus(chantierId: string, newStatus: string): Promise { try { @@ -177,18 +210,23 @@ type ReservationFirestore = { quantity: number; }; -async function convertReservation(res: any): Promise { +async function convertReservation(res: any): Promise { + try { + const data = res.data() as ReservationFirestore; - const data = res.data() as ReservationFirestore; - const chantierSnap = await getDoc(data.chantier as DocumentReference); - const ressourceSnap = await getDoc(data.ressource as DocumentReference); + const chantierSnap = await getDoc(data.chantier as DocumentReference); + const ressourceSnap = await getDoc(data.ressource as DocumentReference); - return { - id: res.id, - chantier: chantierSnap.data() as Chantier, - ressource: ressourceSnap.data() as Ressources, - quantity: data.quantity, - }; + return { + id: res.id, + chantier: chantierSnap.data() as Chantier, + ressource: ressourceSnap.data() as Ressources, + quantity: data.quantity, + }; + } catch (err) { + console.warn("Reservation ignorée :", res.id , err); + return null; + } } @@ -201,7 +239,7 @@ export async function sendNewChantier(chantier:Chantier): Promise { adresse:chantier.adresse, etat:chantier.etat, contact:chantier.contact, - chef: doc(db, "users", chantier.chef.id), //un objet déjà dans la base de donné + chef: doc(db, "user", chantier.chef.id), //un objet déjà dans la base de donné date: Timestamp.fromDate(chantier.dateDep), tempsEst: chantier.tempsEst, anomalies: chantier.anomalies ?? [], //strings[]