12 Commits

Author SHA1 Message Date
Patryk
472dcfc96a Merge remote-tracking branch 'origin/notice_apperance' into integrateWithAuth 2025-06-07 12:54:19 +02:00
df44742a7b safe area view 2025-06-06 20:36:15 +02:00
48cf5cd6c4 fix integration 2025-06-05 21:40:30 +02:00
f6065893d0 better look for account 2025-06-03 22:21:39 +02:00
8722488a45 Added content to account.jsx and userNotices.jsx 2025-06-03 00:19:12 +02:00
1c57984a36 Improved looking 2025-06-02 23:59:05 +02:00
d12ae04f8a added the ability to see the notices of a particular user(transition from the notice) 2025-06-02 23:56:56 +02:00
774b2ef192 improved display of the time of adding notice 2025-05-27 23:51:31 +02:00
95a632741a Added send message form 2025-05-27 22:28:28 +02:00
925dde0bb0 Added scrollview and send message button to notice 2025-05-27 20:55:31 +02:00
06218dcdd4 Added Image Slider 2025-05-25 20:38:35 +02:00
eb1ebc3464 V1 2025-05-25 17:41:07 +02:00
14 changed files with 557 additions and 63 deletions

View File

@@ -1,10 +1,15 @@
import axios from "axios";
import {useAuthStore} from "@/store/authStore";
const API_URL = "https://testowe.zikor.pl/api/v1";
export async function listCategories() {
const { token } = useAuthStore.getState();
const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
try {
const response = await axios.get(`${API_URL}/vars/categories`);
const response = await axios.get(`${API_URL}/vars/categories`, { headers });
return response.data;
} catch (err) {
console.error("Nie udało się pobrać listy kategorii.", err.response.status);

View File

@@ -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;
}
}

View File

@@ -2,9 +2,9 @@ import axios from "axios";
import FormData from 'form-data'
import {useAuthStore} from "@/store/authStore";
// const API_URL = "https://testowe.zikor.pl/api/v1";
const API_URL = "https://testowe.zikor.pl/api/v1";
const API_URL = "http://10.0.2.2:8080/api/v1";
//const API_URL = "http://10.0.2.2:8080/api/v1";
export async function listNotices() {
const { token } = useAuthStore.getState();

View File

@@ -1,14 +1,16 @@
import axios from "axios";
import {useAuthStore} from "@/store/authStore";
// import FormData from 'form-data'
const API_URL = "https://testowe.zikor.pl/api/v1/wishlist";
export async function toggleNoticeStatus(noticeId) {
const { token } = useAuthStore.getState();
const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
try {
const response = await axios.post(`${API_URL}/toggle/${noticeId}`, null, {
headers: {
"Content-Type": "application/json",
},
const response = await axios.post(`${API_URL}/toggle/${noticeId}`, {}, {
headers
});
return response.data;
} catch (error) {
@@ -18,8 +20,11 @@ export async function toggleNoticeStatus(noticeId) {
}
export async function getWishlist() {
const { token } = useAuthStore.getState();
const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
try {
const response = await axios.get(`${API_URL}/`);
const response = await axios.get(`${API_URL}/`, {headers});
console.log("Wishlist response:", response.data);
return response.data;
} catch (error) {

View File

@@ -1,4 +1,105 @@
import { Link } from "expo-router";
import { Pressable } from "react-native";
import { Box } from "@/components/ui/box";
import { Text } from "@/components/ui/text";
export default function User() {
return <Text>Użytkownik</Text>;
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 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 <ActivityIndicator />;
}
if (!user) {
return <Text>Nie udało się pobrać danych użytkownika.</Text>;
}
return (
<VStack className="bg-gray-50 flex-1">
<Box className="bg-white pb-6 shadow-sm">
<Box className="items-center pt-6 mb-4">
<Image
source={{
uri:
user.profileImage ||
"https://th.bing.com/th/id/OIP.3coo_N8sieled8QNroQmkgHaHa?rs=1&pid=ImgDetMain",
}}
className="h-24 w-24 rounded-full border-4 border-white shadow-md"
alt="Zdjęcie profilowe"
/>
</Box>
<Text className="text-2xl font-bold text-center mb-1">
{user.firstName} {user.lastName}
</Text>
<Pressable
className="border border-[#002f34] rounded-md py-2 px-4 self-center"
onPress={() => console.log("Edytuj dane użytkownika")}
>
<Text className="text-[#002f34] font-medium">Edytuj profil</Text>
</Pressable>
</Box>
<Box className="bg-white mt-4 p-5 border-t border-b border-gray-200">
<Text className="font-bold text-lg mb-3">Moje dane</Text>
<HStack className="mb-3">
<Text className="text-gray-600 w-24">E-mail</Text>
<Text className="flex-1">{user.email || "brak danych"}</Text>
</HStack>
</Box>
<Box className="bg-white mt-4 p-5">
<Text className="font-bold text-lg mb-3">Moje konto</Text>
<Link href="/dashboard/userNotices" asChild>
<Pressable className="py-3 flex-row items-center border-b border-gray-100">
<Text className="text-lg flex-1">Moje ogłoszenia</Text>
<Text></Text>
</Pressable>
</Link>
{/*Tak dodałem, można zmienić na coś innego*/}
<Link href="/dashboard/userPaymentHistory" asChild>
<Pressable className="py-3 flex-row items-center border-b border-gray-100">
<Text className="text-lg flex-1">Historia płatności</Text>
<Text></Text>
</Pressable>
</Link>
<Pressable className="py-3 flex-row items-center">
<Text className="text-lg flex-1">Ustawienia powiadomień</Text>
<Text></Text>
</Pressable>
</Box>
<Pressable className="mt-8 mx-5 p-4 bg-white rounded-md items-center shadow-sm">
<Text className="text-red-500 font-medium">Wyloguj się</Text>
</Pressable>
</VStack>
);
}

View File

@@ -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 <Text>Użytkownik</Text>;
}
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 <ActivityIndicator />;
}
return (
<VStack className="p-4">
<Text className="text-2xl font-bold mb-4">Moje ogłoszenia</Text>
{userNotices.length > 0 ? (
<FlatList
data={userNotices}
numColumns={2}
columnWrapperStyle={{ marginBottom: 10, justifyContent: "space-between" }}
renderItem={({ item }) => (
<Box className="flex-1">
<NoticeCard notice={item} />
<Box className="flex-row justify-between mt-2">
<Button
title="Promuj"
onPress={() => {
// TODO: Implementacja promocji ogłoszenia
console.log(`Promuj ogłoszenie ${item.noticeId}`);
}}
className="bg-primary-500 py-2 px-4 rounded-md"
>
</Button>
<Button
title="Podbij"
onPress={() => {
// TODO: Implementacja podbicia ogłoszenia
console.log(`Podbij ogłoszenie ${item.noticeId}`);
}}
className="bg-primary-500 py-2 px-4 rounded-md"
>
</Button>
</Box>
</Box>
)}
keyExtractor={(item) => item.noticeId.toString()}
/>
) : (
<Text>Nie masz żadnych ogłoszeń.</Text>
)}
</VStack>
);
}

View File

@@ -8,10 +8,14 @@ import { FlatList } from 'react-native';
import { useAuthStore } from "@/store/authStore";
import { useRouter } from "expo-router";
import { useEffect, useState } from "react";
// import { SafeAreaView } from "react-native-safe-area-context";
import { SafeAreaView } from "react-native";
export default function Home() {
const token = useAuthStore((state) => state.token);
const router = useRouter();
const [isReady, setIsReady] = useState(false);
const fetchNotices = useNoticesStore((state) => state.fetchNotices);
useEffect(() => {
setIsReady(true);
@@ -23,23 +27,36 @@ const token = useAuthStore((state) => state.token);
}
}, [isReady, token, router]);
useEffect(() => {
if (token) {
fetchNotices();
}
}, [token, fetchNotices]);
const notices = useNoticesStore((state) => state.notices);
const latestNotices = [...notices]
// console.log("Notices:", notices);
const latestNotices = [...notices]
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate))
.slice(0, 6);
const recomendedNotices = [...notices]
.sort(() => Math.random() - 0.5)
.slice(0, 6);
return (
<View>
<SafeAreaView className="flex-1 m-2">
{/* <View> */}
<SearchSection/>
<ScrollView showsVerticalScrollIndicator={false} className='m-2'>
<ScrollView showsVerticalScrollIndicator={false} >
<CategorySection title="Polecane kategorie" notices={notices} />
<NoticeSection title="Najnowsze ogłoszenia" notices={latestNotices} ctaLink="/notices?sort=latest"/>
<UserSection title="Popularni sprzedawcy" notices={notices} />
<NoticeSection title="Proponowane ogłoszenia" notices={recomendedNotices} ctaLink="/notices"/>
</ScrollView>
</View>
{/* </View> */}
</SafeAreaView>
);
}

View File

@@ -2,21 +2,13 @@ import { Stack, Redirect } from "expo-router";
import "@/global.css";
import { GluestackUIProvider } from "@/components/ui/gluestack-ui-provider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useEffect, useState } from "react";
import { useNoticesStore } from "@/store/noticesStore";
const queryClient = new QueryClient();
export default function RootLayout() {
const fetchNotices = useNoticesStore((state) => state.fetchNotices);
useEffect(() => {
fetchNotices();
}, []);
return (
<QueryClientProvider client={queryClient}>
<GluestackUIProvider>
<Stack
screenOptions={{
headerTintColor: "#1c1c1e",

View File

@@ -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 <ActivityIndicator/>;
}
@@ -93,24 +154,60 @@ export default function NoticeDetails() {
<ActivityIndicator size="large" color="#3b82f6" />
</Box>
) : (
<Image
source={{
uri: image,
}}
className="h-auto w-full rounded-md aspect-[1/1]"
alt="image"
resizeMode="cover"
/>
<Box className="sticky top-0 z-10 bg-white">
<FlatList
ref={flatListRef}
data={images}
horizontal
snapToAlignment="center"
decelerationRate="fast"
showsHorizontalScrollIndicator={false}
pagingEnabled
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
renderItem={({ item, index }) => (
<View style={{ width: width }}>
<Image
source={{ uri: item }}
className="h-auto w-full rounded-md aspect-square"
alt={`Zdjęcie ${index + 1}`}
resizeMode="contain"
/>
</View>
)}
keyExtractor={(item, index) => index.toString()}
/>
{images.length > 1 && (
<Box className="flex-row justify-center mt-2">
{images.map((_, index) => (
<Box
key={index}
className={`w-2 h-2 rounded-full mx-1 ${index === currentIndex ? 'bg-primary-500' : 'bg-gray-300'}`}
/>
))}
</Box>
)}
</Box>
)}
<ScrollView
showsVerticalScrollIndicator={false}
>
<VStack className="p-2">
<Text className="text-sm font-normal mb-2 text-typography-700">
{formatDate(notice.publishDate)}
</Text>
<Text className="text-2xl text-gray-950 font-bold mb-2 text-center bg-gray-50 rounded-md p-2">
{notice.title}
</Text>
<Box className="flex-row items-center">
<Heading size="md" className="flex-1">
{notice.price}
<Box className="flex-row items-center bg-gray-50 rounded-md p-2">
<Heading size="md" className="flex-1 text-xl text-gray-950">
<Text className="text-sm text-typography-500">Cena: </Text>
{notice.price}
</Heading>
<Pressable
onPress={() => {
if (isInWishlist) {
@@ -127,7 +224,108 @@ export default function NoticeDetails() {
/>
</Pressable>
</Box>
<Box className="mt-4 bg-gray-50 p-3 rounded-lg shadow-sm">
<Text className="text-sm text-typography-500">
Kategoria: <Text className="font-bold text-gray-950">{notice.category}</Text>
</Text>
</Box>
<Box className="mt-4 bg-gray-50 p-3 rounded-lg shadow-sm">
<Text className="text-2xl text-gray-950">
Opis ogloszenia
</Text>
<Text className="text-sm text-typography-700">
{notice.description}
</Text>
</Box>
<Box className="mt-4 bg-gray-50 p-3 rounded-lg shadow-sm">
<Text className="text-sm text-typography-500">
Uzytkownik:
</Text>
{isUserLoading ? (
<ActivityIndicator />
) : user ? (
<>
<Box className="mr-4">
<Image
source={{ uri: user.profileImage || "https://th.bing.com/th/id/OIP.3coo_N8sieled8QNroQmkgHaHa?rs=1&pid=ImgDetMain" }} // Domyślny obraz, jeśli brak zdjęcia profilowego
className="h-12 w-12 rounded-full"
alt="Zdjęcie profilowe"
/>
</Box>
<Box className="flex-1">
<Text className="text-xl font-bold text-gray-950">
{user.firstName} {user.lastName}
</Text>
<Text className="text-sm text-typography-700">
Email: {user.email}
</Text>
<Pressable
onPress={() => setIsMessageFormVisible(true)}
className="mt-3 bg-primary-500 py-2 px-4 rounded-md"
>
<Text className="text-white text-center font-bold">Wyślij wiadomość</Text>
</Pressable>
<Link href={`/user/${notice.clientId}`}>
<Text className="text-xl p-3 font-bold text-center text-typography-700 mt-3">
Zobacz więcej ogłoszeń od {user.firstName}
</Text>
</Link>
</Box>
</>
) : (
<Text>Błąd podczas ładowania danych użytkownika</Text>
)}
</Box>
</VStack>
</ScrollView>
{isMessageFormVisible && (
<View className="absolute inset-0 bg-black bg-opacity-50 justify-center items-center z-20">
<View className="bg-white p-4 rounded-lg w-4/5">
<Text className="text-lg font-bold mb-4">Wyślij wiadomość do {user?.firstName}</Text>
<Text className="text-sm font-medium mb-1">Do:</Text>
<Text className="bg-gray-100 p-3 rounded text-gray-500">
{user?.email || "Brak adresu e-mail"}
</Text>
<Text className="text-sm font-medium mb-1">Twój e-mail:</Text>
<TextInput
className="border border-gray-300 p-2 rounded"
placeholder="Wpisz swój adres e-mail"
value={Email}
onChangeText={setEmail}
/>
<TextInput
className="border border-gray-300 rounded-md p-2 mb-4 h-32 text-left"
multiline
numberOfLines={4}
placeholder="Napisz swoją wiadomość..."
value={message}
onChangeText={setMessage}
/>
<View className="flex-row justify-end space-x-2">
<Pressable
onPress={() => setIsMessageFormVisible(false)}
className="bg-gray-300 py-2 px-4 rounded-md"
>
<Text className="text-gray-800">Anuluj</Text>
</Pressable>
<Pressable
onPress={handleSendMessage}
className="bg-primary-500 py-2 px-4 rounded-md">
<Text className="text-white">Wyślij</Text>
</Pressable>
</View>
</View>
</View>
)}
</Card>
);
}

View File

@@ -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 <ActivityIndicator />;
}
if (!user) {
return <Text>Nie znaleziono użytkownika</Text>;
}
const userNotices = notices.filter(notice => notice.clientId === Number(userId));
return (
<VStack className="p-4">
<Box className="flex-row items-center mb-4">
<Image
source={{ uri: user.profileImage || "https://th.bing.com/th/id/OIP.3coo_N8sieled8QNroQmkgHaHa?rs=1&pid=ImgDetMain" }}
className="h-16 w-16 rounded-full mr-4"
alt="Zdjęcie profilowe"
/>
<Heading size="lg">
{user.firstName} {user.lastName}
</Heading>
</Box>
{userNotices.length > 0 ? (
<FlatList
data={userNotices}
numColumns={2}
columnWrapperStyle={{ marginBottom: 10, justifyContent: "space-between" }}
renderItem={({ item }) => <NoticeCard notice={item} />}
keyExtractor={(item) => item.noticeId.toString()}
/>
) : (
<Text>Ten użytkownik nie ma żadnych ogłoszeń.</Text>
)}
</VStack>
);
}

View File

@@ -1,30 +1,46 @@
import { View, FlatList} from 'react-native';
import { useEffect, useState } from 'react'
import { useAuthStore } from "@/store/authStore";
import { Heading } from '@/components/ui/heading';
import { Text } from '@/components/ui/text';
import { Link } from 'expo-router';
import { Pressable } from '@/components/ui/pressable';
import axios from 'axios';
// import axios from 'axios';
import {listCategories} from "@/api/categories";
export function CategorySection({notices, title}) {
// const notices = useNoticesStore((state) => state.notices);
const token = useAuthStore((state) => state.token);
const [categoryMap, setCategoryMap] = useState({});
useEffect(() => {
axios.get('https://testowe.zikor.pl/api/v1/vars/categories')
.then(res => setCategoryMap(res.data))
.catch(() => setCategoryMap({}));
}, []);
const categories = Array.from(
new Set(notices.map((notice) => notice.category))
).filter(Boolean);
if(token){
const fetchCategories = async () => {
let data = await listCategories();
if (Array.isArray(data)) {
setCategoryMap(data);
}
}
fetchCategories();
}
}, [token]);
const categories = Array.from(
new Set(notices.map((notice) => notice.category))
).filter(Boolean);
const getCount = (category) =>
notices.filter((notice) => notice.category === category).length;
console.log("CategoryMap:", categoryMap);
if(!categoryMap) {
return null;
}
return (
<View className="mb-6">
<Heading className="text-2xl font-bold mb-4 mt-4">{title}</Heading>

View File

@@ -2,6 +2,7 @@ import { Input, InputField, InputIcon, InputSlot } from "@/components/ui/input"
import { SearchIcon } from "@/components/ui/icon"
import { Box } from "@/components/ui/box"
import { useRouter } from "expo-router";
import { View } from "react-native";
export function SearchSection({ searchQuery, setSearchQuery }) {
const router = useRouter();
@@ -14,18 +15,17 @@ export function SearchSection({ searchQuery, setSearchQuery }) {
});
};
return (
<Box className="m-2 bg-white">
<Input>
<Box className="mb-2 bg-white p-2 rounded-md">
<Input className="p-2">
<InputSlot>
<InputIcon as={SearchIcon} />
</InputSlot>
<InputField placeholder="Wyszukaj.."
<InputField placeholder="Wyszukaj..."
value={searchQuery}
onChangeText={setSearchQuery}
onSubmitEditing={handleSubmit}
returnKeyType="search" />
</Input>
</Box>
)
}

View File

@@ -4,16 +4,21 @@ import { Heading } from '@/components/ui/heading';
import { FlatList } from 'react-native';
import axios from 'axios';
import UserBlock from '@/components/UserBlock';
import {useAuthStore} from "@/store/authStore";
export function UserSection({notices, title}) {
const token = useAuthStore((state) => state.token);
const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get('https://testowe.zikor.pl/api/v1/clients/get/all')
if (token){
axios.get('https://testowe.zikor.pl/api/v1/clients/get/all', { headers })
.then(res => setUsers(res.data))
.catch(() => setUsers([]));
}, []);
}
}, [token]);
const usersWithNoticeCount = users.map(user => {
const count = notices.filter(n => n.clientId === user.id).length;

View File

@@ -3,7 +3,7 @@ import {createJSONStorage, persist} from "zustand/middleware";
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
const API_URL = "http://10.0.2.2:8080/api/v1";
const API_URL = "https://testowe.zikor.pl/api/v1";
export const useAuthStore = create(
persist(
@@ -71,8 +71,10 @@ export const useAuthStore = create(
signOut: async () => {
try {
const {token} = useAuthStore.getState();
const headers = token ? { 'Authorization': `Bearer ${token}` } : {};
// Можно отправить запрос на бэкенд для инвалидации токена
await axios.post(`${API_URL}/auth/logout`);
await axios.post(`${API_URL}/auth/logout`, {}, { headers });
} catch (error) {
console.error("Logout error:", error);
} finally {