Add frontend for market settlement
This commit is contained in:
parent
c9bce83891
commit
51e204b64a
|
@ -28,7 +28,7 @@ func (db *DB) CreateMarket(tx *sql.Tx, ctx context.Context, market *Market) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) FetchMarket(marketId int, market *Market) error {
|
func (db *DB) FetchMarket(marketId int, market *Market) error {
|
||||||
if err := db.QueryRow("SELECT id, description, end_date FROM markets WHERE id = $1", marketId).Scan(&market.Id, &market.Description, &market.EndDate); err != nil {
|
if err := db.QueryRow("SELECT id, description, end_date, pubkey FROM markets WHERE id = $1", marketId).Scan(&market.Id, &market.Description, &market.EndDate, &market.Pubkey); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -29,7 +29,7 @@ type (
|
||||||
Id Serial `json:"id"`
|
Id Serial `json:"id"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
EndDate time.Time `json:"endDate"`
|
EndDate time.Time `json:"endDate"`
|
||||||
Pubkey string
|
Pubkey string `json:"pubkey"`
|
||||||
InvoiceId UUID
|
InvoiceId UUID
|
||||||
}
|
}
|
||||||
Share struct {
|
Share struct {
|
||||||
|
|
|
@ -44,6 +44,7 @@ func HandleMarket(sc context.ServerContext) echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
data = map[string]any{
|
data = map[string]any{
|
||||||
"Id": market.Id,
|
"Id": market.Id,
|
||||||
|
"Pubkey": market.Pubkey,
|
||||||
"Description": market.Description,
|
"Description": market.Description,
|
||||||
"Shares": shares,
|
"Shares": shares,
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,27 +15,32 @@
|
||||||
<StyledLink :to="'/market/' + marketId + '/form'">form</StyledLink>
|
<StyledLink :to="'/market/' + marketId + '/form'">form</StyledLink>
|
||||||
<StyledLink :to="'/market/' + marketId + '/orders'">orders</StyledLink>
|
<StyledLink :to="'/market/' + marketId + '/orders'">orders</StyledLink>
|
||||||
<StyledLink :to="'/market/' + marketId + '/stats'">stats</StyledLink>
|
<StyledLink :to="'/market/' + marketId + '/stats'">stats</StyledLink>
|
||||||
|
<StyledLink v-if="mine" :to="'/market/' + marketId + '/settings'"><i>settings</i></StyledLink>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<router-view />
|
<router-view :market="market" />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useSession } from '@/stores/session'
|
||||||
import StyledLink from '@/components/StyledLink'
|
import StyledLink from '@/components/StyledLink'
|
||||||
|
|
||||||
|
const session = useSession()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const marketId = route.params.id
|
const marketId = route.params.id
|
||||||
|
|
||||||
const market = ref(null)
|
const market = ref(null)
|
||||||
|
const mine = ref(false)
|
||||||
const url = '/api/market/' + marketId
|
const url = '/api/market/' + marketId
|
||||||
await fetch(url)
|
await fetch(url)
|
||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(body => {
|
.then(body => {
|
||||||
market.value = body
|
market.value = body
|
||||||
|
mine.value = market.value.Pubkey === session.pubkey
|
||||||
})
|
})
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
<template>
|
||||||
|
<div id="container2" class="mt-3 mx-3">
|
||||||
|
<div class="grid grid-cols-2 my-3 items-center">
|
||||||
|
<label>market settlement</label>
|
||||||
|
<div class="grid grid-cols-2 my-3">
|
||||||
|
<button :class="yesClass" class="label success font-mono mx-1" @click.prevent="() => click('YES')">YES</button>
|
||||||
|
<button :class="noClass" class="label error font-mono mx-1" @click.prevent="() => click('NO')">NO</button>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2 mb-3" v-if="selected">
|
||||||
|
<p><b>Are you sure you want to settle this market?</b></p>
|
||||||
|
<p>
|
||||||
|
This will cancel all pending orders and halt trading indefinitely.
|
||||||
|
Users with winning shares will receive 100 sats per winning share. Users with losing shares receive nothing.
|
||||||
|
</p>
|
||||||
|
<p class="red"><b>You cannot undo this action.</b></p>
|
||||||
|
</div>
|
||||||
|
<button class="col-span-2" v-if="selected" @click.prevent="confirm">confirm</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="err" class="red text-center">{{ err }}</div>
|
||||||
|
<div v-if="success" class="green text-center">{{ success }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { computed, defineProps, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps(['market'])
|
||||||
|
const market = ref(props.market)
|
||||||
|
|
||||||
|
const err = ref(null)
|
||||||
|
const success = ref(null)
|
||||||
|
|
||||||
|
const selected = ref(null)
|
||||||
|
const yesClass = computed(() => selected.value === 'YES' ? ['active'] : [])
|
||||||
|
const noClass = computed(() => selected.value === 'NO' ? ['active'] : [])
|
||||||
|
const click = (sel) => {
|
||||||
|
selected.value = selected.value === sel ? null : sel
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirm = async () => {
|
||||||
|
const sid = market.value.Shares.find(s => s.Description === selected.value).Id
|
||||||
|
const url = '/api/market/' + market.value.Id + '/settle'
|
||||||
|
const body = JSON.stringify({ sid })
|
||||||
|
try {
|
||||||
|
await fetch(url, { method: 'POST', headers: { 'Content-type': 'application/json' }, body })
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
textarea {
|
||||||
|
padding: 0 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: auto;
|
||||||
|
padding: 0.2em 1em
|
||||||
|
}
|
||||||
|
|
||||||
|
.success.active {
|
||||||
|
background-color: #35df8d;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error.active {
|
||||||
|
background-color: #ff7386;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container2 {
|
||||||
|
max-width: 33vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1024px) {
|
||||||
|
#container2 {
|
||||||
|
max-width: 80vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
#container2 {
|
||||||
|
max-width: 90vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 640px) {
|
||||||
|
#container2 {
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -21,6 +21,9 @@ button:disabled {
|
||||||
input {
|
input {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
textarea {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #8787a4;
|
color: #8787a4;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import UserOrders from '@/components/UserOrders'
|
||||||
import OrderForm from '@/components/OrderForm'
|
import OrderForm from '@/components/OrderForm'
|
||||||
import MarketOrders from '@/components/MarketOrders'
|
import MarketOrders from '@/components/MarketOrders'
|
||||||
import MarketStats from '@/components/MarketStats'
|
import MarketStats from '@/components/MarketStats'
|
||||||
|
import MarketSettings from '@/components/MarketSettings'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
@ -43,7 +44,8 @@ const routes = [
|
||||||
children: [
|
children: [
|
||||||
{ path: 'form', name: 'form', component: OrderForm },
|
{ path: 'form', name: 'form', component: OrderForm },
|
||||||
{ path: 'orders', name: 'market-orders', component: MarketOrders },
|
{ path: 'orders', name: 'market-orders', component: MarketOrders },
|
||||||
{ path: 'stats', name: 'market-stats', component: MarketStats }
|
{ path: 'stats', name: 'market-stats', component: MarketStats },
|
||||||
|
{ path: 'settings', name: 'market-settings', component: MarketSettings }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue