diff --git a/src/components/FabButton.css b/src/components/FabButton.css index 851f9ab..0316d1a 100644 --- a/src/components/FabButton.css +++ b/src/components/FabButton.css @@ -1,7 +1,9 @@ .fab { position: fixed; bottom: 32px; - left: max(24px, calc(5vw + 12px)); + /* Keep clear of row action buttons (trash/edit sit on the physical left in RTL). */ + right: max(24px, calc(5vw + 12px)); + left: auto; width: 56px; height: 56px; border: 0; diff --git a/src/pages/ActiveRepresentationsPage.tsx b/src/pages/ActiveRepresentationsPage.tsx index efe5fd4..7c36d3e 100644 --- a/src/pages/ActiveRepresentationsPage.tsx +++ b/src/pages/ActiveRepresentationsPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' import PageTitle from '../components/PageTitle' import FabButton from '../components/FabButton' @@ -19,6 +19,8 @@ export default function ActiveRepresentationsPage() { const [items, setItems] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState('') + const [city, setCity] = useState('') + const [deletingId, setDeletingId] = useState(null) async function load() { setLoading(true) @@ -36,17 +38,37 @@ export default function ActiveRepresentationsPage() { void load() }, []) + const cities = useMemo( + () => + [...new Set(items.map((item) => item.city.trim()).filter(Boolean))].sort((a, b) => + a.localeCompare(b, 'fa'), + ), + [items], + ) + + const filtered = useMemo( + () => (city ? items.filter((item) => item.city === city) : items), + [items, city], + ) + async function onDelete(id: string) { if (!window.confirm('این نمایندگی حذف شود؟')) return + setDeletingId(id) try { await deleteRepresentation(id) setItems((prev) => prev.filter((item) => item.id !== id)) } catch (err) { window.alert(err instanceof ApiError ? err.message : 'حذف ناموفق بود') + } finally { + setDeletingId(null) } } - const { page, setPage, pageCount, pageItems, offset } = usePagination(items) + const { page, setPage, pageCount, pageItems, offset } = usePagination(filtered) + + useEffect(() => { + setPage(1) + }, [city, setPage]) return (
@@ -63,46 +85,77 @@ export default function ActiveRepresentationsPage() { {!loading && !error ? ( <> -
- {items.length === 0 ?

نمایندگی فعالی نیست

: null} - {pageItems.map((item, index) => ( -
-
{offset + index + 1}
+ {cities.length > 0 ? ( +
+ + {cities.map((name) => ( + + ))} +
+ ) : null} -
- {item.name} -
{item.owner || item.mobile}
-
+
+ {filtered.length === 0 ? ( +

+ {city ? 'نمایندگی فعالی در این شهر نیست' : 'نمایندگی فعالی نیست'} +

+ ) : null} + {pageItems.map((item, index) => ( +
+
{offset + index + 1}
-
-
- {item.city} - {item.address} +
+ {item.name} +
{item.owner || item.mobile}
-
-
- - -
-
- ))} -
- +
+
+ {item.city} + {item.address} +
+
+ +
+ + +
+
+ ))} +
+ ) : null} diff --git a/src/pages/ListPages.css b/src/pages/ListPages.css index 69058f1..8bcaffb 100644 --- a/src/pages/ListPages.css +++ b/src/pages/ListPages.css @@ -186,6 +186,34 @@ background: #b2ebf2; } +.city-filter { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin: 4px 0 18px; +} + +.city-filter button { + border: 1px solid #dde3ee; + background: #fff; + border-radius: 999px; + padding: 8px 14px; + cursor: pointer; + font: inherit; + color: #333; +} + +.city-filter button.is-active { + background: var(--color-primary); + border-color: var(--color-primary); + color: #fff; +} + +.city-filter button:hover:not(.is-active) { + border-color: var(--color-primary); + color: var(--color-primary); +} + .filters { display: flex; flex-wrap: wrap;