feat: event proposal and approval workflow #9
2 changed files with 131 additions and 2 deletions
feat: add pending approvals UI to admin panel
Some checks failed
lint.yml / feat: add pending approvals UI to admin panel (pull_request) Failing after 0s
Some checks failed
lint.yml / feat: add pending approvals UI to admin panel (pull_request) Failing after 0s
- Separate "Pending Approvals" card with approve/reject buttons (appears only when proposed events exist) - Status badge column in events table (green/orange/red) - Inline approve/reject buttons on proposed events in table - Following castle extension's approval UI pattern Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
commit
702ab70559
|
|
@ -73,7 +73,8 @@ window.app = Vue.createApp({
|
|||
field: 'sold'
|
||||
},
|
||||
{name: 'info', align: 'left', label: 'Info', field: 'info'},
|
||||
{name: 'banner', align: 'left', label: 'Banner', field: 'banner'}
|
||||
{name: 'banner', align: 'left', label: 'Banner', field: 'banner'},
|
||||
{name: 'status', align: 'left', label: 'Status', field: 'status'}
|
||||
],
|
||||
pagination: {
|
||||
rowsPerPage: 10
|
||||
|
|
@ -112,7 +113,56 @@ window.app = Vue.createApp({
|
|||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pendingEvents() {
|
||||
return this.events.filter(e => e.status === 'proposed')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
approveEvent(eventId) {
|
||||
LNbits.utils
|
||||
.confirmDialog('Approve this event?')
|
||||
.onOk(() => {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/events/api/v1/events/' + eventId + '/approve',
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(() => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Event approved'
|
||||
})
|
||||
this.getEvents()
|
||||
})
|
||||
.catch(err => {
|
||||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
rejectEvent(eventId) {
|
||||
LNbits.utils
|
||||
.confirmDialog('Reject this event?')
|
||||
.onOk(() => {
|
||||
LNbits.api
|
||||
.request(
|
||||
'PUT',
|
||||
'/events/api/v1/events/' + eventId + '/reject',
|
||||
this.g.user.wallets[0].adminkey
|
||||
)
|
||||
.then(() => {
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Event rejected'
|
||||
})
|
||||
this.getEvents()
|
||||
})
|
||||
.catch(err => {
|
||||
LNbits.utils.notifyApiError(err)
|
||||
})
|
||||
})
|
||||
},
|
||||
getTickets() {
|
||||
LNbits.api
|
||||
.request(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,58 @@
|
|||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<!-- Pending Event Approvals -->
|
||||
<q-card v-if="pendingEvents.length > 0">
|
||||
<q-card-section>
|
||||
<div class="row items-center no-wrap q-mb-md">
|
||||
<div class="col">
|
||||
<h5 class="text-subtitle1 q-my-none">
|
||||
<q-icon name="pending" color="orange" class="q-mr-sm" />
|
||||
Pending Approvals
|
||||
<q-badge color="orange" :label="pendingEvents.length" class="q-ml-sm" />
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<q-list separator>
|
||||
<q-item v-for="event in pendingEvents" :key="event.id">
|
||||
<q-item-section>
|
||||
<q-item-label>{% raw %}{{ event.name }}{% endraw %}</q-item-label>
|
||||
<q-item-label caption>
|
||||
{% raw %}{{ event.event_start_date }}{% endraw %}
|
||||
—
|
||||
{% raw %}{{ event.info.substring(0, 80) }}{% endraw %}{% raw %}{{ event.info.length > 80 ? '...' : '' }}{% endraw %}
|
||||
</q-item-label>
|
||||
<q-item-label caption>
|
||||
{% raw %}{{ event.amount_tickets }}{% endraw %} tickets •
|
||||
{% raw %}{{ event.price_per_ticket }}{% endraw %} {% raw %}{{ event.currency }}{% endraw %}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side>
|
||||
<div class="row q-gutter-sm">
|
||||
<q-btn
|
||||
dense
|
||||
color="green"
|
||||
icon="check_circle"
|
||||
label="Approve"
|
||||
size="sm"
|
||||
@click="approveEvent(event.id)"
|
||||
/>
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
color="red"
|
||||
icon="block"
|
||||
label="Reject"
|
||||
size="sm"
|
||||
@click="rejectEvent(event.id)"
|
||||
/>
|
||||
</div>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
<div class="row items-center no-wrap q-mb-md">
|
||||
|
|
@ -76,9 +128,36 @@
|
|||
></q-btn>
|
||||
</q-td>
|
||||
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
||||
<span v-text="col.value"></span>
|
||||
<q-badge
|
||||
v-if="col.name === 'status'"
|
||||
:color="col.value === 'approved' ? 'green' : col.value === 'proposed' ? 'orange' : 'red'"
|
||||
:label="col.value"
|
||||
/>
|
||||
<span v-else v-text="col.value"></span>
|
||||
</q-td>
|
||||
<q-td auto-width>
|
||||
<q-btn
|
||||
v-if="props.row.status === 'proposed'"
|
||||
flat
|
||||
dense
|
||||
size="xs"
|
||||
@click="approveEvent(props.row.id)"
|
||||
icon="check_circle"
|
||||
color="green"
|
||||
>
|
||||
<q-tooltip>Approve</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
v-if="props.row.status === 'proposed'"
|
||||
flat
|
||||
dense
|
||||
size="xs"
|
||||
@click="rejectEvent(props.row.id)"
|
||||
icon="block"
|
||||
color="red"
|
||||
>
|
||||
<q-tooltip>Reject</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue