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:
@@ -27,7 +27,14 @@ namespace Aberwyn.Controllers
|
||||
{
|
||||
var isOpen = _context.AppSettings.FirstOrDefault(x => x.Key == "RestaurantIsOpen")?.Value == "True";
|
||||
ViewBag.RestaurantIsOpen = isOpen;
|
||||
return View();
|
||||
|
||||
var now = DateTime.Now;
|
||||
var showDate = now.Hour >= 18 ? now.Date.AddDays(1) : now.Date;
|
||||
|
||||
var todaysMenu = _menuService.GetMenuForDate(showDate);
|
||||
|
||||
ViewBag.ShowDate = showDate;
|
||||
return View(todaysMenu);
|
||||
}
|
||||
|
||||
public IActionResult Privacy()
|
||||
|
||||
@@ -339,6 +339,41 @@ public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
|
||||
|
||||
return menus;
|
||||
}
|
||||
public WeeklyMenu? GetMenuForDate(DateTime date)
|
||||
{
|
||||
int week = ISOWeek.GetWeekOfYear(date);
|
||||
int year = date.Year;
|
||||
|
||||
// Gör om till ISO 8601: Måndag = 1, Söndag = 7
|
||||
int dayOfWeek = (int)date.DayOfWeek;
|
||||
if (dayOfWeek == 0) dayOfWeek = 7;
|
||||
|
||||
var menu = _context.WeeklyMenus
|
||||
.FirstOrDefault(w => w.WeekNumber == week && w.Year == year && w.DayOfWeek == dayOfWeek);
|
||||
|
||||
if (menu != null)
|
||||
{
|
||||
var mealIds = new[] { menu.BreakfastMealId, menu.LunchMealId, menu.DinnerMealId }
|
||||
.Where(id => id.HasValue)
|
||||
.Select(id => id.Value)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
var allMeals = _context.Meals
|
||||
.Where(m => mealIds.Contains(m.Id))
|
||||
.ToDictionary(m => m.Id, m => m.Name);
|
||||
|
||||
if (menu.BreakfastMealId.HasValue && allMeals.TryGetValue(menu.BreakfastMealId.Value, out var breakfast))
|
||||
menu.BreakfastMealName = breakfast;
|
||||
if (menu.LunchMealId.HasValue && allMeals.TryGetValue(menu.LunchMealId.Value, out var lunch))
|
||||
menu.LunchMealName = lunch;
|
||||
if (menu.DinnerMealId.HasValue && allMeals.TryGetValue(menu.DinnerMealId.Value, out var dinner))
|
||||
menu.DinnerMealName = dinner;
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
<datalist id="meals-list">
|
||||
@foreach (var meal in (List<Meal>)ViewBag.AvailableMeals)
|
||||
{
|
||||
<option value="@meal.Id">@meal.Name</option>
|
||||
<option value="@meal.Name">@meal.Name</option>
|
||||
}
|
||||
</datalist>
|
||||
</form>
|
||||
|
||||
@@ -7,16 +7,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="budgetApp">
|
||||
<head>
|
||||
<style>
|
||||
[ng-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<title>Budget Overview</title>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="~/css/budget.css">
|
||||
</head>
|
||||
<body ng-controller="BudgetController">
|
||||
<body ng-controller="BudgetController" ng-cloak>
|
||||
|
||||
<div class="budget-page">
|
||||
<h1>Budget Overview</h1>
|
||||
<h1 ng-cloak>Budget Overview</h1>
|
||||
|
||||
<div class="date-picker" ng-click="toggleDatePicker($event)">
|
||||
<button type="button" class="date-picker-toggle">
|
||||
@@ -28,7 +33,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="budget-container">
|
||||
<div ng-if="isLoadingBudget" class="loading-indicator">
|
||||
⏳ Laddar budget...
|
||||
</div>
|
||||
|
||||
<div ng-if="!isLoadingBudget" class="budget-container">
|
||||
<div ng-repeat="category in categories" class="category-block">
|
||||
<div class="category-header">
|
||||
{{ category }}
|
||||
@@ -55,7 +64,7 @@
|
||||
angular.module('budgetApp', [])
|
||||
.controller('BudgetController', function ($scope, $http) {
|
||||
const today = new Date();
|
||||
|
||||
$scope.isLoadingBudget = true;
|
||||
// Initialize months and years
|
||||
$scope.months = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
|
||||
$scope.years = [...Array(11).keys()].map(i => today.getFullYear() - 10 + i);
|
||||
@@ -72,11 +81,16 @@
|
||||
|
||||
// Automatically filter when month or year changes
|
||||
$scope.filterBudget = function () {
|
||||
$scope.isLoadingBudget = true;
|
||||
$http.get('/api/budgetapi/items', {
|
||||
params: { month: $scope.selectedMonth, year: $scope.selectedYear }
|
||||
}).then(response => {
|
||||
$scope.budgetItems = response.data;
|
||||
}).catch(error => console.error("Error fetching budget items:", error));
|
||||
$scope.isLoadingBudget = false;
|
||||
}).catch(error => {
|
||||
console.error("Error fetching budget items:", error);
|
||||
$scope.isLoadingBudget = false;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getItemsByCategory = function (category) {
|
||||
|
||||
@@ -1,18 +1,44 @@
|
||||
@{
|
||||
ViewData["Title"] = "Welcome to Lewel!";
|
||||
@model Aberwyn.Models.WeeklyMenu
|
||||
@{
|
||||
var showDate = (DateTime)ViewBag.ShowDate;
|
||||
var day = showDate.ToString("dddd", new System.Globalization.CultureInfo("sv-SE"));
|
||||
}
|
||||
<link rel="stylesheet" href="~/css/Welcome.css" />
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@ViewData["Title"]</title>
|
||||
<section class="welcome-section light-mode">
|
||||
<div class="welcome-content">
|
||||
<h1 class="welcome-title">Välkommen till
|
||||
<span class="initial L">L</span>
|
||||
<span class="initial E">E</span>
|
||||
<span class="initial W">W</span>
|
||||
<span class="initial E2">E</span>
|
||||
<span class="initial L2">L</span></h1>
|
||||
<p class="welcome-subtitle">Idag är det <strong>@day</strong> – dagens meny är:</p>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Välkommen</h1> Kika på <a href="/Home/Menu" class="button">Menyn</a> om du vill.
|
||||
@if (Model != null)
|
||||
{
|
||||
<div class="meal-lines">
|
||||
@if (!string.IsNullOrWhiteSpace(Model.BreakfastMealName)) {
|
||||
<p><strong>Frukost:</strong> @Model.BreakfastMealName</p>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(Model.LunchMealName)) {
|
||||
<p><strong>Lunch:</strong> @Model.LunchMealName</p>
|
||||
}
|
||||
@if (!string.IsNullOrWhiteSpace(Model.DinnerMealName)) {
|
||||
<p><strong>Middag:</strong> @Model.DinnerMealName</p>
|
||||
}
|
||||
|
||||
@if (ViewBag.RestaurantIsOpen as bool? == true)
|
||||
{
|
||||
<p><strong>Pizzerian är öppen!</strong></p><a asp-controller="FoodMenu" asp-action="PizzaOrder">Klicka här för att Beställa pizza</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="no-menu">Ingen meny är inlagd för denna dag.</p>
|
||||
}
|
||||
|
||||
<a class="nav-button" href="/Home/Menu">Visa hela veckomenyn</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</section>
|
||||
|
||||
@@ -32,13 +32,13 @@
|
||||
<div class="day-header">{{day}}</div>
|
||||
<div class="meal-info" ng-if="menu[day]">
|
||||
<div ng-if="menu[day].breakfastMealId" class="meal-selection">
|
||||
<a href="/Meal/View/{{menu[day].breakfastMealId}}" target="_blank"><strong>Frukost:</strong> {{menu[day].breakfastMealName}}</a>
|
||||
<a href="/Meal/View/{{menu[day].breakfastMealId}}"><strong>Frukost:</strong> {{menu[day].breakfastMealName}}</a>
|
||||
</div>
|
||||
<div ng-if="menu[day].lunchMealId" class="meal-selection">
|
||||
<a href="/Meal/View/{{menu[day].lunchMealId}}" target="_blank"><strong>Lunch:</strong> {{menu[day].lunchMealName}}</a>
|
||||
<a href="/Meal/View/{{menu[day].lunchMealId}}"><strong>Lunch:</strong> {{menu[day].lunchMealName}}</a>
|
||||
</div>
|
||||
<div ng-if="menu[day].dinnerMealId" class="meal-selection">
|
||||
<a href="/Meal/View/{{menu[day].dinnerMealId}}" target="_blank"><strong>Middag:</strong> {{menu[day].dinnerMealName}}</a>
|
||||
<a href="/Meal/View/{{menu[day].dinnerMealId}}"><strong>Middag:</strong> {{menu[day].dinnerMealName}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!menu[day]">
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
@{
|
||||
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="sv" ng-app="mealGalleryApp">
|
||||
<head>
|
||||
@@ -9,6 +6,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
|
||||
<link rel="stylesheet" href="/css/meal-gallery.css">
|
||||
<script src="https://kit.fontawesome.com/yourkit.js" crossorigin="anonymous"></script> <!-- om du använder fontawesome -->
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
body { overflow-x: hidden; }
|
||||
</style>
|
||||
</head>
|
||||
<body ng-controller="MealGalleryController">
|
||||
<div class="meal-gallery-container">
|
||||
@@ -21,7 +23,8 @@
|
||||
</div>
|
||||
|
||||
<div class="meal-gallery-grid">
|
||||
<div class="meal-card" ng-repeat="meal in meals | filter:search">
|
||||
<div class="meal-card"
|
||||
ng-repeat="meal in (meals | filter:search) | limitTo:visibleCount track by meal.Id">
|
||||
<img ng-src="{{ meal.ThumbnailData ? 'data:image/webp;base64,' + meal.ThumbnailData : '/images/fallback.jpg' }}"
|
||||
alt="{{ meal.Name }}">
|
||||
<div class="meal-card-content">
|
||||
@@ -37,10 +40,25 @@
|
||||
angular.module("mealGalleryApp", []).controller("MealGalleryController", function ($scope, $http) {
|
||||
$scope.meals = [];
|
||||
$scope.search = "";
|
||||
$scope.visibleCount = 12;
|
||||
|
||||
$http.get("/api/mealMenuApi/getMeals").then(res => {
|
||||
$scope.meals = res.data;
|
||||
});
|
||||
|
||||
// Lazy loading on scroll
|
||||
window.addEventListener('scroll', function () {
|
||||
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 150) {
|
||||
$scope.$applyAsync(() => {
|
||||
$scope.visibleCount += 8;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Show all when searching
|
||||
$scope.$watch('search', function (newVal) {
|
||||
$scope.visibleCount = newVal ? 9999 : 12;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="icon" type="image/png" sizes="512x512" href="/images/lewel-icon.png">
|
||||
<meta name="theme-color" content="#6a0dad">
|
||||
<style>
|
||||
[ng-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LEWEL - Dashboard</title>
|
||||
@@ -28,42 +33,51 @@
|
||||
</header>
|
||||
|
||||
<div class="page-content">
|
||||
<aside class="sidebar">
|
||||
@if (ViewBag.IsSetupMode as bool? != true)
|
||||
{
|
||||
<ul class="nav-list">
|
||||
<li><a asp-controller="Home" asp-action="Index"><i class="fas fa-home"></i> Home</a></li>
|
||||
@if (User.IsInRole("Budget"))
|
||||
{
|
||||
<li><a asp-controller="Budget" asp-action="Index"><i class="fas fa-wallet"></i> Budget</a></li>
|
||||
}
|
||||
<li><a asp-controller="Home" asp-action="Menu"><i class="fas fa-utensils"></i> Veckomeny</a></li>
|
||||
@if (ViewBag.RestaurantIsOpen as bool? == true)
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaOrder"><i class="fas fa-pizza-slice"></i> Beställ pizza</a></li>
|
||||
}
|
||||
@if (User.IsInRole("Chef"))
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="Veckomeny"><i class="fas fa-calendar-week"></i> Administrera Veckomeny</a></li>
|
||||
<li><a asp-controller="Meal" asp-action="Index"><i class="fas fa-list"></i> Måltider</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaAdmin"><i class="fas fa-list"></i> Pizza Admin</a></li>
|
||||
<nav class="main-nav">
|
||||
<ul class="nav-list-horizontal">
|
||||
<li><a asp-controller="Home" asp-action="Index"><i class="fas fa-home"></i> Hem</a></li>
|
||||
<li><a asp-controller="Home" asp-action="Menu"><i class="fas fa-utensils"></i> Veckomeny</a></li>
|
||||
<li><a asp-controller="Meal" asp-action="Index">Recept</a></li>
|
||||
|
||||
}
|
||||
@if (ViewBag.RestaurantIsOpen as bool? == true)
|
||||
{
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaOrder"><i class="fas fa-pizza-slice"></i> Beställ pizza</a></li>
|
||||
}
|
||||
@if (User.IsInRole("Budget"))
|
||||
{
|
||||
<li><a asp-controller="Budget" asp-action="Index"> Budget</a></li>
|
||||
}
|
||||
@if (User.IsInRole("Chef"))
|
||||
{
|
||||
<li class="dropdown">
|
||||
<button class="dropdown-toggle" type="button">
|
||||
<i class="fas fa-list"></i> Kök <i class="fas fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a asp-controller="FoodMenu" asp-action="Veckomeny">Planera Veckomeny</a></li>
|
||||
<li><a asp-controller="FoodMenu" asp-action="PizzaAdmin">Pizza Admin</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li><a asp-controller="Admin" asp-action="Index"><i class="fas fa-cog"></i> Adminpanel</a></li>
|
||||
<li><a asp-controller="Admin" asp-action="Todo"><i class="fas fa-cog"></i> Todo</a></li>
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<li class="dropdown">
|
||||
<button class="dropdown-toggle" type="button">
|
||||
<i class="fas fa-cog"></i> Admin <i class="fas fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a asp-controller="Admin" asp-action="Index">Adminpanel</a></li>
|
||||
<li><a asp-controller="Admin" asp-action="Todo">Todo</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
</nav>
|
||||
|
||||
}
|
||||
</ul>
|
||||
} else
|
||||
{
|
||||
<ul class="nav-list">
|
||||
<li><a asp-controller="Setup" asp-action="Index"> Setup</a></li>
|
||||
</ul>
|
||||
}
|
||||
</aside>
|
||||
|
||||
<main class="main-panel">
|
||||
@RenderBody()
|
||||
@@ -72,6 +86,45 @@
|
||||
</div>
|
||||
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const dropdowns = document.querySelectorAll(".dropdown");
|
||||
|
||||
dropdowns.forEach(dropdown => {
|
||||
const toggle = dropdown.querySelector(".dropdown-toggle");
|
||||
const menu = dropdown.querySelector(".dropdown-menu");
|
||||
|
||||
toggle.addEventListener("click", e => {
|
||||
e.stopPropagation();
|
||||
const isOpen = dropdown.classList.contains("open");
|
||||
|
||||
// Stäng alla andra först
|
||||
document.querySelectorAll(".dropdown.open").forEach(d => {
|
||||
if (d !== dropdown) d.classList.remove("open");
|
||||
});
|
||||
|
||||
dropdown.classList.toggle("open", !isOpen);
|
||||
|
||||
if (!isOpen) {
|
||||
const rect = toggle.getBoundingClientRect();
|
||||
menu.style.position = "fixed";
|
||||
menu.style.top = rect.bottom + "px";
|
||||
menu.style.left = rect.left + "px";
|
||||
menu.style.zIndex = 3000;
|
||||
} else {
|
||||
menu.style.top = "";
|
||||
menu.style.left = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Klick utanför stänger alla dropdowns
|
||||
document.addEventListener("click", () => {
|
||||
document.querySelectorAll(".dropdown.open").forEach(d => d.classList.remove("open"));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
80
Aberwyn/wwwroot/css/Welcome.css
Normal file
80
Aberwyn/wwwroot/css/Welcome.css
Normal file
@@ -0,0 +1,80 @@
|
||||
.welcome-section.light-mode {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 90vh;
|
||||
text-align: center;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(to bottom right, #f9fafb, #e2e8f0);
|
||||
animation: fadeIn 0.6s ease-in;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-content {
|
||||
max-width: 700px;
|
||||
color: #1e293b;
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #eab308;
|
||||
}
|
||||
|
||||
.welcome-subtitle {
|
||||
font-size: 1.35rem;
|
||||
color: #334155;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.meal-lines p {
|
||||
font-size: 1.2rem;
|
||||
margin: 8px 0;
|
||||
color: #0f172a;
|
||||
}
|
||||
|
||||
.meal-lines strong {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.no-menu {
|
||||
font-size: 1.1rem;
|
||||
color: #64748b;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.nav-button {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 12px 26px;
|
||||
border-radius: 10px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
display: inline-block;
|
||||
margin-top: 24px;
|
||||
transition: background 0.2s ease, transform 0.2s ease;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.nav-button:hover {
|
||||
background: #2563eb;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
@@ -669,3 +669,13 @@ canvas {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
.loading-indicator {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
font-size: 18px;
|
||||
color: #64748B;
|
||||
font-style: italic;
|
||||
}
|
||||
[ng-cloak] {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -3,105 +3,157 @@ body {
|
||||
font-family: 'Segoe UI', sans-serif;
|
||||
color: #222;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.meal-gallery-container {
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.meal-gallery-container {
|
||||
margin: 0 auto;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
.meal-gallery-header {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
/* === Header och sökfält === */
|
||||
.meal-gallery-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.meal-gallery-header h1 {
|
||||
font-size: 2.4rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.meal-gallery-header h1 {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
margin-top: 1rem;
|
||||
position: relative;
|
||||
max-width: 400px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.search-container input {
|
||||
width: 100%;
|
||||
padding: 10px 38px 10px 12px;
|
||||
border-radius: 25px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.search-container i {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.meal-gallery-grid {
|
||||
max-width: 500px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.search-container input {
|
||||
width: 100%;
|
||||
padding: 8px 34px 8px 12px;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-container i {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #888;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* === Grid layout === */
|
||||
.meal-gallery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.meal-card {
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 20px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
/* === Kortstruktur === */
|
||||
.meal-card {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.meal-card:hover {
|
||||
transform: scale(1.015);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.meal-card img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.meal-card-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.meal-card:hover {
|
||||
transform: scale(1.015);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.meal-card img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.meal-card-content {
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.meal-card-content h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 1.2rem;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.meal-card-content p {
|
||||
flex-grow: 1;
|
||||
font-size: 0.95rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.btn-readmore {
|
||||
}
|
||||
|
||||
.meal-card-content h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.15rem;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.meal-card-content p {
|
||||
flex-grow: 1;
|
||||
font-size: 0.95rem;
|
||||
color: #555;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* === Läs mer-knapp === */
|
||||
.btn-readmore {
|
||||
align-self: flex-start;
|
||||
background: #007d36;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
padding: 7px 12px;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
font-size: 0.95rem;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.btn-readmore:hover {
|
||||
background: #005c27;
|
||||
}
|
||||
|
||||
font-size: 0.9rem;
|
||||
margin-top: auto;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-readmore:hover {
|
||||
background: #005c27;
|
||||
}
|
||||
|
||||
/* === Mobilanpassning === */
|
||||
@media (max-width: 600px) {
|
||||
.meal-gallery-grid {
|
||||
grid-template-columns: repeat(2, 1fr); /* exakt 2 per rad */
|
||||
}
|
||||
.meal-gallery-header h1 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.search-container {
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.search-container input {
|
||||
padding: 6px 30px 6px 10px;
|
||||
font-size: 0.9rem;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
.meal-card img {
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.meal-card-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.meal-card-content h3 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.meal-card-content p {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.btn-readmore {
|
||||
padding: 6px 10px;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
:root {
|
||||
--bg: #F9FAFB;
|
||||
--bg-mid: #EDF2F7;
|
||||
--text: #2D3748;
|
||||
--accent: #3182CE;
|
||||
@@ -34,6 +33,10 @@ body {
|
||||
background-color: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
.fade-out {
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-out;
|
||||
}
|
||||
|
||||
.meal-menu-page {
|
||||
position: relative;
|
||||
|
||||
@@ -147,25 +147,25 @@ body {
|
||||
|
||||
.page-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
background-color: #223344;
|
||||
width: 100%; /* force full width */
|
||||
max-width: unset;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 155px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.main-panel {
|
||||
flex: 1;
|
||||
background-color: #ffffff;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
padding: 0px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -173,11 +173,20 @@ body {
|
||||
color: #2C3E50;
|
||||
font-size: 16px;
|
||||
line-height: 1.7;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
NAVIGATIONSLISTA <20> KOMPAKT STIL
|
||||
========================================================================== */
|
||||
|
||||
/*.sidebar {
|
||||
width: 155px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.nav-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
@@ -210,6 +219,70 @@ body {
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
*/
|
||||
|
||||
.main-nav {
|
||||
position: fixed;
|
||||
top: 54px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2000; /* Högt nog för att ligga över allt annat */
|
||||
background-color: #f3f4f6;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
padding: 5px 8px;
|
||||
font-size: 13px;
|
||||
overflow-x: auto; /* behåll för horisontell scroll */
|
||||
overflow-y: visible; /* tillåt dropdown att spilla ut */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.main-nav {
|
||||
position: fixed;
|
||||
top: 54px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.main-panel {
|
||||
margin-top: 24px; /* justera beroende på hur hög nav-baren är */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.nav-list-horizontal {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 5px 0px 0px 0px;
|
||||
margin: 5px 0px 0px 0px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
white-space: nowrap;
|
||||
/* Viktigt: ta bort all overflow här */
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.nav-list-horizontal li a {
|
||||
text-decoration: none;
|
||||
color: #1F2C3C;
|
||||
padding: 0px 5px;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.nav-list-horizontal li a:hover {
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
RESPONSIVT
|
||||
@@ -223,3 +296,70 @@ body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Dropdown
|
||||
========================================================================== */
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #1F2C3C;
|
||||
padding: 0px 5px;
|
||||
border: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
height: 100%; /* viktigt för vertikal alignment */
|
||||
}
|
||||
|
||||
.dropdown-toggle:hover {
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
transform: translateY(4px); /* lite spacing nedåt */
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 8px;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
min-width: 180px;
|
||||
z-index: 3000;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
.dropdown.open .dropdown-menu {
|
||||
display: flex;
|
||||
}
|
||||
.dropdown-menu li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.dropdown-menu li a {
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
color: #1F2C3C;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-menu li a:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
console.log("budget.js loaded");
|
||||
app.controller('BudgetController', function ($scope, $http) {
|
||||
$scope.budget = null;
|
||||
$scope.loading = false;
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$scope.menuOpen = false;
|
||||
$scope.chartMode = "pie";
|
||||
@@ -823,7 +823,7 @@ $scope.addItemFromDefinition = function (cat) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.loading = true;
|
||||
$scope.loadItemDefinitions().then(() => {
|
||||
$scope.loadBudget();
|
||||
});
|
||||
|
||||
@@ -94,8 +94,13 @@ angular.module('mealMenuApp', ['ngSanitize'])
|
||||
|
||||
|
||||
$scope.openMeal = function (mealId) {
|
||||
if (!mealId) return;
|
||||
window.open('/Meal/View/' + mealId, '_blank');
|
||||
if (mealId) {
|
||||
const page = document.querySelector('.meal-menu-page');
|
||||
page.classList.add('fade-out');
|
||||
setTimeout(() => {
|
||||
window.location.href = "/Meal/View/" + mealId;
|
||||
}, 400); // matchar CSS-transition
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getDayImage = function (day) {
|
||||
|
||||
Reference in New Issue
Block a user