Layout ändringar och meal fixar
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -4,230 +4,229 @@
|
||||
@{
|
||||
ViewData["Title"] = "Budget";
|
||||
}
|
||||
<div ng-app="budgetApp" ng-controller="BudgetController" class="budget-page" ng-if="!loading">
|
||||
<div class="budget-header" style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;">
|
||||
<div class="month-nav-bar" style="display: flex; align-items: center; gap: 10px; position: relative;">
|
||||
<button class="nav-button" ng-click="previousMonth()">←</button>
|
||||
<span class="month-label" ng-click="showMonthPicker = !showMonthPicker" style="cursor: pointer;">
|
||||
{{ selectedMonthName }} {{ selectedYear }}
|
||||
</span>
|
||||
<button class="nav-button" ng-click="nextMonth()">→</button>
|
||||
|
||||
<div ng-app="budgetApp" ng-controller="BudgetController" class="budget-page" ng-init="loadBudget()">
|
||||
<div class="budget-header" style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;">
|
||||
<div class="month-nav-bar" style="display: flex; align-items: center; gap: 10px; position: relative;">
|
||||
<button class="nav-button" ng-click="previousMonth()">←</button>
|
||||
<span class="month-label" ng-click="showMonthPicker = !showMonthPicker" style="cursor: pointer;">
|
||||
{{ selectedMonthName }} {{ selectedYear }}
|
||||
</span>
|
||||
<button class="nav-button" ng-click="nextMonth()">→</button>
|
||||
|
||||
<div class="month-picker-dropdown" ng-show="showMonthPicker" style="position: absolute; top: 100%; left: 50%; transform: translateX(-50%); background: white; border: 1px solid #ccc; padding: 10px; border-radius: 8px; z-index: 1000; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
|
||||
<select ng-model="tempMonth" ng-options="month for month in monthNames" style="margin-bottom: 8px;"></select><br>
|
||||
<input type="number" ng-model="tempYear" placeholder="År" style="width: 100%; margin-bottom: 8px;" />
|
||||
<button class="nav-button" ng-click="applyMonthSelection()">Välj</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-container" ng-class="{ 'open': menuOpen }">
|
||||
<button class="icon-button" ng-click="toggleMenu($event)">
|
||||
<i class="fa fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu" ng-show="menuOpen">
|
||||
<button ng-click="copyPreviousMonthSafe()">Kopiera föregående månad</button>
|
||||
|
||||
<button ng-click="deleteMonth(); menuOpen = false;" class="danger">Ta bort hela månaden</button>
|
||||
<button ng-click="createNewCategory(); menuOpen = false;">Lägg till ny kategori</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="budget-overview-row" ng-if="budget && budget.categories.length > 0">
|
||||
<!-- Vänster: Summering -->
|
||||
<div class="budget-summary-box compact">
|
||||
<h3>Sammanställning</h3>
|
||||
<ul class="summary-list">
|
||||
<li><span>💰 Inkomst:</span> {{ getTotalIncome() | number:0 }} kr</li>
|
||||
<li><span>💸 Utgift:</span> {{ getTotalExpense() | number:0 }} kr</li>
|
||||
<li><span>🏦 Sparande:</span> {{ getTotalSaving() | number:0 }} kr</li>
|
||||
<p>
|
||||
📈 Kvar:
|
||||
<span class="highlight leftover" ng-class="{'plus': getLeftover() >= 0, 'minus': getLeftover() < 0}">
|
||||
{{ getLeftover() | number:0 }} kr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
</ul>
|
||||
<hr>
|
||||
<ul class="category-summary-list">
|
||||
<li ng-repeat="cat in budget.categories">
|
||||
<span style="color: {{ cat.color }}">{{ cat.name }}</span>
|
||||
<span>{{ getCategorySum(cat) | number:0 }} kr</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Höger: Diagramväxling -->
|
||||
<div class="budget-chart-box compact">
|
||||
<div class="chart-row">
|
||||
<div class="chart-legend">
|
||||
<h4>Utgifter</h4>
|
||||
|
||||
<p style="font-size: 12px; margin: 4px 0 10px;">
|
||||
Totalt: <strong>{{ getTotalExpense() | number:0 }} kr</strong>
|
||||
</p>
|
||||
|
||||
<p ng-if="topExpenseCategory" style="font-size: 12px; margin-bottom: 12px;">
|
||||
Största kategori: <br>
|
||||
<strong>{{ topExpenseCategory.name }}</strong> ({{ topExpenseCategory.percent | number:1 }}%)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="chart-area">
|
||||
<canvas id="expenseChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="budget-grid" ng-if="budget && budget.categories">
|
||||
<div class="budget-card"
|
||||
ng-repeat="cat in budget.categories"
|
||||
style="--cat-color: {{cat.color}};"
|
||||
draggable-category
|
||||
category="cat"
|
||||
on-category-drop="handleCategoryDrop(data, targetCategory)">
|
||||
|
||||
<div class="card-header" style="background-color: {{cat.color}};">
|
||||
<div class="header-left" ng-if="!cat.editing">{{ cat.name }}</div>
|
||||
<input class="header-edit" type="text" ng-model="cat.name" ng-if="cat.editing" />
|
||||
<input class="color-edit" type="color" ng-model="cat.color" ng-if="cat.editing" />
|
||||
<div class="header-actions">
|
||||
<button class="icon-button" ng-click="cat.editing ? saveCategory(cat) : cat.editing = true">
|
||||
<i ng-class="{'fa fa-check': cat.editing, 'fa fa-pencil': !cat.editing}"></i>
|
||||
</button>
|
||||
<button class="icon-button" ng-if="cat.editing" ng-click="cancelCategoryEdit(cat)">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
<button class="icon-button" ng-if="cat.editing" ng-click="cat.allowDrag = !cat.allowDrag" title="Flytt av kategori">
|
||||
<i class="fa" ng-class="cat.allowDrag ? 'fa-unlock' : 'fa-lock'"></i>
|
||||
</button>
|
||||
<button class="icon-button danger" ng-if="cat.editing" ng-click="deleteCategory(cat)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
<div class="month-picker-dropdown" ng-show="showMonthPicker" style="position: absolute; top: 100%; left: 50%; transform: translateX(-50%); background: white; border: 1px solid #ccc; padding: 10px; border-radius: 8px; z-index: 1000; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
|
||||
<select ng-model="tempMonth" ng-options="month for month in monthNames" style="margin-bottom: 8px;"></select><br>
|
||||
<input type="number" ng-model="tempYear" placeholder="År" style="width: 100%; margin-bottom: 8px;" />
|
||||
<button class="nav-button" ng-click="applyMonthSelection()">Välj</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<div ng-repeat="item in cat.items track by item.id">
|
||||
<div drop-placeholder
|
||||
category="cat"
|
||||
index="$index"
|
||||
on-drop-item="handleItemPreciseDrop(data, cat, $index)">
|
||||
|
||||
<div class="menu-container" ng-class="{ 'open': menuOpen }">
|
||||
<button class="icon-button" ng-click="toggleMenu($event)">
|
||||
<i class="fa fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu" ng-show="menuOpen">
|
||||
<button ng-click="copyPreviousMonthSafe()">Kopiera föregående månad</button>
|
||||
|
||||
<button ng-click="deleteMonth(); menuOpen = false;" class="danger">Ta bort hela månaden</button>
|
||||
<button ng-click="createNewCategory(); menuOpen = false;">Lägg till ny kategori</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="budget-overview-row" ng-if="budget && budget.categories.length > 0">
|
||||
<!-- Vänster: Summering -->
|
||||
<div class="budget-summary-box compact">
|
||||
<h3>Sammanställning</h3>
|
||||
<ul class="summary-list">
|
||||
<li><span>💰 Inkomst:</span> {{ getTotalIncome() | number:0 }} kr</li>
|
||||
<li><span>💸 Utgift:</span> {{ getTotalExpense() | number:0 }} kr</li>
|
||||
<li><span>🏦 Sparande:</span> {{ getTotalSaving() | number:0 }} kr</li>
|
||||
<p>
|
||||
📈 Kvar:
|
||||
<span class="highlight leftover" ng-class="{'plus': getLeftover() >= 0, 'minus': getLeftover() < 0}">
|
||||
{{ getLeftover() | number:0 }} kr
|
||||
</span>
|
||||
</p>
|
||||
|
||||
</ul>
|
||||
<hr>
|
||||
<ul class="category-summary-list">
|
||||
<li ng-repeat="cat in budget.categories">
|
||||
<span style="color: {{ cat.color }}">{{ cat.name }}</span>
|
||||
<span>{{ getCategorySum(cat) | number:0 }} kr</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Höger: Diagramväxling -->
|
||||
<div class="budget-chart-box compact">
|
||||
<div class="chart-row">
|
||||
<div class="chart-legend">
|
||||
<h4>Utgifter</h4>
|
||||
|
||||
<p style="font-size: 12px; margin: 4px 0 10px;">
|
||||
Totalt: <strong>{{ getTotalExpense() | number:0 }} kr</strong>
|
||||
</p>
|
||||
|
||||
<p ng-if="topExpenseCategory" style="font-size: 12px; margin-bottom: 12px;">
|
||||
Största kategori: <br>
|
||||
<strong>{{ topExpenseCategory.name }}</strong> ({{ topExpenseCategory.percent | number:1 }}%)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="chart-area">
|
||||
<canvas id="expenseChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="budget-grid" ng-if="budget && budget.categories">
|
||||
<div class="budget-card"
|
||||
ng-repeat="cat in budget.categories"
|
||||
style="--cat-color: {{cat.color}};"
|
||||
draggable-category
|
||||
category="cat"
|
||||
on-category-drop="handleCategoryDrop(data, targetCategory)">
|
||||
|
||||
<div class="card-header" style="background-color: {{cat.color}};">
|
||||
<div class="header-left" ng-if="!cat.editing">{{ cat.name }}</div>
|
||||
<input class="header-edit" type="text" ng-model="cat.name" ng-if="cat.editing" />
|
||||
<input class="color-edit" type="color" ng-model="cat.color" ng-if="cat.editing" />
|
||||
<div class="header-actions">
|
||||
<button class="icon-button" ng-click="cat.editing ? saveCategory(cat) : cat.editing = true">
|
||||
<i ng-class="{'fa fa-check': cat.editing, 'fa fa-pencil': !cat.editing}"></i>
|
||||
</button>
|
||||
<button class="icon-button" ng-if="cat.editing" ng-click="cancelCategoryEdit(cat)">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
<button class="icon-button" ng-if="cat.editing" ng-click="cat.allowDrag = !cat.allowDrag" title="Flytt av kategori">
|
||||
<i class="fa" ng-class="cat.allowDrag ? 'fa-unlock' : 'fa-lock'"></i>
|
||||
</button>
|
||||
<button class="icon-button danger" ng-if="cat.editing" ng-click="deleteCategory(cat)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-list">
|
||||
<div ng-repeat="item in cat.items track by item.id">
|
||||
<div drop-placeholder
|
||||
category="cat"
|
||||
index="$index"
|
||||
on-drop-item="handleItemPreciseDrop(data, cat, $index)">
|
||||
</div>
|
||||
|
||||
<div class="item-row"
|
||||
draggable-item
|
||||
item="item"
|
||||
category="cat"
|
||||
on-item-drop="handleItemDrop(event, data, cat)"
|
||||
ng-class="{ dragging: dragInProgress && draggedItemId === item.id }">
|
||||
<i class="fa fa-grip-lines drag-handle"
|
||||
ng-show="cat.editing"
|
||||
style="opacity: 0.5; padding-right: 6px; cursor: grab;"></i>
|
||||
<div class="item-row"
|
||||
draggable-item
|
||||
item="item"
|
||||
category="cat"
|
||||
on-item-drop="handleItemDrop(event, data, cat)"
|
||||
ng-class="{ dragging: dragInProgress && draggedItemId === item.id }">
|
||||
<i class="fa fa-grip-lines drag-handle"
|
||||
ng-show="cat.editing"
|
||||
style="opacity: 0.5; padding-right: 6px; cursor: grab;"></i>
|
||||
|
||||
<input type="text" ng-model="item.name" ng-if="cat.editing" />
|
||||
<span ng-if="!cat.editing" title="{{ item.definitionName }}">{{ item.name }}</span>
|
||||
<!-- <span ng-if="!cat.editing">#{{ item.definitionName }}</span>-->
|
||||
<input type="number" ng-model="item.amount" ng-if="cat.editing" />
|
||||
<span class="amount" ng-if="!cat.editing">{{ item.amount | number:0 }}</span>
|
||||
<input type="text" ng-model="item.name" ng-if="cat.editing" />
|
||||
<span ng-if="!cat.editing" title="{{ item.definitionName }}">{{ item.name }}</span>
|
||||
<!-- <span ng-if="!cat.editing">#{{ item.definitionName }}</span>-->
|
||||
<input type="number" ng-model="item.amount" ng-if="cat.editing" />
|
||||
<span class="amount" ng-if="!cat.editing">{{ item.amount | number:0 }}</span>
|
||||
|
||||
<!-- 3-pricksmeny -->
|
||||
<div class="item-menu-container" ng-if="cat.editing" style="position: relative;">
|
||||
<button class="icon-button" ng-click="openItemMenu($event, item)">
|
||||
<i class="fa fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="item-floating-menu" ng-show="menuVisible" ng-style="menuStyle">
|
||||
<span style="font-size: 11px; color: #94a3b8; display: block; margin: 0px 0px 0px 40px;">
|
||||
#{{ menuItem.definitionName }}
|
||||
</span>
|
||||
<button ng-click="deleteItem(menuItem.category, menuItem)">🗑 Ta bort</button>
|
||||
<hr>
|
||||
<button
|
||||
ng-click="setItemType(menuItem, 'expense')"
|
||||
ng-class="{ 'disabled': menuItem.isExpense }">
|
||||
<span ng-if="menuItem.isExpense">✔️</span> 💸 Utgift
|
||||
</button>
|
||||
<button
|
||||
ng-click="setItemType(menuItem, 'income')"
|
||||
ng-class="{ 'disabled': !menuItem.isExpense && menuItem.includeInSummary }">
|
||||
<span ng-if="!menuItem.isExpense && menuItem.includeInSummary">✔️</span> 💰 Inkomst
|
||||
</button>
|
||||
<button
|
||||
ng-click="setItemType(menuItem, 'saving')"
|
||||
ng-class="{ 'disabled': !menuItem.isExpense && !menuItem.includeInSummary }">
|
||||
<span ng-if="!menuItem.isExpense && !menuItem.includeInSummary">✔️</span> 🏦 Sparande
|
||||
<!-- 3-pricksmeny -->
|
||||
<div class="item-menu-container" ng-if="cat.editing" style="position: relative;">
|
||||
<button class="icon-button" ng-click="openItemMenu($event, item)">
|
||||
<i class="fa fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="item-floating-menu" ng-show="menuVisible" ng-style="menuStyle">
|
||||
<span style="font-size: 11px; color: #94a3b8; display: block; margin: 0px 0px 0px 40px;">
|
||||
#{{ menuItem.definitionName }}
|
||||
</span>
|
||||
<button ng-click="deleteItem(menuItem.category, menuItem)">🗑 Ta bort</button>
|
||||
<hr>
|
||||
<button
|
||||
ng-click="setItemType(menuItem, 'expense')"
|
||||
ng-class="{ 'disabled': menuItem.isExpense }">
|
||||
<span ng-if="menuItem.isExpense">✔️</span> 💸 Utgift
|
||||
</button>
|
||||
<button
|
||||
ng-click="setItemType(menuItem, 'income')"
|
||||
ng-class="{ 'disabled': !menuItem.isExpense && menuItem.includeInSummary }">
|
||||
<span ng-if="!menuItem.isExpense && menuItem.includeInSummary">✔️</span> 💰 Inkomst
|
||||
</button>
|
||||
<button
|
||||
ng-click="setItemType(menuItem, 'saving')"
|
||||
ng-class="{ 'disabled': !menuItem.isExpense && !menuItem.includeInSummary }">
|
||||
<span ng-if="!menuItem.isExpense && !menuItem.includeInSummary">✔️</span> 🏦 Sparande
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div drop-fallback
|
||||
ng-if="cat.editing"
|
||||
category="cat"
|
||||
on-drop-item="handleItemPreciseDrop(data, targetCategory, targetIndex)">
|
||||
</div>
|
||||
|
||||
<button class="icon-button add-post-btn"
|
||||
ng-if="cat.editing && !cat.addingItem"
|
||||
ng-click="openItemPopup($event, cat)">
|
||||
➕ Lägg till post
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div drop-fallback
|
||||
ng-if="cat.editing"
|
||||
category="cat"
|
||||
on-drop-item="handleItemPreciseDrop(data, targetCategory, targetIndex)">
|
||||
<div class="item-row total-row">
|
||||
<div>Summa</div>
|
||||
<div class="amount">{{ getCategorySum(cat) | number:0 }}</div>
|
||||
</div>
|
||||
|
||||
<button class="icon-button add-post-btn"
|
||||
ng-if="cat.editing && !cat.addingItem"
|
||||
ng-click="openItemPopup($event, cat)">
|
||||
➕ Lägg till post
|
||||
</button>
|
||||
|
||||
<div class="no-data" ng-if="!loading && budget && budget.categories.length === 0">
|
||||
Det finns ingen budgetdata för vald månad ({{ selectedMonth }}/{{ selectedYear }}).
|
||||
<button ng-click="copyPreviousMonthSafe()">[Test] Kopiera föregående</button>
|
||||
</div>
|
||||
|
||||
<div class="item-row total-row">
|
||||
<div>Summa</div>
|
||||
<div class="amount">{{ getCategorySum(cat) | number:0 }}</div>
|
||||
</div>
|
||||
|
||||
<div class="no-data" ng-if="!loading && budget && budget.categories.length === 0">
|
||||
Det finns ingen budgetdata för vald månad ({{ selectedMonth }}/{{ selectedYear }}).
|
||||
<button ng-click="copyPreviousMonthSafe()">[Test] Kopiera föregående</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="add-item-popup" ng-show="addPopupVisible" ng-style="addPopupStyle" ng-class="{ 'above': addPopupAbove }">
|
||||
<label>Typ:</label>
|
||||
<select ng-model="addPopupData.newItemType">
|
||||
<option value="expense">💸 Utgift</option>
|
||||
<option value="income">💰 Inkomst</option>
|
||||
<option value="saving">🏦 Sparande</option>
|
||||
</select>
|
||||
<div class="add-item-popup" ng-show="addPopupVisible" ng-style="addPopupStyle" ng-class="{ 'above': addPopupAbove }">
|
||||
<label>Typ:</label>
|
||||
<select ng-model="addPopupData.newItemType">
|
||||
<option value="expense">💸 Utgift</option>
|
||||
<option value="income">💰 Inkomst</option>
|
||||
<option value="saving">🏦 Sparande</option>
|
||||
</select>
|
||||
|
||||
<label>Definition:</label>
|
||||
<input type="text"
|
||||
ng-model="addPopupData.newItemDefinition"
|
||||
ng-change="updateDefinitionSuggestions()"
|
||||
placeholder="Ex: Elhandel"
|
||||
autocomplete="on"
|
||||
ng-blur="hideSuggestionsDelayed()"
|
||||
ng-focus="showDefinitionSuggestions = true" />
|
||||
<p style="color: red">{{ filteredDefinitions.length }} träffar</p>
|
||||
<ul class="suggestion-list" ng-show="showDefinitionSuggestions && filteredDefinitions.length > 0">
|
||||
<li ng-repeat="suggestion in filteredDefinitions"
|
||||
ng-mousedown="selectDefinitionSuggestion(suggestion.Name)">
|
||||
{{ suggestion.Name }}
|
||||
</li>
|
||||
</ul>
|
||||
<label>Definition:</label>
|
||||
<input type="text"
|
||||
ng-model="addPopupData.newItemDefinition"
|
||||
ng-change="updateDefinitionSuggestions()"
|
||||
placeholder="Ex: Elhandel"
|
||||
autocomplete="on"
|
||||
ng-blur="hideSuggestionsDelayed()"
|
||||
ng-focus="showDefinitionSuggestions = true" />
|
||||
<p style="color: red">{{ filteredDefinitions.length }} träffar</p>
|
||||
<ul class="suggestion-list" ng-show="showDefinitionSuggestions && filteredDefinitions.length > 0">
|
||||
<li ng-repeat="suggestion in filteredDefinitions"
|
||||
ng-mousedown="selectDefinitionSuggestion(suggestion.Name)">
|
||||
{{ suggestion.Name }}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<label>Etikett (valfritt):</label>
|
||||
<input type="text" ng-model="addPopupData.newItemLabel" placeholder="Ex: Däck till Volvon" />
|
||||
<label>Etikett (valfritt):</label>
|
||||
<input type="text" ng-model="addPopupData.newItemLabel" placeholder="Ex: Däck till Volvon" />
|
||||
|
||||
<label>Belopp:</label>
|
||||
<input type="number" ng-model="addPopupData.newItemAmount" placeholder="0" />
|
||||
<label>Belopp:</label>
|
||||
<input type="number" ng-model="addPopupData.newItemAmount" placeholder="0" />
|
||||
|
||||
<button ng-click="addItemFromPopup()">Lägg till</button>
|
||||
<button ng-click="addPopupVisible = false">Avbryt</button>
|
||||
</div>
|
||||
<button ng-click="addItemFromPopup()">Lägg till</button>
|
||||
<button ng-click="addPopupVisible = false">Avbryt</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<link rel="stylesheet" href="~/css/budget.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user