Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
242 changes: 242 additions & 0 deletions app/markets/components/MarketSettingsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import React from 'react';
import {
Modal,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
Button,
Switch,
Input,
Divider,
} from '@nextui-org/react';

type MarketSettingsModalProps = {
isOpen: boolean;
onOpenChange: () => void;
// Unknown Filters
includeUnknownTokens: boolean;
setIncludeUnknownTokens: (value: boolean) => void;
showUnknownOracle: boolean;
setShowUnknownOracle: (value: boolean) => void;
// USD Filters (Simplified)
usdFilters: {
minSupply: string;
minBorrow: string;
};
setUsdFilters: (filters: MarketSettingsModalProps['usdFilters']) => void;
// Pagination
entriesPerPage: number;
onEntriesPerPageChange: (value: number) => void;
};

// Reusable component for consistent setting layout
function SettingItem({
title,
description,
children,
}: {
title: string;
description: string;
children: React.ReactNode;
}) {
return (
<div className="flex items-start justify-between gap-4">
<div className="flex flex-grow flex-col gap-1 pr-2">
<h4 className="text-base font-medium text-primary">{title}</h4>
<p className="text-xs text-secondary">{description}</p>
</div>
<div className="flex-shrink-0 pt-1">
{' '}
{/* Align control slightly lower */}
{children}
</div>
</div>
);
}

export default function MarketSettingsModal({
isOpen,
onOpenChange,
includeUnknownTokens,
setIncludeUnknownTokens,
showUnknownOracle,
setShowUnknownOracle,
usdFilters,
setUsdFilters,
entriesPerPage,
onEntriesPerPageChange,
}: MarketSettingsModalProps) {
const [customEntries, setCustomEntries] = React.useState(entriesPerPage.toString());

const handleEntriesChange = (value: number) => {
onEntriesPerPageChange(value);
setCustomEntries(value.toString()); // Update local state if preset is clicked
};

const handleCustomEntriesSubmit = () => {
const value = parseInt(customEntries, 10);
if (!isNaN(value) && value > 0) {
onEntriesPerPageChange(value);
} else {
setCustomEntries(entriesPerPage.toString());
}
};

const handleUsdFilterChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
if (/^\d*$/.test(value)) {
setUsdFilters({
...usdFilters,
[name]: value,
});
}
};

return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange} backdrop="blur" size="xl">
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1 font-zen">Market View Settings</ModalHeader>
<ModalBody className="flex flex-col gap-5 px-4 pb-6 pt-2 md:px-6">
{/* --- Filter Settings Section --- */}
<div className="bg-surface-soft flex flex-col gap-4 rounded p-4">
{/* Section Header: Adjusted style & position */}
<h3 className="mb-1 font-zen text-xs uppercase text-secondary">Filter Settings</h3>
<SettingItem
title="Show Unknown Tokens"
description="Display tokens not in the recognized list (marked with '?'). Use with caution."
>
<Switch
isSelected={includeUnknownTokens}
onValueChange={setIncludeUnknownTokens}
size="sm"
color="primary"
/>
</SettingItem>
<Divider />
<SettingItem
title="Show Unknown Oracles"
description="Display markets using unverified oracles. Use with caution."
>
<Switch
isSelected={showUnknownOracle}
onValueChange={setShowUnknownOracle}
size="sm"
color="primary"
/>
</SettingItem>
</div>

{/* --- USD Value Filters Section --- */}
<div className="bg-surface-soft flex flex-col gap-4 rounded p-4">
{/* Section Header: Adjusted style & position */}
<h3 className="mb-1 font-zen text-xs uppercase text-secondary">
Filter by Min USD Value
</h3>
<p className="-mt-3 mb-1 text-xs text-warning">
{' '}
{/* Fine-tune note position */}
Note: USD values are estimates and may not be available or accurate for all
markets.
</p>
<SettingItem
title="Min Supply (USD)"
description="Show markets with total supply >= this value."
>
<Input
aria-label="Minimum Supply in USD"
name="minSupply"
placeholder="0"
value={usdFilters.minSupply}
onChange={handleUsdFilterChange}
size="sm"
type="text"
pattern="[0-9]*"
inputMode="numeric"
className="max-w-[120px]"
classNames={{ input: 'text-right' }}
startContent={
<div className="pointer-events-none flex items-center">
<span className="text-small text-default-400">$</span>
</div>
}
/>
</SettingItem>
<Divider />
<SettingItem
title="Min Borrow (USD)"
description="Show markets with total borrow >= this value."
>
<Input
aria-label="Minimum Borrow in USD"
name="minBorrow"
placeholder="0"
value={usdFilters.minBorrow}
onChange={handleUsdFilterChange}
size="sm"
type="text"
pattern="[0-9]*"
inputMode="numeric"
className="max-w-[120px]"
classNames={{ input: 'text-right' }}
startContent={
<div className="pointer-events-none flex items-center">
<span className="text-small text-default-400">$</span>
</div>
}
/>
</SettingItem>
</div>

{/* --- Pagination Settings Section --- */}
<div className="bg-surface-soft flex flex-col gap-3 rounded p-4">
{/* Section Header: Adjusted style & position */}
<h3 className="mb-1 font-zen text-xs uppercase text-secondary">View Options</h3>
<p className="-mt-1 w-full text-left text-sm">Entries per page:</p>
<div className="flex flex-row flex-wrap items-center justify-start gap-2">
{[8, 10, 15].map((value) => (
<Button
key={value}
size="sm"
onClick={() => handleEntriesChange(value)}
variant={entriesPerPage === value ? 'solid' : 'bordered'}
color={entriesPerPage === value ? 'primary' : 'default'}
className={`min-w-[40px] ${
entriesPerPage === value ? '' : 'border-foreground-300 text-foreground-600'
}`}
>
{value}
</Button>
))}
<div className="flex flex-grow items-center gap-2 sm:flex-grow-0">
<Input
aria-label="Custom entries per page"
type="number"
placeholder="Custom"
value={customEntries}
onChange={(e) => setCustomEntries(e.target.value)}
min="1"
size="sm"
className="w-20"
onKeyDown={(e) => e.key === 'Enter' && handleCustomEntriesSubmit()}
/>
<Button size="sm" onClick={handleCustomEntriesSubmit} variant="flat">
Set
</Button>
</div>
</div>
</div>
</ModalBody>
<ModalFooter>
<Button color="danger" variant="light" onPress={onClose}>
Close
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
);
}
103 changes: 6 additions & 97 deletions app/markets/components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,30 @@
import React, { useEffect, useState } from 'react';
import {
Pagination as NextUIPagination,
Modal,
ModalContent,
ModalHeader,
ModalBody,
Button,
useDisclosure,
Input,
} from '@nextui-org/react';
import { FiSettings } from 'react-icons/fi';
import React from 'react';
import { Pagination as NextUIPagination } from '@nextui-org/react';

type PaginationProps = {
totalPages: number;
currentPage: number;
onPageChange: (page: number) => void;
entriesPerPage: number;
onEntriesPerPageChange: (entries: number) => void;
isDataLoaded: boolean; // New prop to check if data is loaded
isDataLoaded: boolean;
};

export function Pagination({
totalPages,
currentPage,
onPageChange,
entriesPerPage,
onEntriesPerPageChange,
isDataLoaded, // New prop
isDataLoaded,
}: PaginationProps) {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const [customEntries, setCustomEntries] = useState(entriesPerPage.toString());

const handleEntriesChange = (value: number) => {
onEntriesPerPageChange(value);
onOpenChange(); // Close the modal
};

const handleCustomEntriesChange = () => {
const value = parseInt(customEntries, 10);
if (!isNaN(value) && value > 0) {
handleEntriesChange(value);
}
};

// Force re-render when data is loaded
useEffect(() => {
if (isDataLoaded) {
onPageChange(1); // Reset to first page when data loads
}
}, [isDataLoaded, onPageChange]);

if (!isDataLoaded || totalPages === 0) {
return null; // Don't render pagination if data isn't loaded or there are no pages
return null;
}

return (
<div className="mt-4 flex items-center justify-center">
<div className="flex items-center">
<NextUIPagination
key={`pagination-${isDataLoaded}-${totalPages}`} // Force re-render when these change
key={`pagination-${isDataLoaded}-${totalPages}-${entriesPerPage}`}
showControls
total={totalPages}
page={currentPage}
Expand All @@ -71,64 +37,7 @@ export function Pagination({
}}
size="md"
/>
<Button
isIconOnly
aria-label="Settings"
className="bg-surface ml-2"
onClick={onOpen}
size="md"
>
<FiSettings />
</Button>
</div>
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
{() => (
<>
<ModalHeader className="mx-8 mt-8 flex flex-col gap-1 p-4 font-inter">
Settings
</ModalHeader>
<ModalBody className="mx-8 my-4 p-4 font-inter">
<div className="flex flex-col items-center space-y-4">
<p className="w-full text-left">Entries per page:</p>
<div className="flex items-center justify-center space-x-2">
<div className="flex">
{[6, 15, 24].map((value) => (
<Button
key={value}
size="sm"
onClick={() => handleEntriesChange(value)}
className={`px-2 ${
entriesPerPage === value
? 'bg-orange-500 text-white'
: 'bg-surface hover:bg-orange-200'
}`}
>
{value}
</Button>
))}
</div>
<div className="flex items-center space-x-2">
<Input
type="number"
label="Custom"
value={customEntries}
onChange={(e) => setCustomEntries(e.target.value)}
min="1"
size="sm"
className="w-20"
/>
<Button size="sm" onClick={handleCustomEntriesChange}>
Set
</Button>
</div>
</div>
</div>
</ModalBody>
</>
)}
</ModalContent>
</Modal>
</div>
);
}
Loading