-
+
{value?.name}
@@ -494,6 +541,42 @@ function IdentityPublicDataCard({ value }: { value?: IdentityPublicData }) {
)
}
+function AnonPublicDataCard({ value }: { value?: AnonPublicData }) {
+ return (
+
+ )
+}
+
+function PayeePublicDataCard({ value }: { value?: PayeePublicData }) {
+ if (!value) return null
+
+ console.log("Payee public data", value)
+
+ if ("Ident" in value) {
+ const identData = (value as { Ident: IdentityPublicData }).Ident
+ return IdentityPublicDataCard({ value: identData })
+ } else if ("Anon" in value) {
+ const anonData = (value as { Anon: AnonPublicData }).Anon
+ return AnonPublicDataCard({ value: anonData })
+ }
+
+ return <>>
+}
function Quote({ value, isFetching }: { value: InfoReply; isFetching: boolean }) {
return (
@@ -557,7 +640,7 @@ function Quote({ value, isFetching }: { value: InfoReply; isFetching: boolean })
Payee:
-
+
@@ -568,30 +651,6 @@ function Quote({ value, isFetching }: { value: InfoReply; isFetching: boolean })
)
}
-function DevSection({ id }: { id: InfoReply["id"] }) {
- const [devMode] = useLocalStorage("devMode", false)
-
- const { data } = useSuspenseQuery({
- ...adminLookupQuoteOptions({
- path: {
- id,
- },
- }),
- })
-
- return (
- <>
- {devMode && (
- <>
-
- {JSON.stringify(data, null, 2)}
-
- >
- )}
- >
- )
-}
-
function PageBody({ id }: { id: InfoReply["id"] }) {
const { data, isFetching } = useSuspenseQuery({
...adminLookupQuoteOptions({
@@ -640,7 +699,6 @@ export default function QuotePage() {
}>
-
>
)
diff --git a/src/pages/quotes/QuotesPage.tsx b/src/pages/quotes/QuotesPage.tsx
deleted file mode 100644
index d18874c..0000000
--- a/src/pages/quotes/QuotesPage.tsx
+++ /dev/null
@@ -1,188 +0,0 @@
-import { Suspense } from "react"
-import { Breadcrumbs } from "@/components/Breadcrumbs"
-import { PageTitle } from "@/components/PageTitle"
-import { Skeleton } from "@/components/ui/skeleton"
-import { listQuotesOptions, listPendingQuotesOptions } from "@/generated/client/@tanstack/react-query.gen"
-import { useSuspenseQuery } from "@tanstack/react-query"
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
-import { cn } from "@/lib/utils"
-import { Link } from "react-router"
-import { ChevronRight } from "lucide-react"
-import { ListQuotesData } from "@/generated/client"
-
-function Loader() {
- return (
-
-
-
-
-
-
-
-
- )
-}
-
-function PageBody() {
- const { data: quotesPending } = useSuspenseQuery({
- ...listPendingQuotesOptions({}),
- })
-
- const { data: quotesAccepted } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "accepted",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- const { data: quotesOffered } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "offered",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- const { data: quotesDenied } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "denied",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- const { data: quotesExpired } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "expired",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- const { data: quotesRejected } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "rejected",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- return (
-
-
-
0,
- })}
- >
-
-
-
- Pending Quotes
-
-
- {quotesPending.quotes.length === 0 ? (
- <>💪 No pending quotes.>
- ) : (
- <>{quotesPending.quotes.length} pending>
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
- Offered quotes
-
- {quotesOffered.quotes.length} offered
-
-
-
-
-
-
-
-
-
-
-
-
- Accepted quotes
-
- {quotesAccepted.quotes.length} accepted
-
-
-
-
-
-
-
-
-
-
-
-
- Denied quotes
-
- {quotesDenied.quotes.length} denied
-
-
-
-
-
-
-
-
-
-
-
-
- Rejected quotes
-
- {quotesRejected.quotes.length} rejected
-
-
-
-
-
-
-
-
-
-
-
-
- Expired quotes
-
- {quotesExpired.quotes.length} expired
-
-
-
-
-
-
-
-
- )
-}
-
-export default function QuotesPage() {
- return (
- <>
-
Quotes
-
Quotes
-
}>
-
-
- >
- )
-}
diff --git a/src/pages/quotes/RejectedQuotesPage.tsx b/src/pages/quotes/RejectedQuotesPage.tsx
deleted file mode 100644
index 2b89f74..0000000
--- a/src/pages/quotes/RejectedQuotesPage.tsx
+++ /dev/null
@@ -1,136 +0,0 @@
-import { Breadcrumbs } from "@/components/Breadcrumbs"
-import { PageTitle } from "@/components/PageTitle"
-import { Button } from "@/components/ui/button"
-import { Skeleton } from "@/components/ui/skeleton"
-import { ListQuotesData } from "@/generated/client"
-import { listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen"
-import useLocalStorage from "@/hooks/use-local-storage"
-import { cn } from "@/lib/utils"
-import { useSuspenseQuery } from "@tanstack/react-query"
-import { LoaderIcon } from "lucide-react"
-import { Suspense } from "react"
-import { Link, useNavigate } from "react-router"
-
-function Loader() {
- return (
-
-
-
-
-
-
-
-
- )
-}
-
-function QuoteListRejected() {
- const navigate = useNavigate()
-
- const { data, isFetching } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "Rejected",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- return (
- <>
-
-
-
-
-
- {data.quotes.length === 0 &&
No rejected quotes.
}
- {data.quotes.map((it, index) => {
- return (
-
-
- {isFetching ? (
- <>{it.id}>
- ) : (
- <>
- {it.id}
- >
- )}
-
-
-
-
- )
- })}
-
- >
- )
-}
-
-function DevSection() {
- const [devMode] = useLocalStorage("devMode", false)
-
- const { data: quotesRejected } = useSuspenseQuery({
- ...listQuotesOptions({
- query: {
- status: "Rejected",
- } as unknown as ListQuotesData["query"],
- }),
- })
-
- return (
- <>
- {devMode && (
- <>
-
- {JSON.stringify(quotesRejected, null, 2)}
-
- >
- )}
- >
- )
-}
-
-function PageBody() {
- return (
-
- )
-}
-
-export default function RejectedQuotesPage() {
- return (
- <>
-
- Quotes
- >,
- ]}
- >
- Rejected
-
-
Rejected Quotes
-
}>
-
-
-
-
-
- >
- )
-}
diff --git a/src/pages/quotes/PendingQuotesPage.tsx b/src/pages/quotes/StatusQuotePage.tsx
similarity index 82%
rename from src/pages/quotes/PendingQuotesPage.tsx
rename to src/pages/quotes/StatusQuotePage.tsx
index 0fe7c49..2465c42 100644
--- a/src/pages/quotes/PendingQuotesPage.tsx
+++ b/src/pages/quotes/StatusQuotePage.tsx
@@ -6,7 +6,6 @@ import { Skeleton } from "@/components/ui/skeleton"
import { InfoReply } from "@/generated/client"
import { ListQuotesData } from "@/generated/client"
import { adminLookupQuoteOptions, listQuotesOptions } from "@/generated/client/@tanstack/react-query.gen"
-import useLocalStorage from "@/hooks/use-local-storage"
import { useSuspenseQuery } from "@tanstack/react-query"
import { LoaderIcon } from "lucide-react"
import { Suspense } from "react"
@@ -17,6 +16,12 @@ import { formatNumber, truncateString } from "@/utils/strings"
import { Badge } from "@/components/ui/badge"
import { cn } from "@/lib/utils"
+type QuoteStatus = "Accepted" | "Denied" | "Expired" | "Offered" | "Pending" | "Rejected" | "Cancelled"
+
+interface StatusQuotePageProps {
+ status?: QuoteStatus
+}
+
function Loader() {
return (
@@ -91,15 +96,18 @@ function QuoteItemCard({ id, isLoading }: { id: InfoReply["id"]; isLoading: bool
)
}
-function QuoteListPending() {
+function QuoteList({ status }: { status?: QuoteStatus }) {
+ const queryParams = status ? ({ status } as unknown as ListQuotesData["query"]) : {}
+
const { data, isFetching } = useSuspenseQuery({
...listQuotesOptions({
- query: {
- status: "Pending",
- } as unknown as ListQuotesData["query"],
+ query: queryParams,
}),
})
+ const statusText = status ? status.toLowerCase() : "all"
+ const noQuotesMessage = `No ${statusText} quotes.`
+
return (
<>
@@ -112,7 +120,7 @@ function QuoteListPending() {
- {data.quotes.length === 0 &&
No pending quotes.
}
+ {data.quotes.length === 0 &&
{noQuotesMessage}
}
{data.quotes.map((it, index) => {
return (
@@ -125,38 +133,20 @@ function QuoteListPending() {
)
}
-function DevSection() {
- return <>>
- const [devMode] = useLocalStorage("devMode", false)
-
- const { data: quotesPending } = useSuspenseQuery({
- ...listQuotesOptions({}),
- })
-
- return (
- <>
- {devMode && (
- <>
-
- {JSON.stringify(quotesPending, null, 2)}
-
- >
- )}
- >
- )
-}
-
-function PageBody() {
+function PageBody({ status }: { status?: QuoteStatus }) {
return (
)
}
-export default function PendingQuotesPage() {
+export default function StatusQuotePage({ status }: StatusQuotePageProps) {
+ const pageTitle = status ? `${status} Quotes` : "All Quotes"
+ const breadcrumbText = status ?? "All"
+
return (
<>
,
]}
>
- Pending
+ {breadcrumbText}
-
Pending Quotes
+
{pageTitle}
}>
-
-
-
-
+
>
)
diff --git a/src/pages/settings/SettingsPage.tsx b/src/pages/settings/SettingsPage.tsx
index 5fabd74..0901ebc 100644
--- a/src/pages/settings/SettingsPage.tsx
+++ b/src/pages/settings/SettingsPage.tsx
@@ -1,11 +1,7 @@
import { Breadcrumbs } from "@/components/Breadcrumbs"
import { PageTitle } from "@/components/PageTitle"
-import { Label } from "@/components/ui/label"
import { Skeleton } from "@/components/ui/skeleton"
-import { Switch } from "@/components/ui/switch"
-import useLocalStorage from "@/hooks/use-local-storage"
import { Suspense } from "react"
-import { toast } from "sonner"
function Loader() {
return (
@@ -16,32 +12,9 @@ function Loader() {
}
function PageBody() {
- const [devMode, setDevMode] = useLocalStorage("devMode", false)
-
return (
<>
-
-
- {
- toast.info(
- <>
- Developer mode is {(!devMode && "ON") || "OFF"}
- >,
- {
- id: "settings-dev-mode",
- duration: 1_337,
- },
- )
- setDevMode((it) => !it)
- }}
- />
-
-
-
+
>
)
}
diff --git a/src/utils/dev.ts b/src/utils/dev.ts
index 3f50e32..706f779 100644
--- a/src/utils/dev.ts
+++ b/src/utils/dev.ts
@@ -1,9 +1,31 @@
-export const randomAvatar = (path: "men" | "women" | undefined, seed: string | undefined) => {
- const _path = path ?? (Math.random() > 0.5 ? "men" : "women")
- const _seed =
- (seed ?? `${Math.floor(Math.random() * 100)}`)
- .split("")
- .map((it) => it.charCodeAt(0))
- .reduce((prev, curr) => prev + curr, 0) % 100
- return `https://randomuser.me/api/portraits/${_path}/${_seed}.jpg`
+export function getDeterministicColor(seed?: string): string {
+ if (!seed) return "#64748b" // Default gray
+
+ const colorPalette = [
+ "#ef4444",
+ "#f97316",
+ "#f59e0b",
+ "#eab308",
+ "#84cc16",
+ "#22c55e",
+ "#10b981",
+ "#14b8a6",
+ "#06b6d4",
+ "#0ea5e9",
+ "#3b82f6",
+ "#6366f1",
+ "#8b5cf6",
+ "#a855f7",
+ "#d946ef",
+ "#ec4899",
+ "#f43f5e",
+ ]
+
+ let hash = 0
+ for (let i = 0; i < seed.length; i++) {
+ hash = ((hash << 5) - hash + seed.charCodeAt(i)) & 0xffffffff
+ }
+
+ const colorIndex = Math.abs(hash) % colorPalette.length
+ return colorPalette[colorIndex]
}