inv & with satistics + filtering
This commit is contained in:
parent
06f5ed731e
commit
d92f58aaf4
@ -49,46 +49,62 @@ export default {
|
||||
connectAddress: async (parent, args, { lnd }) => {
|
||||
return process.env.LND_CONNECT_ADDRESS
|
||||
},
|
||||
walletHistory: async (parent, { cursor }, { me, models, lnd }) => {
|
||||
walletHistory: async (parent, { cursor, inc }, { me, models, lnd }) => {
|
||||
const decodedCursor = decodeCursor(cursor)
|
||||
if (!me) {
|
||||
throw new AuthenticationError('you must be logged in')
|
||||
}
|
||||
|
||||
const include = new Set(inc.split(','))
|
||||
const queries = []
|
||||
|
||||
if (include.has('invoice')) {
|
||||
queries.push(
|
||||
`(SELECT ('invoice' || id) as id, id as "factId", bolt11, created_at as "createdAt",
|
||||
COALESCE("msatsReceived", "msatsRequested") as msats, NULL as "msatsFee",
|
||||
CASE WHEN "confirmedAt" IS NOT NULL THEN 'CONFIRMED'
|
||||
WHEN "expiresAt" <= $2 THEN 'EXPIRED'
|
||||
WHEN cancelled THEN 'CANCELLED'
|
||||
ELSE 'PENDING' END as status,
|
||||
'invoice' as type
|
||||
FROM "Invoice"
|
||||
WHERE "userId" = $1
|
||||
AND created_at <= $2)`)
|
||||
}
|
||||
|
||||
if (include.has('withdrawal')) {
|
||||
queries.push(
|
||||
`(SELECT ('withdrawal' || id) as id, id as "factId", bolt11, created_at as "createdAt",
|
||||
CASE WHEN status = 'CONFIRMED' THEN "msatsPaid"
|
||||
ELSE "msatsPaying" END as msats,
|
||||
CASE WHEN status = 'CONFIRMED' THEN "msatsFeePaid"
|
||||
ELSE "msatsFeePaying" END as "msatsFee",
|
||||
COALESCE(status::text, 'PENDING') as status,
|
||||
'withdrawal' as type
|
||||
FROM "Withdrawl"
|
||||
WHERE "userId" = $1
|
||||
AND created_at <= $2)`)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// 1. union invoices and withdrawals (check)
|
||||
// 2. add to union spending and receiving
|
||||
|
||||
if (queries.length === 0) {
|
||||
return {
|
||||
cursor: null,
|
||||
facts: []
|
||||
}
|
||||
}
|
||||
|
||||
let history = await models.$queryRaw(`
|
||||
(SELECT id, bolt11, created_at as "createdAt",
|
||||
COALESCE("msatsReceived", "msatsRequested") as msats, NULL as "msatsFee",
|
||||
CASE WHEN "confirmedAt" IS NOT NULL THEN 'CONFIRMED'
|
||||
WHEN "expiresAt" <= $2 THEN 'EXPIRED'
|
||||
WHEN cancelled THEN 'CANCELLED'
|
||||
ELSE 'PENDING' END as status,
|
||||
'invoice' as type
|
||||
FROM "Invoice"
|
||||
WHERE "userId" = $1
|
||||
AND created_at <= $2
|
||||
ORDER BY created_at desc
|
||||
LIMIT ${LIMIT}+$3)
|
||||
UNION ALL
|
||||
(SELECT id, bolt11, created_at as "createdAt",
|
||||
CASE WHEN status = 'CONFIRMED' THEN "msatsPaid"
|
||||
ELSE "msatsPaying" END as msats,
|
||||
CASE WHEN status = 'CONFIRMED' THEN "msatsFeePaid"
|
||||
ELSE "msatsFeePaying" END as "msatsFee",
|
||||
COALESCE(status::text, 'PENDING') as status,
|
||||
'withdrawal' as type
|
||||
FROM "Withdrawl"
|
||||
WHERE "userId" = $1
|
||||
AND created_at <= $2
|
||||
ORDER BY created_at desc
|
||||
LIMIT ${LIMIT}+$3)
|
||||
${queries.join(' UNION ALL ')}
|
||||
ORDER BY "createdAt" DESC
|
||||
OFFSET $3
|
||||
LIMIT ${LIMIT}`, me.id, decodedCursor.time, decodedCursor.offset)
|
||||
|
||||
console.log(history)
|
||||
|
||||
history = history.map(f => {
|
||||
if (f.bolt11) {
|
||||
const inv = lnpr.decode(f.bolt11)
|
||||
@ -113,6 +129,8 @@ export default {
|
||||
return f
|
||||
})
|
||||
|
||||
console.log(history)
|
||||
|
||||
return {
|
||||
cursor: history.length === LIMIT ? nextCursorEncoded(decodedCursor) : null,
|
||||
facts: history
|
||||
|
@ -30,7 +30,7 @@ export default async function getSSRApolloClient (req, me = null) {
|
||||
}
|
||||
|
||||
export function getGetServerSideProps (query, variables = null, foundField) {
|
||||
return async function ({ req, params }) {
|
||||
return async function ({ req, query: params }) {
|
||||
const client = await getSSRApolloClient(req)
|
||||
const { error, data } = await client.query({
|
||||
query,
|
||||
|
@ -5,7 +5,7 @@ export default gql`
|
||||
invoice(id: ID!): Invoice!
|
||||
withdrawl(id: ID!): Withdrawl!
|
||||
connectAddress: String!
|
||||
walletHistory(cursor: String): History
|
||||
walletHistory(cursor: String, inc: String): History
|
||||
}
|
||||
|
||||
extend type Mutation {
|
||||
@ -41,6 +41,7 @@ export default gql`
|
||||
|
||||
type Fact {
|
||||
id: ID!
|
||||
factId: ID!
|
||||
bolt11: String
|
||||
createdAt: String!
|
||||
msats: Int!
|
||||
|
@ -175,33 +175,31 @@ export function Input ({ label, groupClassName, ...props }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function Checkbox ({ children, label, extra, handleChange, ...props }) {
|
||||
export function Checkbox ({ children, label, extra, handleChange, inline, ...props }) {
|
||||
// React treats radios and checkbox inputs differently other input types, select, and textarea.
|
||||
// Formik does this too! When you specify `type` to useField(), it will
|
||||
// return the correct bag of props for you
|
||||
const [field, { value }] = useField({ ...props, type: 'checkbox' })
|
||||
const [field] = useField({ ...props, type: 'checkbox' })
|
||||
return (
|
||||
<div className={value ? styles.checkboxChecked : styles.checkboxUnchecked}>
|
||||
<BootstrapForm.Check
|
||||
custom
|
||||
id={props.id || props.name}
|
||||
>
|
||||
<BootstrapForm.Check.Input
|
||||
{...field} {...props} type='checkbox' onChange={(e) => {
|
||||
field.onChange(e)
|
||||
handleChange && handleChange(e.target.checked)
|
||||
}}
|
||||
/>
|
||||
<BootstrapForm.Check.Label className='d-flex'>
|
||||
<div className='flex-grow-1'>{label}</div>
|
||||
{extra &&
|
||||
<div className={styles.checkboxExtra}>
|
||||
{extra}
|
||||
</div>}
|
||||
</BootstrapForm.Check.Label>
|
||||
</BootstrapForm.Check>
|
||||
{children}
|
||||
</div>
|
||||
<BootstrapForm.Check
|
||||
custom
|
||||
id={props.id || props.name}
|
||||
inline={inline}
|
||||
>
|
||||
<BootstrapForm.Check.Input
|
||||
{...field} {...props} type='checkbox' onChange={(e) => {
|
||||
field.onChange(e)
|
||||
handleChange && handleChange(e.target.checked)
|
||||
}}
|
||||
/>
|
||||
<BootstrapForm.Check.Label className='d-flex'>
|
||||
<div className='flex-grow-1'>{label}</div>
|
||||
{extra &&
|
||||
<div className={styles.checkboxExtra}>
|
||||
{extra}
|
||||
</div>}
|
||||
</BootstrapForm.Check.Label>
|
||||
</BootstrapForm.Check>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ export default function UserHeader ({ user }) {
|
||||
</div>
|
||||
<Nav
|
||||
className={styles.nav}
|
||||
activeKey={router.asPath}
|
||||
activeKey={router.asPath.split('?')[0]}
|
||||
>
|
||||
<Nav.Item>
|
||||
<Link href={'/' + user.name} passHref>
|
||||
@ -132,8 +132,8 @@ export default function UserHeader ({ user }) {
|
||||
</Nav.Item>
|
||||
{isMe &&
|
||||
<Nav.Item>
|
||||
<Link href='/satistics' passHref>
|
||||
<Nav.Link>satistics</Nav.Link>
|
||||
<Link href='/satistics?inc=invoice,withdrawal,stacked,spent' passHref>
|
||||
<Nav.Link eventKey='/satistics'>satistics</Nav.Link>
|
||||
</Link>
|
||||
</Nav.Item>}
|
||||
</Nav>
|
||||
|
@ -25,10 +25,11 @@ export const WITHDRAWL = gql`
|
||||
}`
|
||||
|
||||
export const WALLET_HISTORY = gql`
|
||||
query WalletHistory($cursor: String) {
|
||||
walletHistory(cursor: $cursor) {
|
||||
query WalletHistory($cursor: String, $inc: String) {
|
||||
walletHistory(cursor: $cursor, inc: $inc) {
|
||||
facts {
|
||||
id
|
||||
factId
|
||||
type
|
||||
createdAt
|
||||
msats
|
||||
|
@ -66,7 +66,7 @@ export default function getApolloClient () {
|
||||
}
|
||||
},
|
||||
walletHistory: {
|
||||
keyArgs: false,
|
||||
keyArgs: ['inc'],
|
||||
merge (existing, incoming) {
|
||||
if (isFirstPage(incoming.cursor, existing?.facts)) {
|
||||
return incoming
|
||||
|
@ -12,6 +12,8 @@ import styles from '../styles/satistics.module.css'
|
||||
import Moon from '../svgs/moon-fill.svg'
|
||||
import Check from '../svgs/check-double-line.svg'
|
||||
import ThumbDown from '../svgs/thumb-down-fill.svg'
|
||||
import { Checkbox, Form } from '../components/form'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
export const getServerSideProps = getGetServerSideProps(WALLET_HISTORY)
|
||||
|
||||
@ -83,7 +85,29 @@ function Satus ({ status }) {
|
||||
export default function Satistics ({ data: { walletHistory: { facts, cursor } } }) {
|
||||
const me = useMe()
|
||||
const { value: darkMode } = useDarkMode()
|
||||
const { data, fetchMore } = useQuery(WALLET_HISTORY)
|
||||
const router = useRouter()
|
||||
const { data, fetchMore } = useQuery(WALLET_HISTORY, { variables: { inc: router.query.inc } })
|
||||
|
||||
console.log(router.query.inc, data)
|
||||
|
||||
function filterRoutePush (filter, add) {
|
||||
const inc = new Set(router.query.inc.split(','))
|
||||
inc.delete('')
|
||||
// depending on addrem, add or remove filter
|
||||
if (add) {
|
||||
inc.add(filter)
|
||||
} else {
|
||||
inc.delete(filter)
|
||||
}
|
||||
|
||||
const incstr = [...inc].join(',')
|
||||
router.push(`/satistics?inc=${incstr}`)
|
||||
}
|
||||
|
||||
function included (filter) {
|
||||
const inc = new Set(router.query.inc.split(','))
|
||||
return inc.has(filter)
|
||||
}
|
||||
|
||||
if (data) {
|
||||
({ walletHistory: { facts, cursor } } = data)
|
||||
@ -97,32 +121,61 @@ export default function Satistics ({ data: { walletHistory: { facts, cursor } }
|
||||
return (
|
||||
<Layout noSeo>
|
||||
<UserHeader user={me} />
|
||||
<Table className='mt-3 mb-0' bordered hover size='sm' variant={darkMode ? 'dark' : undefined}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={styles.type}>type</th>
|
||||
<th>detail</th>
|
||||
<th className={styles.sats}>sats</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{facts.map((f, i) => (
|
||||
<Link href={`${f.type}s/${f.id}`} key={`${f.type}-${f.id}`}>
|
||||
<tr className={styles.row}>
|
||||
<td className={`${styles.type} ${satusClass(f.status)}`}>{f.type}</td>
|
||||
<td className={styles.description}>
|
||||
<div className={satusClass(f.status)}>
|
||||
{f.description || 'no description'}
|
||||
</div>
|
||||
<Satus status={f.status} />
|
||||
</td>
|
||||
<td className={`${styles.sats} ${satusClass(f.status)}`}>{f.msats / 1000}</td>
|
||||
</tr>
|
||||
</Link>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<MoreFooter cursor={cursor} fetchMore={fetchMore} Skeleton={SatisticsSkeleton} />
|
||||
<div className='mt-3'>
|
||||
<Form
|
||||
initial={{
|
||||
invoice: included('invoice'),
|
||||
withdrawal: included('withdrawal'),
|
||||
stacked: included('stacked'),
|
||||
spent: included('spent')
|
||||
}}
|
||||
>
|
||||
<div className='d-flex justify-content-around flex-wrap'>
|
||||
<Checkbox
|
||||
label='invoice' name='invoice' inline
|
||||
handleChange={c => filterRoutePush('invoice', c)}
|
||||
/>
|
||||
<Checkbox
|
||||
label='withdrawal' name='withdrawal' inline
|
||||
handleChange={c => filterRoutePush('withdrawal', c)}
|
||||
/>
|
||||
<Checkbox
|
||||
label='stacked' name='stacked' inline
|
||||
handleChange={c => filterRoutePush('stacked', c)}
|
||||
/>
|
||||
<Checkbox
|
||||
label='spent' name='spent' inline
|
||||
handleChange={c => filterRoutePush('spent', c)}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
<Table className='mt-3 mb-0' bordered hover size='sm' variant={darkMode ? 'dark' : undefined}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={styles.type}>type</th>
|
||||
<th>detail</th>
|
||||
<th className={styles.sats}>sats</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{facts.map((f, i) => (
|
||||
<Link href={`${f.type}s/${f.factId}`} key={f.id}>
|
||||
<tr className={styles.row}>
|
||||
<td className={`${styles.type} ${satusClass(f.status)}`}>{f.type}</td>
|
||||
<td className={styles.description}>
|
||||
<div className={satusClass(f.status)}>
|
||||
{f.description || 'no description'}
|
||||
</div>
|
||||
<Satus status={f.status} />
|
||||
</td>
|
||||
<td className={`${styles.sats} ${satusClass(f.status)}`}>{f.msats / 1000}</td>
|
||||
</tr>
|
||||
</Link>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<MoreFooter cursor={cursor} fetchMore={fetchMore} Skeleton={SatisticsSkeleton} />
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
@ -64,6 +64,10 @@ $tooltip-bg: #5c8001;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
.custom-checkbox.custom-control-inline {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: var(--theme-color);
|
||||
background-color: var(--theme-body);
|
||||
|
Loading…
x
Reference in New Issue
Block a user