diff --git a/ArtisanConnect/app/(tabs)/notices.jsx b/ArtisanConnect/app/(tabs)/notices.jsx
index 957fdf9..f1f08ab 100644
--- a/ArtisanConnect/app/(tabs)/notices.jsx
+++ b/ArtisanConnect/app/(tabs)/notices.jsx
@@ -1,38 +1,105 @@
-// import React from "react"
-import {FlatList, Text, ActivityIndicator, RefreshControl} from "react-native";
-import {useState, useEffect} from "react";
+import { FlatList, Text, ActivityIndicator, RefreshControl, Dimensions } from "react-native";
+import { useState, useEffect } from "react";
import { Ionicons } from "@expo/vector-icons";
-import {useNoticesStore} from "@/store/noticesStore";
-import {NoticeCard} from "@/components/NoticeCard";
-import { useLocalSearchParams } from "expo-router";
-import { HStack } from "@/components/ui/hstack"
-import { Box } from "@/components/ui/box"
-import { Button, ButtonText, ButtonIcon } from "@/components/ui/button";
+import { useNoticesStore } from "@/store/noticesStore";
+import { NoticeCard } from "@/components/NoticeCard";
+import { useLocalSearchParams, useRouter } from "expo-router";
+import { Box } from "@/components/ui/box";
+import { Button, ButtonText } from "@/components/ui/button";
+import { ChevronDownIcon } from "@/components/ui/icon";
+import { listCategories } from "@/api/categories";
+import { FormControl, FormControlLabel } from "@/components/ui/form-control";
+import { Input, InputField } from "@/components/ui/input";
+import { HStack } from "@/components/ui/hstack";
+
import {
- Actionsheet,
- ActionsheetContent,
- ActionsheetItem,
- ActionsheetItemText,
- ActionsheetDragIndicator,
- ActionsheetDragIndicatorWrapper,
- ActionsheetBackdrop,
-} from "@/components/ui/actionsheet"
-import { useRouter } from "expo-router";
+ Actionsheet,
+ ActionsheetContent,
+ ActionsheetDragIndicator,
+ ActionsheetDragIndicatorWrapper,
+ ActionsheetBackdrop,
+} from "@/components/ui/actionsheet";
+import {
+ Select,
+ SelectTrigger,
+ SelectInput,
+ SelectIcon,
+ SelectPortal,
+ SelectBackdrop,
+ SelectContent,
+ SelectDragIndicator,
+ SelectDragIndicatorWrapper,
+ SelectItem,
+} from "@/components/ui/select";
export default function Notices() {
- const {notices, fetchNotices} = useNoticesStore();
+ // Hooks
+ const { notices, fetchNotices } = useNoticesStore();
const [refreshing, setRefreshing] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
- const [showActionsheet, setShowActionsheet] = useState(false)
- const handleClose = () => setShowActionsheet(false)
-
+ const [showActionsheet, setShowActionsheet] = useState(false);
+ const [categories, setCategories] = useState([]);
+ const [filteredNotices, setFilteredNotices] = useState([]);
const params = useLocalSearchParams();
- console.log("GET params:", params);
+ const router = useRouter();
+
+ useEffect(() => {
+ const fetchSelectItems = async () => {
+ try {
+ const data = await listCategories();
+ if (Array.isArray(data)) {
+ setCategories(data);
+ } else {
+ console.error('listCategories did not return an array:', data);
+ setError(new Error('Invalid categories data'));
+ }
+ } catch (error) {
+ console.error('Error fetching select items:', error);
+ setError(error);
+ }
+ };
+ fetchSelectItems();
+ }, []);
+
useEffect(() => {
loadData();
}, []);
+ useEffect(() => {
+ let result = notices;
+
+ if (params.category) {
+ result = result.filter(notice => notice.category === params.category);
+ }
+
+ if (params.sort === "latest") {
+ result = [...result].sort(
+ (a, b) => new Date(b.publishDate) - new Date(a.publishDate)
+ );
+ }
+
+ if(params.priceFrom) {
+ result = result.filter(notice => {
+ const price = parseFloat(notice.price);
+ const priceFrom = parseFloat(params.priceFrom);
+ return !isNaN(price) && price >= priceFrom;
+ });
+ }
+
+ if (params.priceTo) {
+ result = result.filter(notice => {
+ const price = parseFloat(notice.price);
+ const priceTo = parseFloat(params.priceTo);
+ return !isNaN(price) && price <= priceTo;
+ });
+ }
+
+ setFilteredNotices(result);
+ }, [notices, params.category, params.sort, params.priceFrom, params.priceTo]);
+
+
+ let filterActive = !!params.category || params.sort === "latest";
const loadData = async () => {
@@ -47,29 +114,29 @@ export default function Notices() {
}
};
- const router = useRouter();
+ const handleCategorySelect = (value) => {
+ router.replace({
+ pathname: "/notices",
+ params: { ...params, category: value }
+ });
+ };
- let filteredNotices = notices;
- let filterActive = false;
- if (params.sort) {
- if( params.sort === "latest") {
- filteredNotices = [...filteredNotices].sort(
- (a, b) => new Date(b.publishDate) - new Date(a.publishDate)
- );
- }
+ const handlePriceFrom = (value) => {
+ router.replace({
+ pathname: "/notices",
+ params: { ...params, priceFrom: value }
+ });
}
- if(params.category) {
- filteredNotices = filteredNotices.filter(
- (notice) => notice.category === params.category
- );
- filterActive = true;
+ const handlePriceTo = (value) => {
+ router.replace({
+ pathname: "/notices",
+ params: { ...params, priceTo: value }
+ });
}
- if(params.attribute) {
- filterActive = true;
- }
-
+ const handleClose = () => setShowActionsheet(false);
+
const onRefresh = async () => {
setRefreshing(true);
try {
@@ -82,52 +149,118 @@ export default function Notices() {
};
if (isLoading && !refreshing) {
- return ;
+ return ;
}
if (error) {
- return Nie udało sie pobrać listy. {error.message};
+ return Nie udało się pobrać listy. {error.message};
}
+ const SCREEN_HEIGHT = Dimensions.get('window').height;
+
+ const selectedCategory = params.category && categories?.find(
+ (cat) => cat.value === params.category
+ ) || null;
+
return (
-<>
-
-
- {filterActive && (
- )}
-
-
-
-
-
-
-
-
- Kategoria
-
-
-
- }
- refreshControl={
-
- }
- />
+ <>
+
+
+ {filterActive && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+ refreshControl={
+
+ }
+ />
>
);
}
\ No newline at end of file
diff --git a/ArtisanConnect/components/FilterSection.jsx b/ArtisanConnect/components/FilterSection.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/ArtisanConnect/components/ui/slider/index.tsx b/ArtisanConnect/components/ui/slider/index.tsx
new file mode 100644
index 0000000..cd67c1f
--- /dev/null
+++ b/ArtisanConnect/components/ui/slider/index.tsx
@@ -0,0 +1,264 @@
+'use client';
+import { createSlider } from '@gluestack-ui/slider';
+import { Pressable } from 'react-native';
+import { View } from 'react-native';
+import React from 'react';
+import { tva } from '@gluestack-ui/nativewind-utils/tva';
+import {
+ withStyleContext,
+ useStyleContext,
+} from '@gluestack-ui/nativewind-utils/withStyleContext';
+import type { VariantProps } from '@gluestack-ui/nativewind-utils';
+import { cssInterop } from 'nativewind';
+
+const SCOPE = 'SLIDER';
+const Root = withStyleContext(View, SCOPE);
+export const UISlider = createSlider({
+ Root: Root,
+ Thumb: View,
+ Track: Pressable,
+ FilledTrack: View,
+ ThumbInteraction: View,
+});
+
+cssInterop(UISlider.Track, { className: 'style' });
+
+const sliderStyle = tva({
+ base: 'justify-center items-center data-[disabled=true]:opacity-40 data-[disabled=true]:web:pointer-events-none',
+ variants: {
+ orientation: {
+ horizontal: 'w-full',
+ vertical: 'h-full',
+ },
+ size: {
+ sm: '',
+ md: '',
+ lg: '',
+ },
+ isReversed: {
+ true: '',
+ false: '',
+ },
+ },
+});
+
+const sliderThumbStyle = tva({
+ base: 'bg-primary-500 absolute rounded-full data-[focus=true]:bg-primary-600 data-[active=true]:bg-primary-600 data-[hover=true]:bg-primary-600 data-[disabled=true]:bg-primary-500 web:cursor-pointer web:data-[active=true]:outline web:data-[active=true]:outline-4 web:data-[active=true]:outline-primary-400 shadow-hard-1',
+
+ parentVariants: {
+ size: {
+ sm: 'h-4 w-4',
+ md: 'h-5 w-5',
+ lg: 'h-6 w-6',
+ },
+ },
+});
+
+const sliderTrackStyle = tva({
+ base: 'bg-background-300 rounded-lg overflow-hidden',
+ parentVariants: {
+ orientation: {
+ horizontal: 'w-full',
+ vertical: 'h-full',
+ },
+ isReversed: {
+ true: '',
+ false: '',
+ },
+ size: {
+ sm: '',
+ md: '',
+ lg: '',
+ },
+ },
+ parentCompoundVariants: [
+ {
+ orientation: 'horizontal',
+ size: 'sm',
+ class: 'h-1 flex-row',
+ },
+ {
+ orientation: 'horizontal',
+ size: 'sm',
+ isReversed: true,
+ class: 'h-1 flex-row-reverse',
+ },
+ {
+ orientation: 'horizontal',
+ size: 'md',
+ class: 'h-1 flex-row',
+ },
+ {
+ orientation: 'horizontal',
+ size: 'md',
+ isReversed: true,
+ class: 'h-[5px] flex-row-reverse',
+ },
+ {
+ orientation: 'horizontal',
+ size: 'lg',
+ class: 'h-1.5 flex-row',
+ },
+ {
+ orientation: 'horizontal',
+ size: 'lg',
+ isReversed: true,
+ class: 'h-1.5 flex-row-reverse',
+ },
+ {
+ orientation: 'vertical',
+ size: 'sm',
+ class: 'w-1 flex-col-reverse',
+ },
+ {
+ orientation: 'vertical',
+ size: 'sm',
+ isReversed: true,
+ class: 'w-1 flex-col',
+ },
+ {
+ orientation: 'vertical',
+ size: 'md',
+ class: 'w-[5px] flex-col-reverse',
+ },
+ {
+ orientation: 'vertical',
+ size: 'md',
+ isReversed: true,
+ class: 'w-[5px] flex-col',
+ },
+ {
+ orientation: 'vertical',
+ size: 'lg',
+ class: 'w-1.5 flex-col-reverse',
+ },
+ {
+ orientation: 'vertical',
+ size: 'lg',
+ isReversed: true,
+ class: 'w-1.5 flex-col',
+ },
+ ],
+});
+
+const sliderFilledTrackStyle = tva({
+ base: 'bg-primary-500 data-[focus=true]:bg-primary-600 data-[active=true]:bg-primary-600 data-[hover=true]:bg-primary-600',
+ parentVariants: {
+ orientation: {
+ horizontal: 'h-full',
+ vertical: 'w-full',
+ },
+ },
+});
+
+type ISliderProps = React.ComponentProps &
+ VariantProps;
+
+const Slider = React.forwardRef<
+ React.ComponentRef,
+ ISliderProps
+>(function Slider(
+ {
+ className,
+ size = 'md',
+ orientation = 'horizontal',
+ isReversed = false,
+ ...props
+ },
+ ref
+) {
+ return (
+
+ );
+});
+
+type ISliderThumbProps = React.ComponentProps &
+ VariantProps;
+
+const SliderThumb = React.forwardRef<
+ React.ComponentRef,
+ ISliderThumbProps
+>(function SliderThumb({ className, size, ...props }, ref) {
+ const { size: parentSize } = useStyleContext(SCOPE);
+
+ return (
+
+ );
+});
+
+type ISliderTrackProps = React.ComponentProps &
+ VariantProps;
+
+const SliderTrack = React.forwardRef<
+ React.ComponentRef,
+ ISliderTrackProps
+>(function SliderTrack({ className, ...props }, ref) {
+ const {
+ orientation: parentOrientation,
+ size: parentSize,
+ isReversed,
+ } = useStyleContext(SCOPE);
+
+ return (
+
+ );
+});
+
+type ISliderFilledTrackProps = React.ComponentProps<
+ typeof UISlider.FilledTrack
+> &
+ VariantProps;
+
+const SliderFilledTrack = React.forwardRef<
+ React.ComponentRef,
+ ISliderFilledTrackProps
+>(function SliderFilledTrack({ className, ...props }, ref) {
+ const { orientation: parentOrientation } = useStyleContext(SCOPE);
+
+ return (
+
+ );
+});
+
+export { Slider, SliderThumb, SliderTrack, SliderFilledTrack };
diff --git a/ArtisanConnect/package-lock.json b/ArtisanConnect/package-lock.json
index 6f0e69f..8f6d65e 100644
--- a/ArtisanConnect/package-lock.json
+++ b/ArtisanConnect/package-lock.json
@@ -23,6 +23,7 @@
"@gluestack-ui/overlay": "^0.1.22",
"@gluestack-ui/pressable": "^0.1.23",
"@gluestack-ui/select": "^0.1.31",
+ "@gluestack-ui/slider": "^0.1.32",
"@gluestack-ui/textarea": "^0.1.25",
"@gluestack-ui/themed": "^1.1.73",
"@gluestack-ui/toast": "^1.0.9",
@@ -2329,6 +2330,8 @@
},
"node_modules/@gluestack-ui/slider": {
"version": "0.1.32",
+ "resolved": "https://registry.npmjs.org/@gluestack-ui/slider/-/slider-0.1.32.tgz",
+ "integrity": "sha512-g0e7dAGOYYARlL3cdHe3mhN71j85TnqUgK/xOYWjVDE0U+atIXxxTVEXeO0ZPGJ3YUOUUAInIVGaa0xvnjEkYg==",
"dependencies": {
"@gluestack-ui/form-control": "^0.1.19",
"@gluestack-ui/hooks": "0.1.13",
diff --git a/ArtisanConnect/package.json b/ArtisanConnect/package.json
index 29070cb..c8f9b57 100644
--- a/ArtisanConnect/package.json
+++ b/ArtisanConnect/package.json
@@ -24,6 +24,7 @@
"@gluestack-ui/overlay": "^0.1.22",
"@gluestack-ui/pressable": "^0.1.23",
"@gluestack-ui/select": "^0.1.31",
+ "@gluestack-ui/slider": "^0.1.32",
"@gluestack-ui/textarea": "^0.1.25",
"@gluestack-ui/themed": "^1.1.73",
"@gluestack-ui/toast": "^1.0.9",