7 Commits

15 changed files with 238 additions and 135 deletions

View File

@@ -27,7 +27,7 @@ npx expo start
#### Présentation de l'application :
##### 5 écrans :
Accuil :
Accueil :
- Affiche le chantier sélectionné :
- Résumé du chantier
- état éditable par l'utilisateur
@@ -36,15 +36,20 @@ Accuil :
Ressources :
Ouvriers :
Permet de voir les ressources enregistrées dans la base de données et leurs différentes données (Nom , Type, quantité totale et quantité disponible). On peut rechercher par le nom et un filtre est aussi disponible pour affiner la recherche.
Users :
Permet de voir les différents utilisateurs enregistrés dans la base de données, ainsi que leur rang (chef ou responsable).
MapScreen :
Permet de voir les différents chantiers sur une carte avec leurs adresses et leur état.
Ajouter :
Permet d'ajouter un chantier ou une ressource (ouvrier,véhicule,outil)
##### Fonctionnalité manquante :
Par manque de temps nous n'avons pas peu finnali certaine fonctionnalité
Par manque de temps nous n'avons pas pu finaliser certaines fonctionnalités
- possibilité de modifier les ressources d'un chantier (ex : réajustement des besoins)
- modifier la quantité totale d'une ressource (ex : restock de ressources)

View File

@@ -1,33 +1,27 @@
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { } from 'expo-router';
import React from 'react';
import { HapticTab } from '@/components/expoExempleComponents/haptic-tab';
import { IconSymbol } from '@/components/ui/icon-symbol';
import { Colors } from '@/constants/theme';
import { useColorScheme } from '@/hooks/use-color-scheme';
import GestionOuvrier from './gestion_ouvrier';
import ListMateriel from './gestionnaire_ressource';
import Home from './home';
import MapScreen from './mapScreen';
import AntDesign from '@expo/vector-icons/AntDesign';
import AddScreen from './addScreen';
const Tabs = createBottomTabNavigator();
import { Tabs } from 'expo-router';
import React from 'react';
import { useAuthHandler } from '../AuthHandler';
import { useUser } from '../ContextUser';
export default function TabLayout() {
const colorScheme = useColorScheme();
const { role } = useUser();
// Handle auth in tabs layout
useAuthHandler();
return (
<Tabs.Navigator
initialRouteName='explore'
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
tabBarButton: HapticTab,
}}>
<Tabs screenOptions={{tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, headerShown: false, tabBarButton: HapticTab}}>
<Tabs.Screen name="index" options={{ href: null}}/>
<Tabs.Screen name="explore" options={{ href: null }}/>
<Tabs.Screen name="templateSreen" options={{ href: null}}/>
<Tabs.Screen
name="home"
component={Home}
options={{
title: 'Home',
tabBarIcon: ({ color }) => (
@@ -37,7 +31,6 @@ export default function TabLayout() {
/>
<Tabs.Screen
name="gestionnaire_ressource"
component={ListMateriel}
options={{
title: 'Ressources',
tabBarIcon: ({ color }) => (
@@ -46,34 +39,29 @@ export default function TabLayout() {
}}
/>
<Tabs.Screen
name="GestionOuvrier"
component={GestionOuvrier}
name="gestion_user"
options={{
title: 'Ouvriers',
title: 'Users',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="person.fill" color={color} />,
}}
/>
<Tabs.Screen
name="explore"
component={MapScreen}
name="mapScreen"
options={{
title: 'MapScreen',
title: 'Map',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
}}
>
</Tabs.Screen>
/>
<Tabs.Screen
name="Ajouter"
component={AddScreen}
name="addScreen"
options={{
title: 'Ajouter',
href: role === 'resp' ? '/(tabs)/addScreen' : null,
tabBarIcon: ({ color }) => (
<AntDesign name="plus" size={24} color={color} />
),
}}
/>
</Tabs.Navigator>
</Tabs>
);
}

View File

@@ -2,14 +2,11 @@ import { ThemedText } from "@/components/theme/themed-text";
import { ThemedTextInput } from "@/components/theme/themed-textinput";
import { ThemedView } from "@/components/theme/themed-view";
import Constants from "expo-constants"; //pour connaître la taille de la barre menu de l'OS en haut
import { useLocalSearchParams, useRouter } from "expo-router";
import React, { useEffect, useMemo, useState } from "react";
import { FlatList, Image, StyleSheet, Text, View } from "react-native";
import React, { useEffect, useState } from "react";
import { FlatList, StyleSheet, Text, View } from "react-native";
import { getUsers } from "@/services/ressourcesService";
import { useChantier } from "../ContextChantier";
import SelectChantier from "@/components/selectChantier";
import { Ressources } from "@/class/class";
import { getRessources } from "@/services/ressourcesService";
import { User } from "@/class/class";
type Concert = {
group: string;
@@ -22,19 +19,15 @@ type Concert = {
favorite: boolean;
};
export default function GestionOuvrier() {
const router = useRouter();
const { nom, prenom } = useLocalSearchParams(); // Recup data ecran precedent
export default function GestionUser() {
const [search, setSearch] = useState("");
const { chantier, setChantier } = useChantier();
const [artisans, setRessources] = useState<Ressources[]>([]);
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
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");
setRessources(data);
const data = (await getUsers());
setUsers(data);
} catch (error) {
console.error("Erreur lors du chargement :", error);
}
@@ -42,17 +35,15 @@ export default function GestionOuvrier() {
loadData();
}, []);
const renderItem = ({ item, index }: { item?: Ressources; index: number }) => {
const renderItem = ({ item, index }: { item?: User; index: number }) => {
if (!item) {
return null;
}
return (
<ThemedView lvl={1} shadow={true} style={styles.card}>
<ThemedView lvl={1} style={styles.info}>
<Image source={{ uri: item.Image }} style={styles.image} />
<ThemedText style={styles.group}>{item.name}</ThemedText>
<ThemedText>{item.quantity}</ThemedText>
<ThemedText>{item.type}</ThemedText>
<ThemedText style={styles.group}>{item.name} {item.last_name}</ThemedText>
<ThemedText>{item.role}</ThemedText>
</ThemedView>
</ThemedView>
);
@@ -67,7 +58,7 @@ export default function GestionOuvrier() {
<FlatList
data={artisans}
data={users}
renderItem={renderItem}
keyExtractor={(_, index) => index.toString()}
contentContainerStyle={{ paddingBottom: 40 }}

View File

@@ -8,19 +8,25 @@ import { useLocalSearchParams, useRouter } from "expo-router";
import React, { useEffect, useState } from "react";
import { FlatList, Image, StyleSheet, Text, View } from "react-native";
import { Ressources } from "../../class/class";
import { getRessources } from "../../services/ressourcesService";
import { getReservations, getRessources } from "../../services/ressourcesService";
import SelectChantier from "@/components/selectChantier";
import { useRessources } from "../ContextRessource";
import { useChantier } from "../ContextChantier";
import { getNbUseRessources, getNbUseRessourcesInChantier, isInChantier } from "@/class/utils";
import { useReservations } from "../ContextReservation";
export default function GestionnaireRessource() {
const [search, setSearch] = useState("");
const {ressources, setRessources} = useRessources();
const {reservations, setReservations} = useReservations();
const {chantier, setChantier} = useChantier();
const [filterType, setFilterType] = useState("Tout");
const [showFilterMenu, setShowFilterMenu] = useState(false);
const [filterChantier, setFilterChantier] = useState(false);
const router = useRouter();
useEffect(() => {
async function loadData() {
async function loadDataRessources() {
try {
const data = await getRessources();
setRessources(data);
@@ -28,13 +34,23 @@ export default function GestionnaireRessource() {
console.error("Erreur lors du chargement :", error);
}
}
loadData();
async function loadDataReservations() {
try {
const data = await getReservations();
setReservations(data);
} catch (error) {
console.error("Erreur lors du chargement :", error);
}
}
loadDataRessources();
loadDataReservations();
}, []);
const filteredData = ressources.filter((r) => {
const matchName = r.name.toLowerCase().includes(search.toLowerCase());
const matchType = filterType === "Tout" || r.type === filterType;
return matchName && matchType;
return matchName && matchType && (!filterChantier || (chantier && isInChantier(r,chantier,reservations)));
});
const renderRessource = ({ item }: { item: Ressources }) => {
@@ -43,11 +59,13 @@ export default function GestionnaireRessource() {
<ThemedView lvl={1} shadow={true} style={styles.card}>
<Image source={{ uri: item.Image }} style={styles.image} />
<ThemedView lvl={1} style={styles.info}>
<ThemedText>Id : {item.id}</ThemedText>
<ThemedText>Nom : {item.name}</ThemedText>
<ThemedText>Type : {item.type}</ThemedText>
<ThemedText>Quantité totale : {item.quantity}</ThemedText>
<ThemedText>Quantité disponible : {item.available_quantity}</ThemedText>
<ThemedText>Quantité disponible : {item.quantity-getNbUseRessources(item, reservations)}</ThemedText>
{filterChantier&&chantier &&
<ThemedText>Quantité utilisé dans le chantier : {getNbUseRessourcesInChantier(item,chantier, reservations)}</ThemedText>
}
</ThemedView>
</ThemedView>
);
@@ -84,6 +102,7 @@ export default function GestionnaireRessource() {
>
<ThemedText style={{ textAlign: "center" }}>{t}</ThemedText>
</ThemedButton>
))}
{/* Bouton "Fermer" remplacé */}
@@ -117,14 +136,22 @@ export default function GestionnaireRessource() {
</ThemedView>
{/* Bouton filtre en haut à droite */}
<View style={{flexDirection: "row", gap:5}}>
<ThemedButton
lvl={1}
shadow={true}
style={{ padding: 10, borderRadius: 8, marginTop: 10 }}
style={styles.button}
onPress={() => setShowFilterMenu(true)}
>
<ThemedText>{`Filtre: ${filterType}`}</ThemedText>
</ThemedButton>
<ThemedButton style={styles.button}>
<ThemedText onPress={() => setFilterChantier(!filterChantier)}>
{filterChantier?"chantier courant":"tous"}
</ThemedText>
</ThemedButton>
</View>
</View>
}
ListEmptyComponent={
@@ -214,4 +241,9 @@ const styles = StyleSheet.create({
marginBottom: 20,
textAlign: "center",
},
button:{
padding: 10,
borderRadius: 8,
marginTop: 10
},
});

View File

@@ -61,7 +61,7 @@ const region = {
<Marker
key = {chantier.id}
coordinate={{ latitude: chantier.latitude, longitude: chantier.longitude}}
title={chantier.adresse}
title={chantier.name}
description={chantier.etat}
/>
))}

45
app/AuthHandler.tsx Normal file
View File

@@ -0,0 +1,45 @@
import { useRouter, useSegments } from "expo-router";
import { onAuthStateChanged } from "firebase/auth";
import { doc, getDoc } from "firebase/firestore";
import { useEffect } from "react";
import { auth, db } from "../firebase_config";
import { useUser } from "./ContextUser";
export function useAuthHandler() {
const router = useRouter();
const segments = useSegments();
const { setUser, setRole } = useUser();
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
if (!currentUser) {
setUser(null);
setRole(null);
router.replace("/login/login");
return;
}
const userDocRef = doc(db, "user", currentUser.uid);
const userDoc = await getDoc(userDocRef);
if (!userDoc.exists()) {
setUser(null);
setRole(null);
router.replace("/login/login");
return;
}
const { role } = userDoc.data();
setUser(currentUser);
setRole(role);
// Only redirect if we're on login page
const inAuthGroup = segments[0] === 'login';
if (inAuthGroup) {
router.replace("/(tabs)/home");
}
});
return unsubscribe;
}, []);
}

View File

@@ -1,16 +1,11 @@
import { ThemedText } from "@/components/theme/themed-text";
import { ThemedTextInput } from "@/components/theme/themed-textinput";
import { ThemedView } from "@/components/theme/themed-view";
import { router } from "expo-router";
import {
signInWithEmailAndPassword
} from "firebase/auth";
import { signInWithEmailAndPassword } from "firebase/auth";
import React, { useState } from "react";
import { Button, StyleSheet, View } from "react-native";
import { auth } from "../../firebase_config";
const DEFAULT_ROLE = "resp";
const LoginScreen: React.FC = () => {
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
@@ -18,21 +13,11 @@ const LoginScreen: React.FC = () => {
const handleLogin = async () => {
try {
await signInWithEmailAndPassword(auth, email, password);
router.replace("/(tabs)");
} catch (error: any) {
alert(error.message);
}
};
/*const handleRegister = async () => {
try {
await createUserWithEmailAndPassword(auth, email, password);
router.replace("/(tabs)");
} catch (error: any) {
alert(error.message);
}
}; */
return (
<ThemedView lvl={1} style={styles.container}>
<ThemedText style={styles.title}>Se connecter</ThemedText>
@@ -45,7 +30,6 @@ const LoginScreen: React.FC = () => {
value={email}
onChangeText={setEmail}
autoCapitalize="none"
/>
<ThemedTextInput
lvl = {2}
@@ -59,7 +43,6 @@ const LoginScreen: React.FC = () => {
/>
<Button title="Se connecter" onPress={handleLogin} />
<View style={{ height: 10 }} />
{/* <Button title="S'inscrire" onPress={handleRegister} /> */}
</ThemedView>
);
};
@@ -76,7 +59,6 @@ const styles = StyleSheet.create({
},
input: {
borderWidth: 1,
//borderColor: "#ccc",
borderRadius: 8,
padding: 10,
marginBottom: 10,

View File

@@ -30,8 +30,8 @@ export type Ressources = {
type: string; //"Machine","Outil","Ouvrier"
Image: string;
quantity: number;
available_quantity: number;
allocation: Reservation[];
//available_quantity: number;
//allocation: Reservation[];
};
export type Reservation = {

View File

@@ -29,3 +29,26 @@ export function getNbUseRessources(ressource:Ressources, allReservations:Reserva
})
return res;
}
export function getNbUseRessourcesInChantier(ressource:Ressources,chantier: Chantier, allReservations:Reservation[]):number{
var res:number = 0;
getReservationOfRessource(ressource,allReservations).forEach(reserv => {
if(reserv.chantier.id === chantier.id && reserv.ressource.id===ressource.id){
res+=reserv.quantity;
}
})
return res;
}
export function isInChantier(ressource:Ressources, chantier: Chantier, allReservations:Reservation[]):boolean{
console.log(allReservations.length+ " --------------------------------");
const reservations:Reservation[] = getReservationOfRessource(ressource,allReservations);
var res=false;
reservations.forEach(reserv => {
console.log(reserv.chantier.id + " " + chantier.id)
if(reserv.chantier.id === chantier.id){
res=true;
}
});
return res;
}

View File

@@ -1,24 +1,17 @@
import ChantierSummary from '@/components/chantierSummary';
import SelectChantier from '@/components/selectChantier';
import SetStatus from '@/components/setStatus';
import { ThemedView } from '@/components/theme/themed-view';
import React, { useEffect, useState } from 'react';
import { StyleSheet, ScrollView, Button, TextInput, Text, View, Modal } from 'react-native';
import { useChantier } from '@/app/ContextChantier';
import { useRessources } from '@/app/ContextRessource';
import { useUser } from '@/app/ContextUser';
import { getRessources, getUsers, addChantier , addRessources} from '@/services/ressourcesService';
import { Chantier, Ressources, User } from '@/class/class';
import { ThemedText } from '@/components/theme/themed-text';
import { Ressources } from '@/class/class';
import { ThemedButton } from '@/components/theme/themed-button';
import { ThemedText } from '@/components/theme/themed-text';
import { ThemedTextInput } from '@/components/theme/themed-textinput';
import DateTimePicker, { DateTimePickerEvent } from '@react-native-community/datetimepicker';
import { ThemedView } from '@/components/theme/themed-view';
import { addRessources } from '@/services/ressourcesService';
import React, { useState } from 'react';
import { Modal, ScrollView, StyleSheet, View } from 'react-native';
import Constants from 'expo-constants'; //pour connaître la taille de la barre menu de l'OS en haut
import SelectChafChantier from '@/components/add/select/selectChefChantier';
import SelectMachine from '@/components/selectMachine';
type Dictionary = {
@@ -66,9 +59,9 @@ export default function AddRessource({ressourceType, ...otherProps }: Props) {
name: nom,
type : ressourceType,
quantity : parseInt(quantite),
available_quantity : parseInt(quantite),
//available_quantity : parseInt(quantite),
Image : "",
allocation : [],
//allocation : [],
};
const id = await addRessources(nouvelleRessource);

View File

@@ -18,22 +18,21 @@ export default function ChantierSummary({data,style , ...otherProps }: Props) {
{data.chantier ? (
<ThemedView lvl={4} style={styles.chantier}>
<View>
<Image source={{ uri:"https://cdn.discordapp.com/attachments/1425108443571945644/1427207643180826757/raw.png?ex=693f1a72&is=693dc8f2&hm=86ffb97145fc8d3aec822b87d99be233c98477d4424c1ef58f80eb81b17c7c80&" /*chantier.urlImg*/ }} style={styles.image} />
<Image source={{ uri:"" /*chantier.urlImg*/ }} style={styles.image} />
</View>
<View style={{flex: 1}}>
<ThemedText selectable={true}>Id: {data.chantier.id}</ThemedText>
<ThemedText selectable={true}>Objet: {data.chantier.name}</ThemedText>
<ThemedText selectable={true}>Adresse: {data.chantier.adresse}</ThemedText>
<ThemedText selectable={true}>Chef de chantier: {data.chantier.chef.last_name}{" "}{data.chantier.chef.name}</ThemedText>
<ThemedText selectable={true}>État: {data.chantier.etat}</ThemedText>
<ThemedText selectable={true}>equipe:
{getNbItemReservation(data.chantier.equipe)} ({data.chantier.equipe.length} type{data.chantier.equipe.length>1&&"s"})
<ThemedText selectable={true}>
equipe: {getNbItemReservation(data.chantier.equipe)} ({data.chantier.equipe.length} type{data.chantier.equipe.length>1&&"s"})
</ThemedText>
<ThemedText selectable={true}>materiel:
{getNbItemReservation(data.chantier.materiel)} ({data.chantier.materiel.length} type{data.chantier.materiel.length>1&&"s"})
<ThemedText selectable={true}>
materiel: {getNbItemReservation(data.chantier.materiel)} ({data.chantier.materiel.length} type{data.chantier.materiel.length>1&&"s"})
</ThemedText>
<ThemedText selectable={true}>vehicules:
{getNbItemReservation(data.chantier.vehicules)} ({data.chantier.vehicules.length} type{data.chantier.vehicules.length>1&&"s"})
<ThemedText selectable={true}>
vehicules: {getNbItemReservation(data.chantier.vehicules)} ({data.chantier.vehicules.length} type{data.chantier.vehicules.length>1&&"s"})
</ThemedText>
</View>
</ThemedView>

View File

@@ -122,7 +122,7 @@ export default function SelectChantier() {
<Image source={{ uri:"https://cdn.discordapp.com/attachments/1425108443571945644/1427207643180826757/raw.png?ex=693f1a72&is=693dc8f2&hm=86ffb97145fc8d3aec822b87d99be233c98477d4424c1ef58f80eb81b17c7c80&" /*chantier.urlImg*/ }} style={styles.image} />
</View>
<View style={{flex: 1}}>
<ThemedText>Renovation: {item.name}</ThemedText>
<ThemedText>Objet: {item.name}</ThemedText>
<ThemedText>Adresse: {item.adresse}</ThemedText>
<ThemedText>Chef de chantier: {item.chef.last_name}{" "}{item.chef.name}</ThemedText>
<ThemedText>État: {item.etat}</ThemedText>
@@ -138,7 +138,7 @@ export default function SelectChantier() {
<ThemedView lvl={2} shadow={true} style={styles.window}>
<AnimatedThemedButton style={animatedButtonStyle} lvl={isOpen ? 1 : 1} onPress={() => onPressOpen()}>
<ThemedText style={styles.buttonText}>
{isOpen ? "Fermer" : (chantier!=null ? chantier.adresse : "Chantier")}
{isOpen ? "Fermer" : (chantier!=null ? chantier.name : "Chantier")}
</ThemedText>
</AnimatedThemedButton>
{isOpen && (

View File

@@ -121,7 +121,7 @@ const styles = StyleSheet.create({
windowBox:{
zIndex: 2,
//backgroundColor: '#00FFFF40',
width:"30%",
width:"35%",
padding: 10,
paddingLeft: 0,
//overflow: 'hidden',

32
package-lock.json generated
View File

@@ -90,6 +90,7 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz",
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.3",
@@ -1471,6 +1472,7 @@
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
"integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -2329,6 +2331,7 @@
"version": "0.14.6",
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.6.tgz",
"integrity": "sha512-4uyt8BOrBsSq6i4yiOV/gG6BnnrvTeyymlNcaN/dKvyU1GoolxAafvIvaNP1RCGPlNab3OuE4MKUQuv2lH+PLQ==",
"peer": true,
"dependencies": {
"@firebase/component": "0.7.0",
"@firebase/logger": "0.5.0",
@@ -2394,6 +2397,7 @@
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.6.tgz",
"integrity": "sha512-YYGARbutghQY4zZUWMYia0ib0Y/rb52y72/N0z3vglRHL7ii/AaK9SA7S/dzScVOlCdnbHXz+sc5Dq+r8fwFAg==",
"peer": true,
"dependencies": {
"@firebase/app": "0.14.6",
"@firebase/component": "0.7.0",
@@ -2409,7 +2413,8 @@
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz",
"integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==",
"license": "Apache-2.0"
"license": "Apache-2.0",
"peer": true
},
"node_modules/@firebase/auth": {
"version": "1.11.1",
@@ -2859,6 +2864,7 @@
"integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==",
"hasInstallScript": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
},
@@ -3894,6 +3900,7 @@
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.25.tgz",
"integrity": "sha512-zQeWK9txDePWbYfqTs0C6jeRdJTm/7VhQtW/1IbJNDi9/rFIRzZule8bdQPAnf8QWUsNujRmi1J9OG/hhfbalg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@react-navigation/core": "^7.13.6",
"escape-string-regexp": "^4.0.0",
@@ -4096,6 +4103,7 @@
"integrity": "sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -4167,6 +4175,7 @@
"integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.0",
"@typescript-eslint/types": "8.46.0",
@@ -4729,6 +4738,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5414,6 +5424,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.9",
"caniuse-lite": "^1.0.30001746",
@@ -6553,6 +6564,7 @@
"integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -6750,6 +6762,7 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -6988,6 +7001,7 @@
"resolved": "https://registry.npmjs.org/expo/-/expo-54.0.13.tgz",
"integrity": "sha512-F1puKXzw8ESnsbvaKdXtcIiyYLQ2kUHqP8LuhgtJS1wm6w55VhtOPg8yl/0i8kPbTA0YfD+KYdXjSfhPXgUPxw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "54.0.11",
@@ -7055,6 +7069,7 @@
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.9.tgz",
"integrity": "sha512-sqoXHAOGDcr+M9NlXzj1tGoZyd3zxYDy215W6E0Z0n8fgBaqce9FAYQE2bu5X4G629AYig5go7U6sQz7Pjcm8A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@expo/config": "~12.0.9",
"@expo/env": "~2.0.7"
@@ -7079,6 +7094,7 @@
"resolved": "https://registry.npmjs.org/expo-font/-/expo-font-14.0.9.tgz",
"integrity": "sha512-xCoQbR/36qqB6tew/LQ6GWICpaBmHLhg/Loix5Rku/0ZtNaXMJv08M9o1AcrdiGTn/Xf/BnLu6DgS45cWQEHZg==",
"license": "MIT",
"peer": true,
"dependencies": {
"fontfaceobserver": "^2.1.0"
},
@@ -7150,6 +7166,7 @@
"resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.8.tgz",
"integrity": "sha512-MyeMcbFDKhXh4sDD1EHwd0uxFQNAc6VCrwBkNvvvufUsTYFq3glTA9Y8a+x78CPpjNqwNAamu74yIaIz7IEJyg==",
"license": "MIT",
"peer": true,
"dependencies": {
"expo-constants": "~18.0.8",
"invariant": "^2.2.4"
@@ -11394,6 +11411,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -11413,6 +11431,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
@@ -11449,6 +11468,7 @@
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.4.tgz",
"integrity": "sha512-bt5bz3A/+Cv46KcjV0VQa+fo7MKxs17RCcpzjftINlen4ZDUl0I6Ut+brQ2FToa5oD0IB0xvQHfmsg2EDqsZdQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jest/create-cache-key-function": "^29.7.0",
"@react-native/assets-registry": "0.81.4",
@@ -11506,6 +11526,7 @@
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz",
"integrity": "sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@egjs/hammerjs": "^2.0.17",
"hoist-non-react-statics": "^3.3.0",
@@ -11560,6 +11581,7 @@
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.3.tgz",
"integrity": "sha512-GP8wsi1u3nqvC1fMab/m8gfFwFyldawElCcUSBJQgfrXeLmsPPUOpDw44lbLeCpcwUuLa05WTVePdTEwCLTUZg==",
"license": "MIT",
"peer": true,
"dependencies": {
"react-native-is-edge-to-edge": "^1.2.1",
"semver": "7.7.2"
@@ -11588,6 +11610,7 @@
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
"integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==",
"license": "MIT",
"peer": true,
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -11598,6 +11621,7 @@
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz",
"integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"react-freeze": "^1.0.0",
"react-native-is-edge-to-edge": "^1.2.1",
@@ -11613,6 +11637,7 @@
"resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.1.tgz",
"integrity": "sha512-BeNsgwwe4AXUFPAoFU+DKjJ+CVQa3h54zYX77p7GVZrXiiNo3vl03WYDYVEy5R2J2HOPInXtQZB5gmj3vuzrKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.6",
"@react-native/normalize-colors": "^0.74.1",
@@ -11645,6 +11670,7 @@
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.5.1.tgz",
"integrity": "sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-class-properties": "^7.0.0-0",
@@ -11755,6 +11781,7 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
"integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -13177,6 +13204,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -13383,6 +13411,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -14358,6 +14387,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@@ -1,4 +1,4 @@
import { addDoc, arrayUnion, collection, doc, Firestore, getDoc, getDocs, Timestamp, updateDoc, DocumentReference, query, where } from "firebase/firestore";
import { addDoc, arrayUnion, collection, doc, DocumentReference, getDoc, getDocs, query, Timestamp, updateDoc, where } from "firebase/firestore";
import { Chantier, Reservation, Ressources, User } from "../class/class";
import { db } from "../firebase_config";
@@ -40,10 +40,15 @@ export async function getRessources(): Promise<Ressources[]> {
}
//ADD RESSOURCES
export async function addRessources(ressourceData: Omit<Ressources, 'id'>): Promise<string | null> {
export async function addRessources(ressourceData: Ressources): Promise<string | null> {
try {
const colRef = collection(db, "ressources");
const ressourcesRef = await addDoc(colRef, ressourceData);
const ressourcesRef = await addDoc(collection(db, "ressources"), {
name:ressourceData.name,
type:ressourceData.type,
Image: ressourceData.Image,
quantity: ressourceData.quantity,
});
console.log(`Ressource ajoutée avec ID: ${ressourcesRef.id}`);
return ressourcesRef.id;
} catch (err) {
@@ -219,8 +224,14 @@ async function convertReservation(res: any): Promise<Reservation|null> {
return {
id: res.id,
chantier: chantierSnap.data() as Chantier,
ressource: ressourceSnap.data() as Ressources,
chantier: {
id: chantierSnap.id,
...(chantierSnap.data() as Omit<Chantier, "id">),
},
ressource: {
id: ressourceSnap.id,
...(ressourceSnap.data() as Omit<Ressources, "id">),
},
quantity: data.quantity,
};
} catch (err) {
@@ -254,6 +265,10 @@ export async function sendNewChantier(chantier:Chantier): Promise<void> {
}
export async function sendNewReservation(list: Reservation[],chantierId:string): Promise<void> {
list.forEach(reservation => {
console.log("log: " + reservation.ressource.id);
});
const promises = list.map((reservation) =>
addDoc(collection(db,"Reservation"),{
chantier: doc(db, "chantier", chantierId),