initial commit for manalinks UI improvements (#642)
* manalinks UI improvements * made manalink look more like card * changed new link to pulsing indigo instead of green
This commit is contained in:
parent
55c91dfcdd
commit
a4e2cce4aa
37
web/components/button.tsx
Normal file
37
web/components/button.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { ReactNode } from 'react'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
|
export default function Button(props: {
|
||||||
|
className?: string
|
||||||
|
onClick?: () => void
|
||||||
|
color: 'green' | 'red' | 'blue' | 'indigo' | 'yellow' | 'gray'
|
||||||
|
children?: ReactNode
|
||||||
|
type?: 'button' | 'reset' | 'submit'
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
|
color = 'indigo',
|
||||||
|
type = 'button',
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type={type}
|
||||||
|
className={clsx(
|
||||||
|
'font-md items-center justify-center rounded-md border border-transparent px-4 py-2 shadow-sm hover:transition-colors',
|
||||||
|
color === 'green' && 'btn-primary text-white',
|
||||||
|
color === 'red' && 'bg-red-400 text-white hover:bg-red-500',
|
||||||
|
color === 'yellow' && 'bg-yellow-400 text-white hover:bg-yellow-500',
|
||||||
|
color === 'blue' && 'bg-blue-400 text-white hover:bg-blue-500',
|
||||||
|
color === 'indigo' && 'bg-indigo-500 text-white hover:bg-indigo-600',
|
||||||
|
color === 'gray' && 'bg-gray-200 text-gray-700 hover:bg-gray-300',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
|
@ -67,3 +67,44 @@ export function ManalinkCard(props: {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ManalinkCardPreview(props: {
|
||||||
|
className?: string
|
||||||
|
info: ManalinkInfo
|
||||||
|
defaultMessage: string
|
||||||
|
}) {
|
||||||
|
const { className, defaultMessage, info } = props
|
||||||
|
const { expiresTime, maxUses, uses, amount, message } = info
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
className,
|
||||||
|
' group flex flex-col rounded-lg bg-gradient-to-br from-indigo-200 via-indigo-400 to-indigo-800 shadow-lg transition-all'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Col className="mx-4 mt-2 -mb-4 text-right text-xs text-gray-100">
|
||||||
|
<div>
|
||||||
|
{maxUses != null
|
||||||
|
? `${maxUses - uses}/${maxUses} uses left`
|
||||||
|
: `Unlimited use`}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{expiresTime != null
|
||||||
|
? `Expires ${fromNow(expiresTime)}`
|
||||||
|
: 'Never expires'}
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<img
|
||||||
|
className="my-2 block h-1/3 w-1/3 self-center transition-all group-hover:rotate-12"
|
||||||
|
src="/logo-white.svg"
|
||||||
|
/>
|
||||||
|
<Row className="rounded-b-lg bg-white p-2">
|
||||||
|
<Col className="text-md">
|
||||||
|
<div className="mb-1 text-indigo-500">{formatMoney(amount)}</div>
|
||||||
|
<div className="text-xs">{message || defaultMessage}</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
197
web/components/manalinks/create-links-button.tsx
Normal file
197
web/components/manalinks/create-links-button.tsx
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Col } from '../layout/col'
|
||||||
|
import { Row } from '../layout/row'
|
||||||
|
import { Title } from '../title'
|
||||||
|
import { User } from 'common/user'
|
||||||
|
import { ManalinkCardPreview, ManalinkInfo } from 'web/components/manalink-card'
|
||||||
|
import { createManalink } from 'web/lib/firebase/manalinks'
|
||||||
|
import { Modal } from 'web/components/layout/modal'
|
||||||
|
import Textarea from 'react-expanding-textarea'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import Button from '../button'
|
||||||
|
import { getManalinkUrl } from 'web/pages/links'
|
||||||
|
import { DuplicateIcon } from '@heroicons/react/outline'
|
||||||
|
|
||||||
|
export function CreateLinksButton(props: {
|
||||||
|
user: User
|
||||||
|
highlightedSlug: string
|
||||||
|
setHighlightedSlug: (slug: string) => void
|
||||||
|
}) {
|
||||||
|
const { user, highlightedSlug, setHighlightedSlug } = props
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal open={open} setOpen={(newOpen) => setOpen(newOpen)}>
|
||||||
|
<Col className="gap-4 rounded-md bg-white px-8 py-6">
|
||||||
|
<CreateManalinkForm
|
||||||
|
highlightedSlug={highlightedSlug}
|
||||||
|
user={user}
|
||||||
|
onCreate={async (newManalink) => {
|
||||||
|
const slug = await createManalink({
|
||||||
|
fromId: user.id,
|
||||||
|
amount: newManalink.amount,
|
||||||
|
expiresTime: newManalink.expiresTime,
|
||||||
|
maxUses: newManalink.maxUses,
|
||||||
|
message: newManalink.message,
|
||||||
|
})
|
||||||
|
setHighlightedSlug(slug || '')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
color={'indigo'}
|
||||||
|
onClick={() => setOpen(true)}
|
||||||
|
className={clsx('whitespace-nowrap')}
|
||||||
|
>
|
||||||
|
Create a Manalink
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreateManalinkForm(props: {
|
||||||
|
highlightedSlug: string
|
||||||
|
user: User
|
||||||
|
onCreate: (m: ManalinkInfo) => Promise<void>
|
||||||
|
}) {
|
||||||
|
const { user, onCreate, highlightedSlug } = props
|
||||||
|
const [isCreating, setIsCreating] = useState(false)
|
||||||
|
const [finishedCreating, setFinishedCreating] = useState(false)
|
||||||
|
const [copyPressed, setCopyPressed] = useState(false)
|
||||||
|
setTimeout(() => setCopyPressed(false), 300)
|
||||||
|
|
||||||
|
const [newManalink, setNewManalink] = useState<ManalinkInfo>({
|
||||||
|
expiresTime: null,
|
||||||
|
amount: 100,
|
||||||
|
maxUses: 1,
|
||||||
|
uses: 0,
|
||||||
|
message: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!finishedCreating && (
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
setIsCreating(true)
|
||||||
|
onCreate(newManalink).finally(() => setIsCreating(false))
|
||||||
|
setFinishedCreating(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Title className="!my-0" text="Create a Manalink" />
|
||||||
|
<div className="flex flex-col flex-wrap gap-x-5 gap-y-2">
|
||||||
|
<div className="form-control flex-auto">
|
||||||
|
<label className="label">Amount</label>
|
||||||
|
<input
|
||||||
|
className="input input-bordered"
|
||||||
|
type="number"
|
||||||
|
value={newManalink.amount}
|
||||||
|
onChange={(e) =>
|
||||||
|
setNewManalink((m) => {
|
||||||
|
return { ...m, amount: parseInt(e.target.value) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-2 md:flex-row">
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">Uses</label>
|
||||||
|
<input
|
||||||
|
className="input input-bordered w-full"
|
||||||
|
type="number"
|
||||||
|
value={newManalink.maxUses ?? ''}
|
||||||
|
onChange={(e) =>
|
||||||
|
setNewManalink((m) => {
|
||||||
|
return { ...m, maxUses: parseInt(e.target.value) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
></input>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">Expires in</label>
|
||||||
|
<input
|
||||||
|
value={
|
||||||
|
newManalink.expiresTime != null
|
||||||
|
? dayjs(newManalink.expiresTime).format(
|
||||||
|
'YYYY-MM-DDTHH:mm'
|
||||||
|
)
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
className="input input-bordered"
|
||||||
|
type="datetime-local"
|
||||||
|
onChange={(e) => {
|
||||||
|
setNewManalink((m) => {
|
||||||
|
return {
|
||||||
|
...m,
|
||||||
|
expiresTime: e.target.value
|
||||||
|
? dayjs(e.target.value, 'YYYY-MM-DDTHH:mm').valueOf()
|
||||||
|
: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
></input>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-control w-full">
|
||||||
|
<label className="label">Message</label>
|
||||||
|
<Textarea
|
||||||
|
placeholder={`From ${user.name}`}
|
||||||
|
className="input input-bordered resize-none"
|
||||||
|
autoFocus
|
||||||
|
value={newManalink.message}
|
||||||
|
rows="3"
|
||||||
|
onChange={(e) =>
|
||||||
|
setNewManalink((m) => {
|
||||||
|
return { ...m, message: e.target.value }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color={'indigo'}
|
||||||
|
className={clsx(
|
||||||
|
'mt-8 whitespace-nowrap drop-shadow-md',
|
||||||
|
isCreating ? 'disabled' : ''
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
{finishedCreating && (
|
||||||
|
<>
|
||||||
|
<Title className="!my-0" text="Manalink Created!" />
|
||||||
|
<ManalinkCardPreview
|
||||||
|
className="my-4"
|
||||||
|
defaultMessage={`From ${user.name}`}
|
||||||
|
info={newManalink}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
className={clsx(
|
||||||
|
'rounded border bg-gray-50 py-2 px-3 text-sm text-gray-500 transition-colors duration-700',
|
||||||
|
copyPressed ? 'bg-indigo-50 text-indigo-500 transition-none' : ''
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="w-full select-text truncate">
|
||||||
|
{getManalinkUrl(highlightedSlug)}
|
||||||
|
</div>
|
||||||
|
<DuplicateIcon
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(getManalinkUrl(highlightedSlug))
|
||||||
|
setCopyPressed(true)
|
||||||
|
}}
|
||||||
|
className="my-auto ml-2 h-5 w-5 cursor-pointer transition hover:opacity-50"
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
15
web/components/subtitle.tsx
Normal file
15
web/components/subtitle.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
|
export function Subtitle(props: { text: string; className?: string }) {
|
||||||
|
const { text, className } = props
|
||||||
|
return (
|
||||||
|
<h1
|
||||||
|
className={clsx(
|
||||||
|
'mt-6 mb-2 inline-block text-lg text-indigo-500 sm:mt-6 sm:mb-2 sm:text-xl',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</h1>
|
||||||
|
)
|
||||||
|
}
|
3
web/get-manalink-url.ts
Normal file
3
web/get-manalink-url.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function getManalinkUrl(slug: string) {
|
||||||
|
return `${location.protocol}//${location.host}/link/${slug}`
|
||||||
|
}
|
|
@ -4,41 +4,29 @@ import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
|
||||||
import { Claim, Manalink } from 'common/manalink'
|
import { Claim, Manalink } from 'common/manalink'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
|
import { Row } from 'web/components/layout/row'
|
||||||
import { Page } from 'web/components/page'
|
import { Page } from 'web/components/page'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
import { Title } from 'web/components/title'
|
import { Title } from 'web/components/title'
|
||||||
|
import { Subtitle } from 'web/components/subtitle'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import { createManalink, useUserManalinks } from 'web/lib/firebase/manalinks'
|
import { useUserManalinks } from 'web/lib/firebase/manalinks'
|
||||||
import { fromNow } from 'web/lib/util/time'
|
import { fromNow } from 'web/lib/util/time'
|
||||||
import { useUserById } from 'web/hooks/use-users'
|
import { useUserById } from 'web/hooks/use-users'
|
||||||
import { ManalinkTxn } from 'common/txn'
|
import { ManalinkTxn } from 'common/txn'
|
||||||
import { User } from 'common/user'
|
|
||||||
import { Tabs } from 'web/components/layout/tabs'
|
|
||||||
import { Avatar } from 'web/components/avatar'
|
import { Avatar } from 'web/components/avatar'
|
||||||
import { RelativeTimestamp } from 'web/components/relative-timestamp'
|
import { RelativeTimestamp } from 'web/components/relative-timestamp'
|
||||||
import { UserLink } from 'web/components/user-page'
|
import { UserLink } from 'web/components/user-page'
|
||||||
import { ManalinkCard, ManalinkInfo } from 'web/components/manalink-card'
|
import { CreateLinksButton } from 'web/components/manalinks/create-links-button'
|
||||||
|
|
||||||
import Textarea from 'react-expanding-textarea'
|
|
||||||
|
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
function getLinkUrl(slug: string) {
|
export function getManalinkUrl(slug: string) {
|
||||||
return `${location.protocol}//${location.host}/link/${slug}`
|
return `${location.protocol}//${location.host}/link/${slug}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: incredibly gross, but the tab component is wrongly designed and
|
|
||||||
// keeps the tab state inside of itself, so this seems like the only
|
|
||||||
// way we can tell it to switch tabs from outside after initial render.
|
|
||||||
function setTabIndex(tabIndex: number) {
|
|
||||||
const tabHref = document.getElementById(`tab-${tabIndex}`)
|
|
||||||
if (tabHref) {
|
|
||||||
tabHref.click()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LinkPage() {
|
export default function LinkPage() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const links = useUserManalinks(user?.id ?? '')
|
const links = useUserManalinks(user?.id ?? '')
|
||||||
|
@ -58,163 +46,28 @@ export default function LinkPage() {
|
||||||
<Page>
|
<Page>
|
||||||
<SEO
|
<SEO
|
||||||
title="Manalinks"
|
title="Manalinks"
|
||||||
description="Send mana to anyone via link!"
|
description="Send M$ to others with a link, even if they don't have a Manifold account yet!"
|
||||||
url="/send"
|
url="/send"
|
||||||
/>
|
/>
|
||||||
<Col className="w-full px-8">
|
<Col className="w-full px-8">
|
||||||
|
<Row className="items-center justify-between">
|
||||||
<Title text="Manalinks" />
|
<Title text="Manalinks" />
|
||||||
<Tabs
|
{user && (
|
||||||
labelClassName={'pb-2 pt-1 '}
|
<CreateLinksButton
|
||||||
defaultIndex={0}
|
|
||||||
tabs={[
|
|
||||||
{
|
|
||||||
title: 'Create a link',
|
|
||||||
content: (
|
|
||||||
<CreateManalinkForm
|
|
||||||
user={user}
|
user={user}
|
||||||
onCreate={async (newManalink) => {
|
|
||||||
const slug = await createManalink({
|
|
||||||
fromId: user.id,
|
|
||||||
amount: newManalink.amount,
|
|
||||||
expiresTime: newManalink.expiresTime,
|
|
||||||
maxUses: newManalink.maxUses,
|
|
||||||
message: newManalink.message,
|
|
||||||
})
|
|
||||||
setTabIndex(1)
|
|
||||||
setHighlightedSlug(slug || '')
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Unclaimed links',
|
|
||||||
content: (
|
|
||||||
<LinksTable
|
|
||||||
links={unclaimedLinks}
|
|
||||||
highlightedSlug={highlightedSlug}
|
highlightedSlug={highlightedSlug}
|
||||||
|
setHighlightedSlug={setHighlightedSlug}
|
||||||
/>
|
/>
|
||||||
),
|
)}
|
||||||
},
|
</Row>
|
||||||
// TODO: we have no use case for this atm and it's also really inefficient
|
|
||||||
// {
|
|
||||||
// title: 'Claimed',
|
|
||||||
// content: <ClaimsList txns={manalinkTxns} />,
|
|
||||||
// },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Page>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function CreateManalinkForm(props: {
|
|
||||||
user: User
|
|
||||||
onCreate: (m: ManalinkInfo) => Promise<void>
|
|
||||||
}) {
|
|
||||||
const { user, onCreate } = props
|
|
||||||
const [isCreating, setIsCreating] = useState(false)
|
|
||||||
const [newManalink, setNewManalink] = useState<ManalinkInfo>({
|
|
||||||
expiresTime: null,
|
|
||||||
amount: 100,
|
|
||||||
maxUses: 1,
|
|
||||||
uses: 0,
|
|
||||||
message: '',
|
|
||||||
})
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>
|
<p>
|
||||||
You can use manalinks to send mana to other people, even if they
|
You can use manalinks to send mana to other people, even if they
|
||||||
don't yet have a Manifold account.
|
don't yet have a Manifold account.
|
||||||
</p>
|
</p>
|
||||||
<form
|
<Subtitle text="Your Manalinks" />
|
||||||
className="my-5"
|
<LinksTable links={unclaimedLinks} highlightedSlug={highlightedSlug} />
|
||||||
onSubmit={(e) => {
|
</Col>
|
||||||
e.preventDefault()
|
</Page>
|
||||||
setIsCreating(true)
|
|
||||||
onCreate(newManalink).finally(() => setIsCreating(false))
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex flex-row flex-wrap gap-x-5 gap-y-2">
|
|
||||||
<div className="form-control flex-auto">
|
|
||||||
<label className="label">Amount</label>
|
|
||||||
<input
|
|
||||||
className="input"
|
|
||||||
type="number"
|
|
||||||
value={newManalink.amount}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewManalink((m) => {
|
|
||||||
return { ...m, amount: parseInt(e.target.value) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
></input>
|
|
||||||
</div>
|
|
||||||
<div className="form-control flex-auto">
|
|
||||||
<label className="label">Uses</label>
|
|
||||||
<input
|
|
||||||
className="input"
|
|
||||||
type="number"
|
|
||||||
value={newManalink.maxUses ?? ''}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewManalink((m) => {
|
|
||||||
return { ...m, maxUses: parseInt(e.target.value) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
></input>
|
|
||||||
</div>
|
|
||||||
<div className="form-control flex-auto">
|
|
||||||
<label className="label">Expires at</label>
|
|
||||||
<input
|
|
||||||
value={
|
|
||||||
newManalink.expiresTime != null
|
|
||||||
? dayjs(newManalink.expiresTime).format('YYYY-MM-DDTHH:mm')
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
className="input"
|
|
||||||
type="datetime-local"
|
|
||||||
onChange={(e) => {
|
|
||||||
setNewManalink((m) => {
|
|
||||||
return {
|
|
||||||
...m,
|
|
||||||
expiresTime: e.target.value
|
|
||||||
? dayjs(e.target.value, 'YYYY-MM-DDTHH:mm').valueOf()
|
|
||||||
: null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
></input>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="form-control w-full">
|
|
||||||
<label className="label">Message</label>
|
|
||||||
<Textarea
|
|
||||||
placeholder={`From ${user.name}`}
|
|
||||||
className="input input-bordered resize-none"
|
|
||||||
autoFocus
|
|
||||||
value={newManalink.message}
|
|
||||||
onChange={(e) =>
|
|
||||||
setNewManalink((m) => {
|
|
||||||
return { ...m, message: e.target.value }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className={clsx('btn mt-5', isCreating ? 'loading disabled' : '')}
|
|
||||||
>
|
|
||||||
{isCreating ? '' : 'Create'}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<Title text="Preview" />
|
|
||||||
<p>This is what the person you send the link to will see:</p>
|
|
||||||
<ManalinkCard
|
|
||||||
className="my-5"
|
|
||||||
defaultMessage={`From ${user.name}`}
|
|
||||||
info={newManalink}
|
|
||||||
isClaiming={false}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,8 +187,8 @@ function LinkSummaryRow(props: {
|
||||||
}) {
|
}) {
|
||||||
const { link, highlight, expanded, onToggle } = props
|
const { link, highlight, expanded, onToggle } = props
|
||||||
const className = clsx(
|
const className = clsx(
|
||||||
'whitespace-nowrap text-sm hover:cursor-pointer',
|
'whitespace-nowrap text-sm hover:cursor-pointer text-gray-500 hover:bg-sky-50 bg-white',
|
||||||
highlight ? 'bg-primary' : 'text-gray-500 hover:bg-sky-50 bg-white'
|
highlight ? 'bg-indigo-100 rounded-lg animate-pulse' : ''
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<tr id={link.slug} key={link.slug} className={className}>
|
<tr id={link.slug} key={link.slug} className={className}>
|
||||||
|
@ -350,7 +203,7 @@ function LinkSummaryRow(props: {
|
||||||
<td className="px-5 py-4 font-medium text-gray-900">
|
<td className="px-5 py-4 font-medium text-gray-900">
|
||||||
{formatMoney(link.amount)}
|
{formatMoney(link.amount)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-5 py-4">{getLinkUrl(link.slug)}</td>
|
<td className="px-5 py-4">{getManalinkUrl(link.slug)}</td>
|
||||||
<td className="px-5 py-4">{link.claimedUserIds.length}</td>
|
<td className="px-5 py-4">{link.claimedUserIds.length}</td>
|
||||||
<td className="px-5 py-4">{link.maxUses == null ? '∞' : link.maxUses}</td>
|
<td className="px-5 py-4">{link.maxUses == null ? '∞' : link.maxUses}</td>
|
||||||
<td className="px-5 py-4">
|
<td className="px-5 py-4">
|
||||||
|
@ -365,6 +218,7 @@ function LinksTable(props: { links: Manalink[]; highlightedSlug?: string }) {
|
||||||
return links.length == 0 ? (
|
return links.length == 0 ? (
|
||||||
<p>You don't currently have any outstanding manalinks.</p>
|
<p>You don't currently have any outstanding manalinks.</p>
|
||||||
) : (
|
) : (
|
||||||
|
<div className="overflow-scroll">
|
||||||
<table className="w-full divide-y divide-gray-300 rounded-lg border border-gray-200">
|
<table className="w-full divide-y divide-gray-300 rounded-lg border border-gray-200">
|
||||||
<thead className="bg-gray-50 text-left text-sm font-semibold text-gray-900">
|
<thead className="bg-gray-50 text-left text-sm font-semibold text-gray-900">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -378,9 +232,13 @@ function LinksTable(props: { links: Manalink[]; highlightedSlug?: string }) {
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-gray-200 bg-white">
|
<tbody className="divide-y divide-gray-200 bg-white">
|
||||||
{links.map((link) => (
|
{links.map((link) => (
|
||||||
<LinkTableRow link={link} highlight={link.slug === highlightedSlug} />
|
<LinkTableRow
|
||||||
|
link={link}
|
||||||
|
highlight={link.slug === highlightedSlug}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user