views_api wires the full task lifecycle: create / update / delete / list (per-wallet, public, admin-all) and the completion flow (claim / start / complete via POST, unclaim via DELETE, plus a "mine" lookup for the current user's claim on a task or specific occurrence). Auth model: tasks are owned by an LNbits wallet but signed with the wallet owner's account.pubkey — _wallet_pubkey resolves that pubkey at create time and refuses to create tasks for accounts that haven't generated a keypair yet, so we never publish a task we can't sign. Completions optimistically insert with a local hash, publish to Nostr, then re-insert under the actual event id so a later delete can find it. Static SPA: Quasar-UMD index.vue / index.js mirroring the events extension layout — wallet picker, task table, create/edit dialog with optional daily/weekly recurrence, plus an admin-only public_listing toggle. /tasks/:id and display.vue intentionally left out until the public task page lands. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
198 lines
5.9 KiB
Vue
198 lines
5.9 KiB
Vue
<template id="page-tasks">
|
|
<div class="row q-col-gutter-md">
|
|
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
|
|
<q-card v-if="isAdmin">
|
|
<q-card-section>
|
|
<div class="row items-center justify-between">
|
|
<div class="col">
|
|
<span class="text-subtitle1">Settings</span>
|
|
</div>
|
|
<div class="col-auto">
|
|
<q-toggle
|
|
v-model="settings.public_listing"
|
|
label="Public task listing"
|
|
@update:model-value="saveSettings"
|
|
></q-toggle>
|
|
</div>
|
|
</div>
|
|
</q-card-section>
|
|
</q-card>
|
|
|
|
<q-card>
|
|
<q-card-section>
|
|
<q-btn unelevated color="primary" @click="openTaskDialog"
|
|
>New Task</q-btn
|
|
>
|
|
</q-card-section>
|
|
</q-card>
|
|
|
|
<q-card>
|
|
<q-card-section>
|
|
<div class="row items-center no-wrap q-mb-md">
|
|
<div class="col">
|
|
<h5 class="text-subtitle1 q-my-none">Tasks</h5>
|
|
</div>
|
|
</div>
|
|
<q-table
|
|
dense
|
|
flat
|
|
:rows="tasks"
|
|
row-key="id"
|
|
:columns="tasksTable.columns"
|
|
v-model:pagination="tasksTable.pagination"
|
|
>
|
|
<template v-slot:body="props">
|
|
<q-tr :props="props">
|
|
<q-td auto-width>
|
|
<q-btn
|
|
flat
|
|
dense
|
|
size="xs"
|
|
@click="editTask(props.row)"
|
|
icon="edit"
|
|
color="light-blue"
|
|
></q-btn>
|
|
<q-btn
|
|
flat
|
|
dense
|
|
size="xs"
|
|
@click="deleteTask(props.row.id)"
|
|
icon="cancel"
|
|
color="pink"
|
|
class="q-ml-xs"
|
|
></q-btn>
|
|
</q-td>
|
|
<q-td v-for="col in props.cols" :key="col.name" :props="props">
|
|
<q-badge
|
|
v-if="col.name === 'recurrence' && col.value"
|
|
color="purple"
|
|
:label="col.value"
|
|
></q-badge>
|
|
<span v-else v-text="col.value"></span>
|
|
</q-td>
|
|
</q-tr>
|
|
</template>
|
|
</q-table>
|
|
</q-card-section>
|
|
</q-card>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
|
|
<q-card>
|
|
<q-card-section>
|
|
<h6 class="text-subtitle1 q-my-none">
|
|
{{SITE_TITLE}} Tasks extension
|
|
</h6>
|
|
</q-card-section>
|
|
<q-card-section class="q-pa-none">
|
|
<q-separator></q-separator>
|
|
<q-list>
|
|
<q-expansion-item
|
|
group="extras"
|
|
icon="info"
|
|
label="About"
|
|
:content-inset-level="0.5"
|
|
>
|
|
<q-card-section class="text-caption">
|
|
Recurring tasks and chore tracking, federated over Nostr
|
|
NIP-52 calendar events (kind 31922) and a task-status RSVP
|
|
variant (kind 31925).
|
|
</q-card-section>
|
|
</q-expansion-item>
|
|
</q-list>
|
|
</q-card-section>
|
|
</q-card>
|
|
</div>
|
|
|
|
<q-dialog v-model="taskDialog.show" position="top">
|
|
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
|
|
<q-form @submit="submitTask" class="q-gutter-md">
|
|
<q-select
|
|
filled
|
|
dense
|
|
emit-value
|
|
v-model="taskDialog.data.wallet"
|
|
:options="g.user.walletOptions"
|
|
label="Wallet *"
|
|
></q-select>
|
|
<q-input
|
|
filled
|
|
dense
|
|
v-model.trim="taskDialog.data.title"
|
|
label="Title *"
|
|
></q-input>
|
|
<q-input
|
|
filled
|
|
dense
|
|
v-model.trim="taskDialog.data.start_date"
|
|
label="Start date (YYYY-MM-DD) *"
|
|
placeholder="2026-05-13"
|
|
></q-input>
|
|
<q-input
|
|
filled
|
|
dense
|
|
v-model.trim="taskDialog.data.end_date"
|
|
label="End date (optional)"
|
|
></q-input>
|
|
<q-input
|
|
filled
|
|
dense
|
|
type="textarea"
|
|
v-model.trim="taskDialog.data.description"
|
|
label="Description"
|
|
></q-input>
|
|
<q-input
|
|
filled
|
|
dense
|
|
v-model.trim="taskDialog.data.location"
|
|
label="Location"
|
|
></q-input>
|
|
<q-select
|
|
filled
|
|
dense
|
|
v-model="taskDialog.data.recurrence_frequency"
|
|
:options="['', 'daily', 'weekly']"
|
|
label="Recurrence"
|
|
emit-value
|
|
></q-select>
|
|
<q-input
|
|
v-if="taskDialog.data.recurrence_frequency === 'weekly'"
|
|
filled
|
|
dense
|
|
v-model.trim="taskDialog.data.recurrence_day_of_week"
|
|
label="Weekday (e.g. monday)"
|
|
></q-input>
|
|
<q-input
|
|
v-if="taskDialog.data.recurrence_frequency"
|
|
filled
|
|
dense
|
|
v-model.trim="taskDialog.data.recurrence_end_date"
|
|
label="Recurrence end date (optional)"
|
|
></q-input>
|
|
|
|
<div class="row q-mt-lg">
|
|
<q-btn
|
|
v-if="taskDialog.data.id"
|
|
unelevated
|
|
color="primary"
|
|
type="submit"
|
|
:disable="!canSubmit"
|
|
>Update Task</q-btn
|
|
>
|
|
<q-btn
|
|
v-else
|
|
unelevated
|
|
color="primary"
|
|
type="submit"
|
|
:disable="!canSubmit"
|
|
>Create Task</q-btn
|
|
>
|
|
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
|
|
>Cancel</q-btn
|
|
>
|
|
</div>
|
|
</q-form>
|
|
</q-card>
|
|
</q-dialog>
|
|
</div>
|
|
</template>
|