fixes to app
This commit is contained in:
@@ -1,10 +1,16 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { useAuthStore } from "@/store/authStore";
|
||||||
|
|
||||||
const API_URL = "https://hopp.zikor.pl/api/v1";
|
const API_URL = "https://hopp.zikor.pl/api/v1";
|
||||||
|
|
||||||
export async function getUserById(userId) {
|
export async function getUserById(userId) {
|
||||||
|
const { token } = useAuthStore.getState();
|
||||||
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`${API_URL}/clients/get/${userId}`);
|
const response = await axios.get(
|
||||||
|
`${API_URL}/clients/get/${userId}`,
|
||||||
|
headers
|
||||||
|
);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ export async function listNotices() {
|
|||||||
headers: headers,
|
headers: headers,
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(response.toString());
|
throw new Error(response.toString());
|
||||||
}
|
}
|
||||||
|
// console.info("Notices fetched successfully:", data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +70,12 @@ export async function getImageByNoticeId(noticeId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllImagesByNoticeId(noticeId) {
|
export async function getAllImagesByNoticeId(noticeId) {
|
||||||
|
const { token } = useAuthStore.getState();
|
||||||
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
try {
|
try {
|
||||||
const listResponse = await axios.get(`${API_URL}/images/list/${noticeId}`);
|
const listResponse = await axios.get(`${API_URL}/images/list/${noticeId}`, {
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
if (listResponse.data && listResponse.data.length > 0) {
|
if (listResponse.data && listResponse.data.length > 0) {
|
||||||
return listResponse.data.map(
|
return listResponse.data.map(
|
||||||
@@ -127,3 +133,23 @@ export const uploadImage = async (noticeId, imageUri) => {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deleteNotice = async (noticeId) => {
|
||||||
|
const { token } = useAuthStore.getState();
|
||||||
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.delete(
|
||||||
|
`${API_URL}/notices/delete/${noticeId}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"Error deleting notice:",
|
||||||
|
error.response?.data,
|
||||||
|
error.response?.status
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,12 +1,67 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
|
|
||||||
const API_URL = "https://hopp.zikor.pl/api/v1";
|
import { useAuthStore } from "@/store/authStore";
|
||||||
export async function listOrders() {
|
|
||||||
const response = await fetch(`${API_URL}/orders/get/all`);
|
const API_URL = "https://hopp.zikor.pl/api/v1/orders";
|
||||||
const data = await response.json();
|
|
||||||
if (!response.ok) {
|
export async function createOrder(noticeId, orderType) {
|
||||||
throw new Error(response.toString());
|
const { token } = useAuthStore.getState();
|
||||||
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
|
const clientId = 1;
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${API_URL}/add`,
|
||||||
|
{ clientId, noticeId, orderType },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error", error.response?.data, error.response?.status);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPayment(orderId) {
|
||||||
|
const { token } = useAuthStore.getState();
|
||||||
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
|
const clientId = 1;
|
||||||
|
try {
|
||||||
|
const response = await axios.post(
|
||||||
|
`${API_URL}/token`,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error", error.response?.data, error.response?.status);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listOrders() {
|
||||||
|
const { token } = useAuthStore.getState();
|
||||||
|
const headers = token ? { Authorization: `Bearer ${token}` } : {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`${API_URL}/get/all`, { headers });
|
||||||
|
return response.data; // to będzie tablica OrderWithPaymentsDTO
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"Error fetching orders:",
|
||||||
|
error.response?.data,
|
||||||
|
error.response?.status
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export default function AccountDrawerLayout() {
|
|||||||
name="userNotices"
|
name="userNotices"
|
||||||
options={{ title: "Moje ogłoszenia" }}
|
options={{ title: "Moje ogłoszenia" }}
|
||||||
/>
|
/>
|
||||||
|
<Drawer.Screen name="userOrders" options={{ title: "Moje zamówienia" }} />
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ export default function Account() {
|
|||||||
return <Text>Nie udało się pobrać danych użytkownika.</Text>;
|
return <Text>Nie udało się pobrać danych użytkownika.</Text>;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(user);
|
|
||||||
return (
|
return (
|
||||||
<VStack className=" flex-1 m-2">
|
<VStack className=" flex-1 m-2">
|
||||||
<Box className="bg-white p-5 rounded-lg ">
|
<Box className="bg-white p-5 rounded-lg ">
|
||||||
|
|||||||
@@ -1,16 +1,46 @@
|
|||||||
import { useNoticesStore } from "@/store/noticesStore";
|
import { useNoticesStore } from "@/store/noticesStore";
|
||||||
import { NoticeCard } from "@/components/NoticeCard";
|
import { NoticeCard } from "@/components/NoticeCard";
|
||||||
import { Button } from "react-native";
|
import { Button, ButtonIcon, ButtonText } from "@/components/ui/button";
|
||||||
|
|
||||||
import { Box } from "@/components/ui/box";
|
import { Box } from "@/components/ui/box";
|
||||||
import { Text } from "@/components/ui/text";
|
import { Text } from "@/components/ui/text";
|
||||||
import { VStack } from "@/components/ui/vstack";
|
import { VStack } from "@/components/ui/vstack";
|
||||||
import { ActivityIndicator, FlatList } from "react-native";
|
import { ActivityIndicator, FlatList } from "react-native";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState, useRef } from "react";
|
||||||
|
import { createOrder, createPayment } from "@/api/order";
|
||||||
|
import { Linking } from "react-native";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import { useToast, Toast, ToastTitle } from "@/components/ui/toast";
|
||||||
|
import { AppState } from "react-native";
|
||||||
|
|
||||||
export default function UserNotices() {
|
export default function UserNotices() {
|
||||||
const { notices, fetchNotices } = useNoticesStore();
|
const { notices, fetchNotices, deleteNotice } = useNoticesStore();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const currentUserId = 1; // Tymczasowo, do czasu zaimplementowania logowania bo nie moge pobrac usera
|
const [isRedirecting, setIsRedirecting] = useState(false);
|
||||||
|
const currentUserId = 1;
|
||||||
|
const toast = useToast();
|
||||||
|
const appState = useRef(AppState.currentState);
|
||||||
|
const [toastId, setToastId] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isRedirecting) return;
|
||||||
|
const subscription = AppState.addEventListener("change", (state) => {
|
||||||
|
if (state === "active") {
|
||||||
|
setIsRedirecting(false);
|
||||||
|
const paymentStatus = "CORRECT";
|
||||||
|
if (paymentStatus == "INCORRECT") {
|
||||||
|
showNewToast("Płatność została anulowana.");
|
||||||
|
} else if (paymentStatus == "CORRECT") {
|
||||||
|
showNewToast("Płatność została zrealizowana.");
|
||||||
|
} else {
|
||||||
|
showNewToast("Płatność jeszcze nie wpłynęła.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appState.current = state;
|
||||||
|
});
|
||||||
|
return () => subscription.remove();
|
||||||
|
}, [isRedirecting, toast]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadNotices = async () => {
|
const loadNotices = async () => {
|
||||||
@@ -26,6 +56,58 @@ export default function UserNotices() {
|
|||||||
loadNotices();
|
loadNotices();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const showNewToast = (title) => {
|
||||||
|
const newId = Math.random();
|
||||||
|
setToastId(newId);
|
||||||
|
toast.show({
|
||||||
|
id: newId,
|
||||||
|
placement: "top",
|
||||||
|
duration: 3000,
|
||||||
|
render: ({ id }) => {
|
||||||
|
const uniqueToastId = "toast-" + id;
|
||||||
|
return (
|
||||||
|
<Toast nativeID={uniqueToastId} action="muted" variant="solid">
|
||||||
|
<ToastTitle>{title}</ToastTitle>
|
||||||
|
</Toast>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOrder = async (noticeId, type) => {
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const result = await createOrder(noticeId, type);
|
||||||
|
if (result) {
|
||||||
|
try {
|
||||||
|
const paymentResult = await createPayment(); //trzeba dodać orderId
|
||||||
|
if (paymentResult) {
|
||||||
|
setIsRedirecting(true);
|
||||||
|
Linking.openURL(paymentResult);
|
||||||
|
setWaitingForPayment(true);
|
||||||
|
} else {
|
||||||
|
console.log(`Nie udało się aktywować ogłoszenia ${noticeId}.`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Błąd podczas aktywacji ogłoszenia:", err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`Nie udało się aktywować ogłoszenia ${noticeId}.`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Błąd podczas aktywacji ogłoszenia:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteNotice = async (noticeId) => {
|
||||||
|
try {
|
||||||
|
await deleteNotice(noticeId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Błąd podczas usuwania ogłoszenia:", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const userNotices = notices
|
const userNotices = notices
|
||||||
.filter((notice) => notice.clientId === currentUserId)
|
.filter((notice) => notice.clientId === currentUserId)
|
||||||
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate));
|
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate));
|
||||||
@@ -36,45 +118,61 @@ export default function UserNotices() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack className="p-2">
|
<VStack className="p-2">
|
||||||
|
{isRedirecting && (
|
||||||
|
<Box className="absolute inset-0 bg-white bg-opacity-30 justify-center items-center z-50">
|
||||||
|
<Ionicons name="card-outline" size="30" className="pt-4" />
|
||||||
|
<Text className="text-lg font-bold pt-2">
|
||||||
|
Przekierowanie do płatności...
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
{/* <Text className="text-2xl font-bold mb-4">Moje ogłoszenia</Text> */}
|
{/* <Text className="text-2xl font-bold mb-4">Moje ogłoszenia</Text> */}
|
||||||
{userNotices.length > 0 ? (
|
{userNotices.length > 0 ? (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={userNotices}
|
data={userNotices}
|
||||||
// numColumns={1}
|
|
||||||
// columnWrapperStyle={{
|
|
||||||
// marginBottom: 10,
|
|
||||||
// justifyContent: "space-between",
|
|
||||||
// }}
|
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => (
|
||||||
<Box className="flex-1 mb-4 pb-2 bg-white rounded-lg">
|
<Box className="flex-1 mb-4 pb-2 bg-white rounded-lg">
|
||||||
<NoticeCard notice={item} />
|
<NoticeCard notice={item} />
|
||||||
<Box className="flex-row justify-between mt-2">
|
<Box className="flex-row justify-between mt-2">
|
||||||
{item.status === "ACTIVE" ? (
|
{item.status === "ACTIVE" ? (
|
||||||
<Button
|
<Button
|
||||||
title="Usuń"
|
className="ml-2"
|
||||||
onPress={() => {
|
onPress={() => handleDeleteNotice(item.noticeId)}
|
||||||
console.log(`Promuj ogłoszenie ${item.noticeId}`);
|
size="md"
|
||||||
}}
|
variant="outline"
|
||||||
className="bg-primary-500 py-2 px-4 rounded-md"
|
action="primary"
|
||||||
></Button>
|
>
|
||||||
|
<ButtonText>Usuń</ButtonText>
|
||||||
|
<Ionicons name="trash-outline" size={14} />
|
||||||
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
title="Aktywj"
|
className="ml-2"
|
||||||
onPress={() => {
|
size="md"
|
||||||
console.log(`Promuj ogłoszenie ${item.noticeId}`);
|
variant="solid"
|
||||||
}}
|
action="primary"
|
||||||
className="bg-primary-500 py-2 px-4 rounded-md"
|
onPress={() => handleOrder(item.noticeId, "ACTIVATE")}
|
||||||
></Button>
|
>
|
||||||
|
<ButtonText>Aktywuj</ButtonText>
|
||||||
|
<Ionicons
|
||||||
|
name="arrow-redo-outline"
|
||||||
|
size={14}
|
||||||
|
color="#fff"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{item.status === "ACTIVE" && (
|
||||||
|
<Button
|
||||||
|
className="mr-2"
|
||||||
|
size="md"
|
||||||
|
variant="solid"
|
||||||
|
action="primary"
|
||||||
|
onPress={() => handleOrder(item.noticeId, "BOOST")}
|
||||||
|
>
|
||||||
|
<ButtonText>Podbij</ButtonText>
|
||||||
|
<Ionicons name="arrow-up" size={14} color="#fff" />
|
||||||
|
</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>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
24
ArtisanConnect/app/(tabs)/dashboard/userOrders.jsx
Normal file
24
ArtisanConnect/app/(tabs)/dashboard/userOrders.jsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { View, Text } from "react-native";
|
||||||
|
import { useState, useEffect, use } from "react";
|
||||||
|
import { listOrders } from "@/api/order";
|
||||||
|
|
||||||
|
export default function UserOrders() {
|
||||||
|
const [orders, setOrders] = useState([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchOrders = async () => {
|
||||||
|
try {
|
||||||
|
const data = await listOrders();
|
||||||
|
setOrders(data);
|
||||||
|
} catch (err) {}
|
||||||
|
};
|
||||||
|
fetchOrders();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
console.log("Orders:", orders);
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
||||||
|
<Text>Orders</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import { CategorySection } from "@/components/CategorySection";
|
|||||||
import { NoticeSection } from "@/components/NoticeSection";
|
import { NoticeSection } from "@/components/NoticeSection";
|
||||||
import { UserSection } from "@/components/UserSection";
|
import { UserSection } from "@/components/UserSection";
|
||||||
import { SearchSection } from "@/components/SearchSection";
|
import { SearchSection } from "@/components/SearchSection";
|
||||||
import { FlatList } from "react-native";
|
|
||||||
import { useAuthStore } from "@/store/authStore";
|
import { useAuthStore } from "@/store/authStore";
|
||||||
import { useRouter } from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -36,7 +35,7 @@ export default function Home() {
|
|||||||
// console.log("Notices:", notices);
|
// console.log("Notices:", notices);
|
||||||
// console.log("Notices length:", notices.length);
|
// console.log("Notices length:", notices.length);
|
||||||
|
|
||||||
const activeNotices = notices.filter((notice) => notice.status === "ACTIVE");
|
const activeNotices = notices.filter((notice) => notice.status == "ACTIVE");
|
||||||
// console.log("Activer Notices:", activeNotices.length);
|
// console.log("Activer Notices:", activeNotices.length);
|
||||||
const latestNotices = [...activeNotices]
|
const latestNotices = [...activeNotices]
|
||||||
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate))
|
.sort((a, b) => new Date(b.publishDate) - new Date(a.publishDate))
|
||||||
|
|||||||
@@ -1,256 +1,263 @@
|
|||||||
import {useState, useEffect} from "react";
|
import { useState, useEffect } from "react";
|
||||||
import {Image, StyleSheet} from "react-native";
|
import { Image, StyleSheet } from "react-native";
|
||||||
import {Button, ButtonText} from "@/components/ui/button";
|
import { Button, ButtonText } from "@/components/ui/button";
|
||||||
import {FormControl} from "@/components/ui/form-control";
|
import { FormControl } from "@/components/ui/form-control";
|
||||||
import {Input, InputField} from "@/components/ui/input";
|
import { Input, InputField } from "@/components/ui/input";
|
||||||
import {Text} from "@/components/ui/text";
|
import { Text } from "@/components/ui/text";
|
||||||
import {VStack} from "@/components/ui/vstack";
|
import { VStack } from "@/components/ui/vstack";
|
||||||
import {Textarea, TextareaInput} from "@/components/ui/textarea";
|
import { Textarea, TextareaInput } from "@/components/ui/textarea";
|
||||||
import {ScrollView} from '@gluestack-ui/themed';
|
import { ScrollView } from "@gluestack-ui/themed";
|
||||||
import * as ImagePicker from 'expo-image-picker';
|
import * as ImagePicker from "expo-image-picker";
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectInput,
|
SelectInput,
|
||||||
SelectIcon,
|
SelectIcon,
|
||||||
SelectPortal,
|
SelectPortal,
|
||||||
SelectBackdrop,
|
SelectBackdrop,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectScrollView,
|
SelectScrollView,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
import {ChevronDownIcon} from "@/components/ui/icon";
|
import { ChevronDownIcon } from "@/components/ui/icon";
|
||||||
import {useNoticesStore} from "@/store/noticesStore";
|
import { useNoticesStore } from "@/store/noticesStore";
|
||||||
import {listCategories} from "@/api/categories";
|
import { listCategories } from "@/api/categories";
|
||||||
import {useRouter} from "expo-router";
|
import { useRouter } from "expo-router";
|
||||||
|
|
||||||
export default function CreateNotice() {
|
export default function CreateNotice() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {addNotice, fetchNotices} = useNoticesStore();
|
const { addNotice, fetchNotices } = useNoticesStore();
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
const [price, setPrice] = useState("");
|
const [price, setPrice] = useState("");
|
||||||
const [category, setCategory] = useState("");
|
const [category, setCategory] = useState("");
|
||||||
const [image, setImage] = useState([]);
|
const [image, setImage] = useState([]);
|
||||||
const [selectItems, setSelectItems] = useState([]);
|
const [selectItems, setSelectItems] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
|
|
||||||
const fetchSelectItems = async () => {
|
const fetchSelectItems = async () => {
|
||||||
try {
|
try {
|
||||||
let data = await listCategories();
|
let data = await listCategories();
|
||||||
if (isMounted && Array.isArray(data)) {
|
if (isMounted && Array.isArray(data)) {
|
||||||
setSelectItems(data);
|
setSelectItems(data);
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching select items:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchSelectItems();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [error, setError] = useState({
|
|
||||||
title: false,
|
|
||||||
description: false,
|
|
||||||
price: false,
|
|
||||||
category: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleAddNotice = async () => {
|
|
||||||
setError({
|
|
||||||
title: !title,
|
|
||||||
description: !description,
|
|
||||||
price: !price,
|
|
||||||
category: !category,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!title || !description || !price || !category) {
|
|
||||||
console.log("Error in form");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(true);
|
|
||||||
try {
|
|
||||||
const result = await addNotice({
|
|
||||||
title: title,
|
|
||||||
clientId: 1,
|
|
||||||
description: description,
|
|
||||||
price: price,
|
|
||||||
category: category,
|
|
||||||
status: "ACTIVE",
|
|
||||||
image: image
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
console.log("Notice created successfully with ID: ", result.noticeId);
|
|
||||||
await fetchNotices();
|
|
||||||
clearForm();
|
|
||||||
router.push("/(tabs)/notices");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating notice. Error message: ", error.message);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching select items:", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const takePicture = async () => {
|
fetchSelectItems();
|
||||||
const {status} = await ImagePicker.requestCameraPermissionsAsync();
|
|
||||||
if (status !== 'granted') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const result = await ImagePicker.launchCameraAsync({
|
|
||||||
allowsEditing: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.canceled && result.assets) {
|
return () => {
|
||||||
setImage(result.assets.map(asset => asset.uri));
|
isMounted = false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pickImage = async () => {
|
|
||||||
let result = await ImagePicker.launchImageLibraryAsync({
|
|
||||||
mediaTypes: 'images',
|
|
||||||
selectionLimit: 8,
|
|
||||||
allowsEditing: false,
|
|
||||||
allowsMultipleSelection: true,
|
|
||||||
aspect: [4, 3],
|
|
||||||
quality: 0.5,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.canceled) {
|
|
||||||
setImage(result.assets.map(asset => asset.uri));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const clearForm = () => {
|
const [error, setError] = useState({
|
||||||
setTitle("");
|
title: false,
|
||||||
setDescription("");
|
description: false,
|
||||||
setPrice("");
|
price: false,
|
||||||
setCategory("");
|
category: false,
|
||||||
setImage([]);
|
});
|
||||||
setError({
|
|
||||||
title: false,
|
const styles = StyleSheet.create({
|
||||||
description: false,
|
container: {
|
||||||
price: false,
|
flex: 1,
|
||||||
category: false,
|
alignItems: "center",
|
||||||
})
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAddNotice = async () => {
|
||||||
|
setError({
|
||||||
|
title: !title,
|
||||||
|
description: !description,
|
||||||
|
price: !price,
|
||||||
|
category: !category,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!title || !description || !price || !category) {
|
||||||
|
console.log("Error in form");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
setIsLoading(true);
|
||||||
<ScrollView h="$80" w="$80">
|
try {
|
||||||
<FormControl className="p-4 border rounded-lg border-outline-300">
|
const result = await addNotice({
|
||||||
<VStack space="xl">
|
title: title,
|
||||||
<VStack space="md">
|
clientId: 1,
|
||||||
<Text className="text-typography-500">Zdjęcia</Text>
|
description: description,
|
||||||
<Button onPress={pickImage}>
|
price: price,
|
||||||
<ButtonText>
|
category: category,
|
||||||
Wybierz zdjęcia
|
status: "INACTIVE",
|
||||||
</ButtonText>
|
image: image,
|
||||||
</Button>
|
});
|
||||||
<Button onPress={takePicture}>
|
|
||||||
<ButtonText>Zrób zdjęcie</ButtonText>
|
|
||||||
</Button>
|
|
||||||
<Text size="sm"
|
|
||||||
bold="true"
|
|
||||||
>
|
|
||||||
Pierwsze zdjęcie będzie zdjęciem głównym</Text>
|
|
||||||
{image && image.length > 0 && (
|
|
||||||
<VStack space="xs" className="flex-row flex-wrap">
|
|
||||||
{image.map((img, index) => (
|
|
||||||
<Image key={index} source={{uri: img}} style={styles.image} className="m-1"/>
|
|
||||||
))}
|
|
||||||
</VStack>
|
|
||||||
)}
|
|
||||||
</VStack>
|
|
||||||
|
|
||||||
<VStack space="xs">
|
if (result) {
|
||||||
<Text className="text-typography-500">Tytuł</Text>
|
console.log("Notice created successfully with ID: ", result.noticeId);
|
||||||
<Input className="min-w-[250px]" isInvalid={error.title}>
|
await fetchNotices();
|
||||||
<InputField
|
clearForm();
|
||||||
type="text"
|
|
||||||
value={title}
|
|
||||||
onChangeText={(value) => setTitle(value)}
|
|
||||||
/>
|
|
||||||
</Input>
|
|
||||||
</VStack>
|
|
||||||
|
|
||||||
<VStack space="xs">
|
router.push("/(tabs)/dashboard/userNotices");
|
||||||
<Text className="text-typography-500">Opis</Text>
|
}
|
||||||
<Textarea
|
} catch (error) {
|
||||||
size="md"
|
console.error("Error creating notice. Error message: ", error.message);
|
||||||
className="min-w-[250px] "
|
} finally {
|
||||||
isInvalid={error.description}
|
setIsLoading(false);
|
||||||
>
|
}
|
||||||
<TextareaInput
|
};
|
||||||
placeholder="Opisz produkt"
|
|
||||||
value={description}
|
|
||||||
onChangeText={(value) => setDescription(value)}
|
|
||||||
/>
|
|
||||||
</Textarea>
|
|
||||||
</VStack>
|
|
||||||
|
|
||||||
<VStack space="xs">
|
const takePicture = async () => {
|
||||||
<Text className="text-typography-500">Cena</Text>
|
const { status } = await ImagePicker.requestCameraPermissionsAsync();
|
||||||
<Input className="min-w-[250px]" isInvalid={error.price}>
|
if (status !== "granted") {
|
||||||
<InputField
|
return;
|
||||||
type="text"
|
}
|
||||||
value={price}
|
const result = await ImagePicker.launchCameraAsync({
|
||||||
onChangeText={(value) => setPrice(value)}
|
allowsEditing: false,
|
||||||
/>
|
});
|
||||||
</Input>
|
|
||||||
</VStack>
|
if (!result.canceled && result.assets) {
|
||||||
<VStack space="xs">
|
setImage(result.assets.map((asset) => asset.uri));
|
||||||
<Text className="text-typography-500">Kategoria</Text>
|
}
|
||||||
<Select
|
};
|
||||||
onValueChange={(value) => setCategory(value)}
|
|
||||||
isInvalid={error.category}
|
const pickImage = async () => {
|
||||||
>
|
let result = await ImagePicker.launchImageLibraryAsync({
|
||||||
<SelectTrigger variant="outline" size="md">
|
mediaTypes: "images",
|
||||||
<SelectInput placeholder="Wybierz kategorię"/>
|
selectionLimit: 8,
|
||||||
<SelectIcon className="mr-3" as={ChevronDownIcon}/>
|
allowsEditing: false,
|
||||||
</SelectTrigger>
|
allowsMultipleSelection: true,
|
||||||
<SelectPortal>
|
aspect: [4, 3],
|
||||||
<SelectBackdrop/>
|
quality: 0.5,
|
||||||
<SelectContent style={{maxHeight: 400}}>
|
});
|
||||||
<SelectScrollView>
|
|
||||||
{selectItems.map((item) => (
|
if (!result.canceled) {
|
||||||
<SelectItem key={item.value} label={item.label} value={item.value}/>
|
setImage(result.assets.map((asset) => asset.uri));
|
||||||
))}
|
}
|
||||||
</SelectScrollView>
|
};
|
||||||
</SelectContent>
|
|
||||||
</SelectPortal>
|
const clearForm = () => {
|
||||||
</Select>
|
setTitle("");
|
||||||
</VStack>
|
setDescription("");
|
||||||
<Button
|
setPrice("");
|
||||||
className="mt-5 w-full"
|
setCategory("");
|
||||||
onPress={handleAddNotice}
|
setImage([]);
|
||||||
disabled={isLoading}
|
setError({
|
||||||
>
|
title: false,
|
||||||
<ButtonText className="text-typography-0">Dodaj</ButtonText>
|
description: false,
|
||||||
</Button>
|
price: false,
|
||||||
</VStack>
|
category: false,
|
||||||
</FormControl>
|
});
|
||||||
</ScrollView>
|
};
|
||||||
);
|
|
||||||
}
|
return (
|
||||||
|
<ScrollView h="$80" w="$80">
|
||||||
|
<FormControl className="p-4 border rounded-lg border-outline-300">
|
||||||
|
<VStack space="xl">
|
||||||
|
<VStack space="md">
|
||||||
|
<Text className="text-typography-500">Zdjęcia</Text>
|
||||||
|
<Button onPress={pickImage}>
|
||||||
|
<ButtonText>Wybierz zdjęcia</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Button onPress={takePicture}>
|
||||||
|
<ButtonText>Zrób zdjęcie</ButtonText>
|
||||||
|
</Button>
|
||||||
|
<Text size="sm" bold="true">
|
||||||
|
Pierwsze zdjęcie będzie zdjęciem głównym
|
||||||
|
</Text>
|
||||||
|
{image && image.length > 0 && (
|
||||||
|
<VStack space="xs" className="flex-row flex-wrap">
|
||||||
|
{image.map((img, index) => (
|
||||||
|
<Image
|
||||||
|
key={index}
|
||||||
|
source={{ uri: img }}
|
||||||
|
style={styles.image}
|
||||||
|
className="m-1"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</VStack>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
<VStack space="xs">
|
||||||
|
<Text className="text-typography-500">Tytuł</Text>
|
||||||
|
<Input className="min-w-[250px]" isInvalid={error.title}>
|
||||||
|
<InputField
|
||||||
|
type="text"
|
||||||
|
value={title}
|
||||||
|
onChangeText={(value) => setTitle(value)}
|
||||||
|
/>
|
||||||
|
</Input>
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
<VStack space="xs">
|
||||||
|
<Text className="text-typography-500">Opis</Text>
|
||||||
|
<Textarea
|
||||||
|
size="md"
|
||||||
|
className="min-w-[250px] "
|
||||||
|
isInvalid={error.description}
|
||||||
|
>
|
||||||
|
<TextareaInput
|
||||||
|
placeholder="Opisz produkt"
|
||||||
|
value={description}
|
||||||
|
onChangeText={(value) => setDescription(value)}
|
||||||
|
/>
|
||||||
|
</Textarea>
|
||||||
|
</VStack>
|
||||||
|
|
||||||
|
<VStack space="xs">
|
||||||
|
<Text className="text-typography-500">Cena</Text>
|
||||||
|
<Input className="min-w-[250px]" isInvalid={error.price}>
|
||||||
|
<InputField
|
||||||
|
type="text"
|
||||||
|
value={price}
|
||||||
|
onChangeText={(value) => setPrice(value)}
|
||||||
|
/>
|
||||||
|
</Input>
|
||||||
|
</VStack>
|
||||||
|
<VStack space="xs">
|
||||||
|
<Text className="text-typography-500">Kategoria</Text>
|
||||||
|
<Select
|
||||||
|
onValueChange={(value) => setCategory(value)}
|
||||||
|
isInvalid={error.category}
|
||||||
|
>
|
||||||
|
<SelectTrigger variant="outline" size="md">
|
||||||
|
<SelectInput placeholder="Wybierz kategorię" />
|
||||||
|
<SelectIcon className="mr-3" as={ChevronDownIcon} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectPortal>
|
||||||
|
<SelectBackdrop />
|
||||||
|
<SelectContent style={{ maxHeight: 400 }}>
|
||||||
|
<SelectScrollView>
|
||||||
|
{selectItems.map((item) => (
|
||||||
|
<SelectItem
|
||||||
|
key={item.value}
|
||||||
|
label={item.label}
|
||||||
|
value={item.value}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SelectScrollView>
|
||||||
|
</SelectContent>
|
||||||
|
</SelectPortal>
|
||||||
|
</Select>
|
||||||
|
</VStack>
|
||||||
|
<Button
|
||||||
|
className="mt-5 w-full"
|
||||||
|
onPress={handleAddNotice}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
<ButtonText className="text-typography-0">Dodaj</ButtonText>
|
||||||
|
</Button>
|
||||||
|
</VStack>
|
||||||
|
</FormControl>
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
240
ArtisanConnect/components/ui/toast/index.tsx
Normal file
240
ArtisanConnect/components/ui/toast/index.tsx
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import { createToastHook } from '@gluestack-ui/toast';
|
||||||
|
import { AccessibilityInfo, Text, View, ViewStyle } from 'react-native';
|
||||||
|
import { tva } from '@gluestack-ui/nativewind-utils/tva';
|
||||||
|
import { cssInterop } from 'nativewind';
|
||||||
|
import {
|
||||||
|
Motion,
|
||||||
|
AnimatePresence,
|
||||||
|
MotionComponentProps,
|
||||||
|
} from '@legendapp/motion';
|
||||||
|
import {
|
||||||
|
withStyleContext,
|
||||||
|
useStyleContext,
|
||||||
|
} from '@gluestack-ui/nativewind-utils/withStyleContext';
|
||||||
|
import type { VariantProps } from '@gluestack-ui/nativewind-utils';
|
||||||
|
|
||||||
|
type IMotionViewProps = React.ComponentProps<typeof View> &
|
||||||
|
MotionComponentProps<typeof View, ViewStyle, unknown, unknown, unknown>;
|
||||||
|
|
||||||
|
const MotionView = Motion.View as React.ComponentType<IMotionViewProps>;
|
||||||
|
|
||||||
|
const useToast = createToastHook(MotionView, AnimatePresence);
|
||||||
|
const SCOPE = 'TOAST';
|
||||||
|
|
||||||
|
cssInterop(MotionView, { className: 'style' });
|
||||||
|
|
||||||
|
const toastStyle = tva({
|
||||||
|
base: 'p-4 m-1 rounded-md gap-1 web:pointer-events-auto shadow-hard-5 border-outline-100',
|
||||||
|
variants: {
|
||||||
|
action: {
|
||||||
|
error: 'bg-error-800',
|
||||||
|
warning: 'bg-warning-700',
|
||||||
|
success: 'bg-success-700',
|
||||||
|
info: 'bg-info-700',
|
||||||
|
muted: 'bg-background-800',
|
||||||
|
},
|
||||||
|
|
||||||
|
variant: {
|
||||||
|
solid: '',
|
||||||
|
outline: 'border bg-background-0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const toastTitleStyle = tva({
|
||||||
|
base: 'text-typography-0 font-medium font-body tracking-md text-left',
|
||||||
|
variants: {
|
||||||
|
isTruncated: {
|
||||||
|
true: '',
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
true: 'font-bold',
|
||||||
|
},
|
||||||
|
underline: {
|
||||||
|
true: 'underline',
|
||||||
|
},
|
||||||
|
strikeThrough: {
|
||||||
|
true: 'line-through',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
'2xs': 'text-2xs',
|
||||||
|
'xs': 'text-xs',
|
||||||
|
'sm': 'text-sm',
|
||||||
|
'md': 'text-base',
|
||||||
|
'lg': 'text-lg',
|
||||||
|
'xl': 'text-xl',
|
||||||
|
'2xl': 'text-2xl',
|
||||||
|
'3xl': 'text-3xl',
|
||||||
|
'4xl': 'text-4xl',
|
||||||
|
'5xl': 'text-5xl',
|
||||||
|
'6xl': 'text-6xl',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parentVariants: {
|
||||||
|
variant: {
|
||||||
|
solid: '',
|
||||||
|
outline: '',
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
error: '',
|
||||||
|
warning: '',
|
||||||
|
success: '',
|
||||||
|
info: '',
|
||||||
|
muted: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parentCompoundVariants: [
|
||||||
|
{
|
||||||
|
variant: 'outline',
|
||||||
|
action: 'error',
|
||||||
|
class: 'text-error-800',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant: 'outline',
|
||||||
|
action: 'warning',
|
||||||
|
class: 'text-warning-800',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant: 'outline',
|
||||||
|
action: 'success',
|
||||||
|
class: 'text-success-800',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant: 'outline',
|
||||||
|
action: 'info',
|
||||||
|
class: 'text-info-800',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variant: 'outline',
|
||||||
|
action: 'muted',
|
||||||
|
class: 'text-background-800',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const toastDescriptionStyle = tva({
|
||||||
|
base: 'font-normal font-body tracking-md text-left',
|
||||||
|
variants: {
|
||||||
|
isTruncated: {
|
||||||
|
true: '',
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
true: 'font-bold',
|
||||||
|
},
|
||||||
|
underline: {
|
||||||
|
true: 'underline',
|
||||||
|
},
|
||||||
|
strikeThrough: {
|
||||||
|
true: 'line-through',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
'2xs': 'text-2xs',
|
||||||
|
'xs': 'text-xs',
|
||||||
|
'sm': 'text-sm',
|
||||||
|
'md': 'text-base',
|
||||||
|
'lg': 'text-lg',
|
||||||
|
'xl': 'text-xl',
|
||||||
|
'2xl': 'text-2xl',
|
||||||
|
'3xl': 'text-3xl',
|
||||||
|
'4xl': 'text-4xl',
|
||||||
|
'5xl': 'text-5xl',
|
||||||
|
'6xl': 'text-6xl',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parentVariants: {
|
||||||
|
variant: {
|
||||||
|
solid: 'text-typography-50',
|
||||||
|
outline: 'text-typography-900',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const Root = withStyleContext(View, SCOPE);
|
||||||
|
type IToastProps = React.ComponentProps<typeof Root> & {
|
||||||
|
className?: string;
|
||||||
|
} & VariantProps<typeof toastStyle>;
|
||||||
|
|
||||||
|
const Toast = React.forwardRef<React.ComponentRef<typeof Root>, IToastProps>(
|
||||||
|
function Toast(
|
||||||
|
{ className, variant = 'solid', action = 'muted', ...props },
|
||||||
|
ref
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Root
|
||||||
|
ref={ref}
|
||||||
|
className={toastStyle({ variant, action, class: className })}
|
||||||
|
context={{ variant, action }}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
type IToastTitleProps = React.ComponentProps<typeof Text> & {
|
||||||
|
className?: string;
|
||||||
|
} & VariantProps<typeof toastTitleStyle>;
|
||||||
|
|
||||||
|
const ToastTitle = React.forwardRef<
|
||||||
|
React.ComponentRef<typeof Text>,
|
||||||
|
IToastTitleProps
|
||||||
|
>(function ToastTitle({ className, size = 'md', children, ...props }, ref) {
|
||||||
|
const { variant: parentVariant, action: parentAction } =
|
||||||
|
useStyleContext(SCOPE);
|
||||||
|
React.useEffect(() => {
|
||||||
|
// Issue from react-native side
|
||||||
|
// Hack for now, will fix this later
|
||||||
|
AccessibilityInfo.announceForAccessibility(children as string);
|
||||||
|
}, [children]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
aria-live="assertive"
|
||||||
|
aria-atomic="true"
|
||||||
|
role="alert"
|
||||||
|
className={toastTitleStyle({
|
||||||
|
size,
|
||||||
|
class: className,
|
||||||
|
parentVariants: {
|
||||||
|
variant: parentVariant,
|
||||||
|
action: parentAction,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
type IToastDescriptionProps = React.ComponentProps<typeof Text> & {
|
||||||
|
className?: string;
|
||||||
|
} & VariantProps<typeof toastDescriptionStyle>;
|
||||||
|
|
||||||
|
const ToastDescription = React.forwardRef<
|
||||||
|
React.ComponentRef<typeof Text>,
|
||||||
|
IToastDescriptionProps
|
||||||
|
>(function ToastDescription({ className, size = 'md', ...props }, ref) {
|
||||||
|
const { variant: parentVariant } = useStyleContext(SCOPE);
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
className={toastDescriptionStyle({
|
||||||
|
size,
|
||||||
|
class: className,
|
||||||
|
parentVariants: {
|
||||||
|
variant: parentVariant,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Toast.displayName = 'Toast';
|
||||||
|
ToastTitle.displayName = 'ToastTitle';
|
||||||
|
ToastDescription.displayName = 'ToastDescription';
|
||||||
|
|
||||||
|
export { useToast, Toast, ToastTitle, ToastDescription };
|
||||||
719
ArtisanConnect/package-lock.json
generated
719
ArtisanConnect/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,7 @@
|
|||||||
"expo-auth-session": "~6.2.0",
|
"expo-auth-session": "~6.2.0",
|
||||||
"expo-camera": "~16.1.7",
|
"expo-camera": "~16.1.7",
|
||||||
"expo-constants": "~17.1.5",
|
"expo-constants": "~17.1.5",
|
||||||
|
"expo-crypto": "~14.1.4",
|
||||||
"expo-image-picker": "~16.1.4",
|
"expo-image-picker": "~16.1.4",
|
||||||
"expo-linking": "~7.1.4",
|
"expo-linking": "~7.1.4",
|
||||||
"expo-router": "~5.0.5",
|
"expo-router": "~5.0.5",
|
||||||
@@ -61,8 +62,7 @@
|
|||||||
"react-native-svg": "15.11.2",
|
"react-native-svg": "15.11.2",
|
||||||
"react-native-web": "~0.20.0",
|
"react-native-web": "~0.20.0",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"zustand": "^5.0.3",
|
"zustand": "^5.0.3"
|
||||||
"expo-crypto": "~14.1.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
|
|||||||
@@ -1,41 +1,54 @@
|
|||||||
import {create} from "zustand";
|
import { create } from "zustand";
|
||||||
import * as api from "@/api/notices";
|
import * as api from "@/api/notices";
|
||||||
|
|
||||||
export const useNoticesStore = create((set, get) => ({
|
export const useNoticesStore = create((set, get) => ({
|
||||||
notices: [],
|
notices: [],
|
||||||
fetchNotices: async () => {
|
fetchNotices: async () => {
|
||||||
set({error: null});
|
set({ error: null });
|
||||||
try {
|
try {
|
||||||
const data = await api.listNotices();
|
const data = await api.listNotices();
|
||||||
set({notices: data});
|
set({ notices: data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
set(error);
|
set(error);
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
addNotice: async (notice) => {
|
|
||||||
try {
|
|
||||||
const newNotice = await api.createNotice(notice);
|
|
||||||
set((state) => ({
|
|
||||||
notices: [...state.notices, newNotice],
|
|
||||||
}));
|
|
||||||
return newNotice;
|
|
||||||
} catch (error) {
|
|
||||||
set({ error });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getNoticeById: (noticeId) => {
|
|
||||||
return get().notices.find((notice) => String(notice.noticeId) === String(noticeId));
|
|
||||||
},
|
|
||||||
|
|
||||||
getAllImagesByNoticeId: async (noticeId) => {
|
|
||||||
try {
|
|
||||||
return await api.getAllImagesByNoticeId(noticeId);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error while getting images:", error);
|
|
||||||
return ["https://http.cat/404.jpg"];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}));
|
},
|
||||||
|
|
||||||
|
addNotice: async (notice) => {
|
||||||
|
try {
|
||||||
|
const newNotice = await api.createNotice(notice);
|
||||||
|
set((state) => ({
|
||||||
|
notices: [...state.notices, newNotice],
|
||||||
|
}));
|
||||||
|
return newNotice;
|
||||||
|
} catch (error) {
|
||||||
|
set({ error });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getNoticeById: (noticeId) => {
|
||||||
|
return get().notices.find(
|
||||||
|
(notice) => String(notice.noticeId) === String(noticeId)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllImagesByNoticeId: async (noticeId) => {
|
||||||
|
try {
|
||||||
|
return await api.getAllImagesByNoticeId(noticeId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error while getting images:", error);
|
||||||
|
return ["https://http.cat/404.jpg"];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteNotice: async (noticeId) => {
|
||||||
|
try {
|
||||||
|
await api.deleteNotice(noticeId);
|
||||||
|
set((state) => ({
|
||||||
|
notices: state.notices.filter((notice) => notice.noticeId !== noticeId),
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting notice:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user