diff --git a/ArtisanConnect/api/client.jsx b/ArtisanConnect/api/client.jsx new file mode 100644 index 0000000..6f0489c --- /dev/null +++ b/ArtisanConnect/api/client.jsx @@ -0,0 +1,13 @@ +import axios from "axios"; + +const API_URL = "https://testowe.zikor.pl/api/v1"; + +export async function getUserById(userId) { + try { + const response = await axios.get(`${API_URL}/clients/get/${userId}`); + return response.data; + } catch (err) { + console.error(`Nie udało się pobrać danych użytkownika o ID ${userId}.`, err.response.status); + throw err; + } +} diff --git a/ArtisanConnect/app/(tabs)/dashboard/account.jsx b/ArtisanConnect/app/(tabs)/dashboard/account.jsx index a6bff6d..1b770e5 100644 --- a/ArtisanConnect/app/(tabs)/dashboard/account.jsx +++ b/ArtisanConnect/app/(tabs)/dashboard/account.jsx @@ -1,16 +1,105 @@ +import { Link } from "expo-router"; +import { Pressable } from "react-native"; +import { Box } from "@/components/ui/box"; import { Text } from "@/components/ui/text"; -import { View } from "react-native"; -import { Button, ButtonText } from "@gluestack-ui/themed"; -import { useAuthStore } from "@/store/authStore"; +import { VStack } from "@/components/ui/vstack"; +import { Image } from "@/components/ui/image"; +import { ActivityIndicator } from "react-native"; +import { useEffect, useState } from "react"; +import { getUserById } from "@/api/client"; +import { HStack } from "@gluestack-ui/themed"; -export default function User() { - const signOut = useAuthStore((state) => state.signOut); - - return ( - Użytkownik - - - ) +export default function Account() { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const currentUserId = 2; // Tymczasowo, do czasu zaimplementowania logowania bo nie moge pobrac usera + + useEffect(() => { + const fetchUser = async () => { + setIsLoading(true); + try { + const userData = await getUserById(currentUserId); + setUser(userData); + } catch (err) { + console.error("Błąd podczas pobierania danych użytkownika:", err); + } finally { + setIsLoading(false); + } + }; + fetchUser(); + }, []); + + if (isLoading) { + return ; + } + + if (!user) { + return Nie udało się pobrać danych użytkownika.; + } + + return ( + + + + Zdjęcie profilowe + + + + {user.firstName} {user.lastName} + + + console.log("Edytuj dane użytkownika")} + > + Edytuj profil + + + + + Moje dane + + + E-mail + {user.email || "brak danych"} + + + + + Moje konto + + + + Moje ogłoszenia + + + + + {/*Tak dodałem, można zmienić na coś innego*/} + + + Historia płatności + + + + + + Ustawienia powiadomień + + + + + + Wyloguj się + + + ); } diff --git a/ArtisanConnect/app/(tabs)/dashboard/userNotices.jsx b/ArtisanConnect/app/(tabs)/dashboard/userNotices.jsx index 9f5ef2f..7fe0f2b 100644 --- a/ArtisanConnect/app/(tabs)/dashboard/userNotices.jsx +++ b/ArtisanConnect/app/(tabs)/dashboard/userNotices.jsx @@ -1,4 +1,75 @@ -import { Text } from "@/components/ui/text"; +import { useNoticesStore } from "@/store/noticesStore"; +import { NoticeCard } from "@/components/NoticeCard"; +import {Button} from "react-native"; +import {Box} from "@/components/ui/box"; +import {Text} from "@/components/ui/text"; +import {VStack} from "@/components/ui/vstack"; +import {ActivityIndicator, FlatList } from "react-native"; +import {useEffect, useState} from "react"; + export default function UserNotices() { - return Użytkownik; -} + const { notices, fetchNotices } = useNoticesStore(); + const [isLoading, setIsLoading] = useState(true); + const currentUserId = 1; // Tymczasowo, do czasu zaimplementowania logowania bo nie moge pobrac usera + + useEffect(() => { + const loadNotices = async () => { + setIsLoading(true); + try { + await fetchNotices(); + } catch (err) { + console.error("Błąd podczas pobierania ogłoszeń:", err); + } finally { + setIsLoading(false); + } + }; + loadNotices(); + }, []); + + const userNotices = notices.filter(notice => notice.clientId === currentUserId); + + if (isLoading) { + return ; + } + + return ( + + Moje ogłoszenia + {userNotices.length > 0 ? ( + ( + + + + + + + + )} + keyExtractor={(item) => item.noticeId.toString()} + /> + ) : ( + Nie masz żadnych ogłoszeń. + )} + + ); +} \ No newline at end of file diff --git a/ArtisanConnect/app/notice/[id].jsx b/ArtisanConnect/app/notice/[id].jsx index 5847f79..f95f3c6 100644 --- a/ArtisanConnect/app/notice/[id].jsx +++ b/ArtisanConnect/app/notice/[id].jsx @@ -1,4 +1,4 @@ -import {Stack, useLocalSearchParams} from "expo-router"; +import {Link, Stack, useLocalSearchParams} from "expo-router"; import {Box} from "@/components/ui/box"; import {Card} from "@/components/ui/card"; import {Heading} from "@/components/ui/heading"; @@ -6,19 +6,50 @@ import {Image} from "@/components/ui/image"; import {Text} from "@/components/ui/text"; import {VStack} from "@/components/ui/vstack"; import {Ionicons} from "@expo/vector-icons"; -import {ActivityIndicator} from "react-native"; -import {useEffect, useState} from "react"; +import {ActivityIndicator, Dimensions, FlatList, View, TextInput} from "react-native"; +import {useEffect, useState, useRef} from "react"; import {useNoticesStore} from "@/store/noticesStore"; import {useWishlist} from "@/store/wishlistStore"; -import {Pressable} from "react-native"; +import {Pressable, ScrollView} from "react-native"; +import {getUserById} from "@/api/client"; + +const {width} = Dimensions.get("window"); + export default function NoticeDetails() { const {id} = useLocalSearchParams(); - const [image, setImage] = useState(null); + const [images, setImages] = useState([]); const [isImageLoading, setIsImageLoading] = useState(true); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); + const [user, setUser] = useState(null); + const [isUserLoading, setIsUserLoading] = useState(true); + const flatListRef = useRef(null); + const [currentIndex, setCurrentIndex] = useState(0); + + + const [isMessageFormVisible, setIsMessageFormVisible] = useState(false); + const [message, setMessage] = useState(''); + const [Email, setEmail] = useState(''); + const handleSendMessage = () => { + console.log('Wiadomość do:', user?.email); + console.log('Email nadawcy:', Email); + console.log('Treść:', message); + + setIsMessageFormVisible(false); + setMessage(''); + setEmail(''); + }; + + const formatDate = (dateString) => { + const date = new Date(dateString); + return date.toLocaleDateString('pl-PL', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); + }; const {getNoticeById, getAllImagesByNoticeId} = useNoticesStore(); const addNoticeToWishlist = useWishlist((state) => state.addNoticeToWishlist); @@ -26,6 +57,16 @@ export default function NoticeDetails() { const isInWishlist = useWishlist((state) => notice ? state.wishlistNotices.some((item) => item.noticeId === notice.noticeId) : false ); + const onViewableItemsChanged = useRef(({ viewableItems }) => { + if (viewableItems.length > 0) { + setCurrentIndex(viewableItems[0].index); + } + }).current; + + const viewabilityConfig = useRef({ + itemVisiblePercentThreshold: 70, + }).current; + useEffect(() => { const fetchNotice = async () => { @@ -53,8 +94,9 @@ export default function NoticeDetails() { setIsImageLoading(true); if (notice) { try { - const images = await getAllImagesByNoticeId(notice.noticeId); - setImage(images && images.length > 0 ? images[0] : "https://http.cat/404.jpg"); + const fetchedImages = await getAllImagesByNoticeId(notice.noticeId); + setImages(fetchedImages && fetchedImages.length > 0 ? fetchedImages : ["https://http.cat/404.jpg"]); + } catch (err) { console.error("Error while loading images:", err); setImage("https://http.cat/404.jpg"); @@ -69,6 +111,25 @@ export default function NoticeDetails() { } }, [notice]); + useEffect(() => { + const fetchUser = async () => { + if (notice && notice.clientId) { + setIsUserLoading(true); + try { + const userData = await getUserById(notice.clientId); + setUser(userData); + } catch (err) { + console.error("Nie udało się pobrać danych użytkownika:", err); + } finally { + setIsUserLoading(false); + } + } + }; + + fetchUser(); + }, [notice]); + + if (isLoading) { return ; } @@ -93,24 +154,60 @@ export default function NoticeDetails() { ) : ( - image + + ( + + {`Zdjęcie + + )} + keyExtractor={(item, index) => index.toString()} + /> + + {images.length > 1 && ( + + {images.map((_, index) => ( + + ))} + + )} + )} + + {formatDate(notice.publishDate)} + + {notice.title} - - - {notice.price}zł + + + + Cena: + {notice.price} zł + { if (isInWishlist) { @@ -127,7 +224,108 @@ export default function NoticeDetails() { /> + + + Kategoria: {notice.category} + + + + + Opis ogloszenia + + + {notice.description} + + + + + + Uzytkownik: + + {isUserLoading ? ( + + ) : user ? ( + <> + + Zdjęcie profilowe + + + + + {user.firstName} {user.lastName} + + + Email: {user.email} + + setIsMessageFormVisible(true)} + className="mt-3 bg-primary-500 py-2 px-4 rounded-md" + > + Wyślij wiadomość + + + + Zobacz więcej ogłoszeń od {user.firstName} + + + + + ) : ( + Błąd podczas ładowania danych użytkownika + )} + + + + + {isMessageFormVisible && ( + + + Wyślij wiadomość do {user?.firstName} + + Do: + + {user?.email || "Brak adresu e-mail"} + + Twój e-mail: + + + + + + setIsMessageFormVisible(false)} + className="bg-gray-300 py-2 px-4 rounded-md" + > + Anuluj + + + + Wyślij + + + + + )} + ); } \ No newline at end of file diff --git a/ArtisanConnect/app/user/[userId].jsx b/ArtisanConnect/app/user/[userId].jsx new file mode 100644 index 0000000..0f85231 --- /dev/null +++ b/ArtisanConnect/app/user/[userId].jsx @@ -0,0 +1,69 @@ +import { useLocalSearchParams } from "expo-router"; +import { useState, useEffect } from "react"; +import { FlatList, ActivityIndicator, Text } from "react-native"; +import { Box } from "@/components/ui/box"; +import { Image } from "@/components/ui/image"; +import { VStack } from "@/components/ui/vstack"; +import { Heading } from "@/components/ui/heading"; +import { getUserById } from "@/api/client"; +import { useNoticesStore } from "@/store/noticesStore"; +import { NoticeCard } from "@/components/NoticeCard"; + +export default function UserProfile() { + const { userId } = useLocalSearchParams(); + const [user, setUser] = useState(null); + const [isUserLoading, setIsUserLoading] = useState(true); + const { notices } = useNoticesStore(); + + useEffect(() => { + const fetchUser = async () => { + setIsUserLoading(true); + try { + const userData = await getUserById(Number(userId)); + setUser(userData); + } catch (err) { + console.error("Błąd podczas pobierania danych użytkownika:", err); + setUser(null); + } finally { + setIsUserLoading(false); + } + }; + fetchUser(); + }, [userId]); + + if (isUserLoading) { + return ; + } + + if (!user) { + return Nie znaleziono użytkownika; + } + + const userNotices = notices.filter(notice => notice.clientId === Number(userId)); + + return ( + + + Zdjęcie profilowe + + {user.firstName} {user.lastName} + + + {userNotices.length > 0 ? ( + } + keyExtractor={(item) => item.noticeId.toString()} + /> + ) : ( + Ten użytkownik nie ma żadnych ogłoszeń. + )} + + ); +} \ No newline at end of file