initial commit

This commit is contained in:
padreug 2025-10-22 12:33:45 +02:00
commit 95b8af2360
15 changed files with 1519 additions and 0 deletions

209
templates/castle/index.html Normal file
View file

@ -0,0 +1,209 @@
{% extends "base.html" %}
{% from "macros.jinja" import window_vars with context %}
{% block scripts %}
{{ window_vars(user) }}
<script src="{{ static_url_for('castle/static', path='js/index.js') }}"></script>
{% endblock %}
{% block page %}
<div class="row q-col-gutter-md">
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
<q-card>
<q-card-section>
<h5 class="q-my-none">🏰 Castle Accounting</h5>
<p>Track expenses, receivables, and balances for the collective</p>
</q-card-section>
</q-card>
<!-- User Balance Card -->
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-sm">
<div class="col">
<h6 class="q-my-none">Your Balance</h6>
</div>
<div class="col-auto">
<q-btn flat round icon="refresh" @click="loadBalance">
<q-tooltip>Refresh balance</q-tooltip>
</q-btn>
</div>
</div>
<div v-if="balance !== null">
<div class="text-h4" :class="balance.balance >= 0 ? 'text-positive' : 'text-negative'">
{% raw %}{{ formatSats(Math.abs(balance.balance)) }} sats{% endraw %}
</div>
<div class="text-subtitle2">
{% raw %}{{ balance.balance >= 0 ? 'Castle owes you' : 'You owe Castle' }}{% endraw %}
</div>
<q-btn
v-if="balance.balance < 0"
color="primary"
class="q-mt-md"
@click="showPayBalanceDialog"
>
Pay Balance
</q-btn>
</div>
<div v-else>
<q-spinner color="primary" size="md" />
Loading balance...
</div>
</q-card-section>
</q-card>
<!-- Quick Actions -->
<q-card>
<q-card-section>
<h6 class="q-my-none q-mb-md">Quick Actions</h6>
<div class="row q-gutter-sm">
<q-btn color="primary" @click="expenseDialog.show = true">
Add Expense
</q-btn>
<q-btn color="secondary" @click="loadTransactions">
View Transactions
</q-btn>
</div>
</q-card-section>
</q-card>
<!-- Recent Transactions -->
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-sm">
<div class="col">
<h6 class="q-my-none">Recent Transactions</h6>
</div>
<div class="col-auto">
<q-btn flat round icon="refresh" @click="loadTransactions">
<q-tooltip>Refresh transactions</q-tooltip>
</q-btn>
</div>
</div>
<q-list v-if="transactions.length > 0" separator>
<q-item v-for="entry in transactions" :key="entry.id">
<q-item-section>
<q-item-label>{% raw %}{{ entry.description }}{% endraw %}</q-item-label>
<q-item-label caption>
{% raw %}{{ formatDate(entry.entry_date) }}{% endraw %}
</q-item-label>
</q-item-section>
<q-item-section side>
<q-item-label>{% raw %}{{ formatSats(getTotalAmount(entry)) }} sats{% endraw %}</q-item-label>
</q-item-section>
</q-item>
</q-list>
<div v-else class="text-center q-pa-md text-grey">
No transactions yet
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
<!-- Chart of Accounts -->
<q-card>
<q-card-section>
<h6 class="q-my-none q-mb-md">Chart of Accounts</h6>
<q-list dense v-if="accounts.length > 0">
<q-item v-for="account in accounts" :key="account.id">
<q-item-section>
<q-item-label>{% raw %}{{ account.name }}{% endraw %}</q-item-label>
<q-item-label caption>{% raw %}{{ account.account_type }}{% endraw %}</q-item-label>
</q-item-section>
</q-item>
</q-list>
<div v-else>
<q-spinner color="primary" size="sm" />
Loading accounts...
</div>
</q-card-section>
</q-card>
</div>
</div>
<!-- Add Expense Dialog -->
<q-dialog v-model="expenseDialog.show" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-card-section>
<h6 class="q-my-none q-mb-md">Add Expense</h6>
<q-form @submit="submitExpense" class="q-gutter-md">
<q-input
v-model="expenseDialog.description"
label="Description"
placeholder="e.g., Groceries for the house"
required
/>
<q-input
v-model.number="expenseDialog.amount"
type="number"
label="Amount (sats)"
required
min="1"
/>
<q-select
v-model="expenseDialog.expenseAccount"
:options="expenseAccounts"
option-label="name"
option-value="id"
emit-value
map-options
label="Expense Category"
required
/>
<q-select
v-model="expenseDialog.isEquity"
:options="[
{label: 'Liability (Castle owes me)', value: false},
{label: 'Equity (My contribution)', value: true}
]"
option-label="label"
option-value="value"
emit-value
map-options
label="Type"
required
/>
<q-input
v-model="expenseDialog.reference"
label="Reference (optional)"
placeholder="e.g., Receipt #123"
/>
<div class="row q-mt-lg">
<q-btn unelevated color="primary" type="submit" :loading="expenseDialog.loading">
Submit Expense
</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</div>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
<!-- Pay Balance Dialog -->
<q-dialog v-model="payDialog.show" position="top">
<q-card class="q-pa-lg lnbits__dialog-card">
<q-card-section>
<h6 class="q-my-none q-mb-md">Pay Balance</h6>
<p v-if="balance">Amount owed: <strong>{% raw %}{{ formatSats(Math.abs(balance.balance)) }}{% endraw %} sats</strong></p>
<q-form @submit="submitPayment" class="q-gutter-md">
<q-input
v-model.number="payDialog.amount"
type="number"
label="Amount to pay (sats)"
required
min="1"
:max="balance ? Math.abs(balance.balance) : 0"
/>
<div class="row q-mt-lg">
<q-btn unelevated color="primary" type="submit" :loading="payDialog.loading">
Generate Invoice
</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</div>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
{% endblock %}