mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
37 lines
1.3 KiB
TypeScript
37 lines
1.3 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { api } from '@hanzo/app/lib/api'
|
|
import { drop, kept, read, type Quote } from '@/lib/coupon'
|
|
|
|
/**
|
|
* Commerce's quote for a coupon code (lib/coupon): `undefined` while it is
|
|
* asked, `null` with no code. A code commerce refuses is dropped from this tab
|
|
* when it is the code the tab keeps; one it could not be asked about is said as
|
|
* such and kept. The answer is held
|
|
* with the code it was asked for, so a new code reads as asked until its own
|
|
* answer arrives.
|
|
*/
|
|
export function useQuote(code: string | null): Quote | null | undefined {
|
|
const [got, setGot] = useState<{ code: string; quote: Quote } | null>(null)
|
|
useEffect(() => {
|
|
if (!code) return
|
|
let live = true
|
|
const answer = (quote: Quote) => live && setGot({ code, quote })
|
|
|
|
fetch(`${api()}/v1/billing/plans?${new URLSearchParams({ coupon: code })}`, { cache: 'no-store' })
|
|
.then((res) => (res.ok ? res.json() : null))
|
|
.then((body: unknown) => {
|
|
const quote = read(body) ?? { refused: '' }
|
|
if ('refused' in quote && quote.refused && kept() === code) drop()
|
|
answer(quote)
|
|
})
|
|
.catch(() => answer({ refused: '' }))
|
|
return () => {
|
|
live = false
|
|
}
|
|
}, [code])
|
|
if (!code) return null
|
|
return got?.code === code ? got.quote : undefined
|
|
}
|