- Planned expenses: dropdown with Edit/Delete, inline edit, clone from other years - Move Logout button from navbar to Settings danger zone - Navbar reduced to 5 buttons - Apply |money filter to all input values (no trailing .00)
357 lines
16 KiB
HTML
357 lines
16 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load money %}
|
|
|
|
{% block title %}Kakebo - Month{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="px-4 py-6 max-w-2xl mx-auto">
|
|
<h1 class="text-2xl font-bold mb-4">Month</h1>
|
|
|
|
<div class="flex w-full items-center justify-between mb-8 py-3">
|
|
<a data-liveview-function="navigate"
|
|
data-action="click->page#run"
|
|
data-data-url="/month/?offset={{ prev_offset }}"
|
|
class="btn btn-ghost btn-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
|
</svg>
|
|
</a>
|
|
<span class="text-base-content/70 font-medium">{{ current_month|date:"F Y" }}</span>
|
|
{% if is_current_month %}
|
|
<span class="btn btn-ghost btn-sm btn-disabled opacity-0">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
</svg>
|
|
</span>
|
|
{% else %}
|
|
<a data-liveview-function="navigate"
|
|
data-action="click->page#run"
|
|
data-data-url="/month/?offset={{ next_offset }}"
|
|
class="btn btn-ghost btn-sm">
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
</svg>
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{# 1. Month start #}
|
|
<h2 class="text-xl font-semibold mb-4">Month start</h2>
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<div class="flex flex-wrap justify-between items-center gap-2 mb-2">
|
|
<h3 class="card-title text-lg">Income</h3>
|
|
<form data-liveview-function="clone_income"
|
|
data-action="submit->page#run"
|
|
class="flex items-center gap-2">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<select name="clone_source" class="select select-bordered select-sm" {% if not clone_months %}disabled{% endif %}>
|
|
{% for cy, cm in clone_months %}
|
|
<option value="{{ cy }}-{{ cm }}" {% if cy == prev_year and cm == prev_month %}selected{% endif %}>
|
|
{{ cm }}/{{ cy }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button type="submit" class="btn btn-outline btn-sm" {% if not clone_months %}disabled{% endif %}>Clone</button>
|
|
</form>
|
|
</div>
|
|
<div id="income-table">
|
|
{% include "pages/monthly/partials/income_table.html" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<div class="flex flex-wrap justify-between items-center gap-2 mb-2">
|
|
<h3 class="card-title text-lg">Fixed expenses</h3>
|
|
<form data-liveview-function="clone_fixed_expenses"
|
|
data-action="submit->page#run"
|
|
class="flex items-center gap-2">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<select name="clone_fe_source" class="select select-bordered select-sm" {% if not clone_fe_months %}disabled{% endif %}>
|
|
{% for cy, cm in clone_fe_months %}
|
|
<option value="{{ cy }}-{{ cm }}" {% if cy == prev_year and cm == prev_month %}selected{% endif %}>
|
|
{{ cm }}/{{ cy }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button type="submit" class="btn btn-outline btn-sm" {% if not clone_fe_months %}disabled{% endif %}>Clone</button>
|
|
</form>
|
|
</div>
|
|
<div id="fixed-expenses-table">
|
|
{% include "pages/monthly/partials/fixed_expenses_table.html" with entries=fe_entries total=fe_total %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if planned_expenses %}
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<h3 class="card-title text-lg mb-2">Planned expenses</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Concept</th>
|
|
<th class="text-right">Amount</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for pe in planned_expenses %}
|
|
<tr>
|
|
<td>{{ pe.concept }}</td>
|
|
<td class="text-right whitespace-nowrap">{{ pe.amount|money }} €</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr class="font-bold">
|
|
<td>Total</td>
|
|
<td class="text-right">{{ planned_total|money }} €</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-8">
|
|
<div class="card-body">
|
|
<h3 class="card-title text-lg mb-4">Your budget</h3>
|
|
<p class="text-lg mb-4">
|
|
Your monthly budget is <span id="budget-amount" class="font-bold">{{ budget|money }} €</span>
|
|
</p>
|
|
<div class="flex items-center gap-2">
|
|
<span>I want to save</span>
|
|
<form data-liveview-function="save_savings_target"
|
|
data-action="input->page#run"
|
|
data-liveview-debounce="300"
|
|
class="inline-flex items-center gap-2">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<input type="text"
|
|
name="savings_amount"
|
|
inputmode="decimal"
|
|
value="{% if savings_target %}{{ savings_target|money }}{% endif %}"
|
|
placeholder="0,00"
|
|
class="input input-bordered input-sm w-28 text-right">
|
|
</form>
|
|
<span>€</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<h3 class="card-title text-lg mb-2">What are your monthly goals?</h3>
|
|
<div id="goals-list">
|
|
{% include "pages/monthly/partials/goals_list.html" with items=goals kind="goal" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-8">
|
|
<div class="card-body">
|
|
<h3 class="card-title text-lg mb-2">And your promises?</h3>
|
|
<div id="promises-list">
|
|
{% include "pages/monthly/partials/goals_list.html" with items=promises kind="promise" %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
{# 2. Month end #}
|
|
<h2 class="text-xl font-semibold mb-4">Month end</h2>
|
|
|
|
{# Weekly expenses table #}
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<h3 class="card-title text-lg mb-2">Your weekly expenses</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-sm">
|
|
<tbody>
|
|
{% for wt in month_end.weekly_totals %}
|
|
<tr>
|
|
<td>Week #{{ forloop.counter }}</td>
|
|
<td class="text-right">{{ wt|money }} €</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr class="font-bold">
|
|
<td>Total</td>
|
|
<td class="text-right">{{ month_end.monthly_expense_total|money }} €</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# 3. Expense summary #}
|
|
<h2 class="text-xl font-semibold mb-4">Expense summary</h2>
|
|
|
|
{% for cat_data in month_end.category_weeks %}
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body p-4">
|
|
<h3 class="card-title text-lg mb-2">{{ cat_data.category.name }}</h3>
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-sm">
|
|
<tbody>
|
|
{% for val in cat_data.weekly %}
|
|
<tr>
|
|
<td>Week #{{ forloop.counter }}</td>
|
|
<td class="text-right">{{ val|money }} €</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr class="font-bold">
|
|
<td>Total</td>
|
|
<td class="text-right">{{ cat_data.total|money }} €</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<p class="text-base-content/50 text-center">No expenses this month</p>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{# Budget result #}
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body">
|
|
<div class="grid grid-cols-3 text-center gap-4">
|
|
<div>
|
|
<p class="text-sm text-base-content/60">Your initial budget</p>
|
|
<p class="text-xl font-bold">{{ budget|money }} €</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-sm text-base-content/60">Total monthly expenses</p>
|
|
<p class="text-xl font-bold">{{ month_end.monthly_expense_total|money }} €</p>
|
|
</div>
|
|
<div>
|
|
<p class="text-sm text-base-content/60">Your savings!</p>
|
|
<p class="text-xl font-bold {% if savings_result >= 0 %}text-success{% else %}text-error{% endif %}">{{ savings_result|money }} €</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
{# Assessment #}
|
|
<h2 class="text-xl font-semibold mb-4">Self-assessment</h2>
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-4">
|
|
<div class="card-body space-y-6">
|
|
<div>
|
|
<p class="font-medium mb-2">Did you achieve your monthly goals?</p>
|
|
<form data-liveview-function="save_assessment"
|
|
data-action="change->page#run"
|
|
class="flex gap-4">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<input type="hidden" name="assessment_field" value="goals_achieved">
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="yes" class="radio radio-sm" {% if note.goals_achieved == "yes" %}checked{% endif %}> Yes
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="no" class="radio radio-sm" {% if note.goals_achieved == "no" %}checked{% endif %}> No
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="almost" class="radio radio-sm" {% if note.goals_achieved == "almost" %}checked{% endif %}> Almost
|
|
</label>
|
|
</form>
|
|
</div>
|
|
|
|
<div>
|
|
<p class="font-medium mb-2">Did you keep your promises?</p>
|
|
<form data-liveview-function="save_assessment"
|
|
data-action="change->page#run"
|
|
class="flex gap-4">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<input type="hidden" name="assessment_field" value="promises_kept">
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="yes" class="radio radio-sm" {% if note.promises_kept == "yes" %}checked{% endif %}> Yes
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="no" class="radio radio-sm" {% if note.promises_kept == "no" %}checked{% endif %}> No
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="almost" class="radio radio-sm" {% if note.promises_kept == "almost" %}checked{% endif %}> Almost
|
|
</label>
|
|
</form>
|
|
</div>
|
|
|
|
<div>
|
|
<p class="font-medium mb-2">Did you keep your initial savings intact?</p>
|
|
<form data-liveview-function="save_assessment"
|
|
data-action="change->page#run"
|
|
class="flex gap-4">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<input type="hidden" name="assessment_field" value="savings_kept">
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="yes" class="radio radio-sm" {% if note.savings_kept == "yes" %}checked{% endif %}> Yes
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="no" class="radio radio-sm" {% if note.savings_kept == "no" %}checked{% endif %}> No
|
|
</label>
|
|
<label class="label cursor-pointer gap-2">
|
|
<input type="radio" name="assessment_value" value="almost" class="radio radio-sm" {% if note.savings_kept == "almost" %}checked{% endif %}> Almost
|
|
</label>
|
|
</form>
|
|
</div>
|
|
|
|
<div>
|
|
<p class="font-medium mb-2">Reflect on your successes, efforts, failures...</p>
|
|
<form data-liveview-function="save_reflection"
|
|
data-action="input->page#run"
|
|
data-liveview-debounce="500">
|
|
<input type="hidden" name="year" value="{{ year }}">
|
|
<input type="hidden" name="month" value="{{ month }}">
|
|
<textarea name="reflection"
|
|
class="textarea textarea-bordered w-full h-32"
|
|
placeholder="Write your reflection...">{{ note.reflection }}</textarea>
|
|
<div class="mt-2">
|
|
<span id="reflection-status"></span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
{# 4. Notes #}
|
|
<h2 class="text-xl font-semibold mb-4">Notes</h2>
|
|
|
|
<div class="card bg-base-100 shadow-sm mb-6">
|
|
<div class="card-body">
|
|
<form data-liveview-function="save_monthly_note"
|
|
data-action="input->page#run"
|
|
data-liveview-debounce="500">
|
|
<input type="hidden" name="year" value="{{ current_month|date:'Y' }}">
|
|
<input type="hidden" name="month" value="{{ current_month|date:'n' }}">
|
|
<textarea name="notes"
|
|
class="textarea textarea-bordered w-full h-40"
|
|
placeholder="Write your notes for this month...">{{ note.text }}</textarea>
|
|
<div class="mt-2">
|
|
<span id="notes-status"></span>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|