profileShare

rasmusjy / splitapp-backend-microservices

Read-only snapshot

No repository description.

main default branch 501 files Expires Sep 13, 2026, 9:06 AM
Index.cshtml 7,531 bytes
1 @model List<SplitApp.WebApp.Controllers.BudgetCategoryViewModel>
2
3 @{
4 ViewData["Title"] = Localizer["Budget"];
5 var tripId = (Guid)ViewData["TripId"]!;
6 var tripName = (string)ViewData["TripName"]!;
7 var totalPlanned = (decimal)ViewData["TotalPlanned"]!;
8 var totalSpent = (decimal)ViewData["TotalSpent"]!;
9 var remaining = totalPlanned - totalSpent;
10 var overallPct = totalPlanned > 0 ? (int)(totalSpent * 100 / totalPlanned) : 0;
11 var isOrganizer = (bool)ViewData["IsOrganizer"]!;
12 }
13
14 <!-- Page Header -->
15 <div class="sa-gradient-header" style="background: linear-gradient(135deg, #22c55e 0%, #1a9e8f 100%);">
16 <div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
17 <div>
18 <h1 style="margin-bottom: 4px;">@Localizer["Budget"]</h1>
19 <p class="text-muted mb-0">@tripName</p>
20 </div>
21 <div class="d-flex gap-2">
22 @if (isOrganizer)
23 {
24 <a asp-action="CreateCategory" asp-route-tripId="@tripId" class="sa-btn sa-btn-sm" style="background: rgba(255,255,255,0.2); color: #fff; border: 1.5px solid rgba(255,255,255,0.4);">
25 <i class="bi bi-plus-lg"></i> @Localizer["Add Category"]
26 </a>
27 }
28 <a asp-controller="Trips" asp-action="Details" asp-route-id="@tripId" class="sa-btn sa-btn-sm" style="background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.9);">
29 <i class="bi bi-arrow-left"></i>
30 </a>
31 </div>
32 </div>
33 </div>
34
35 <!-- Summary Cards -->
36 <div class="row g-3 mb-4">
37 <div class="col-md-4">
38 <div class="sa-card-static">
39 <div class="sa-stat">
40 <div class="sa-stat-icon" style="color: var(--sa-info);"><i class="bi bi-bullseye"></i></div>
41 <div class="sa-stat-value">@totalPlanned.ToString("N2")</div>
42 <div class="sa-stat-label">@Localizer["Total Planned"]</div>
43 </div>
44 </div>
45 </div>
46 <div class="col-md-4">
47 <div class="sa-card-static">
48 <div class="sa-stat">
49 <div class="sa-stat-icon" style="color: @(totalSpent > totalPlanned && totalPlanned > 0 ? "var(--sa-danger)" : "var(--sa-accent)");"><i class="bi bi-credit-card"></i></div>
50 <div class="sa-stat-value" style="color: @(totalSpent > totalPlanned && totalPlanned > 0 ? "var(--sa-danger)" : "inherit");">@totalSpent.ToString("N2")</div>
51 <div class="sa-stat-label">@Localizer["Total Spent"]</div>
52 </div>
53 </div>
54 </div>
55 <div class="col-md-4">
56 <div class="sa-card-static">
57 <div class="sa-stat">
58 <div class="sa-stat-icon" style="color: @(remaining >= 0 ? "var(--sa-success)" : "var(--sa-danger)");"><i class="bi bi-piggy-bank"></i></div>
59 <div class="sa-stat-value" style="color: @(remaining >= 0 ? "var(--sa-success)" : "var(--sa-danger)");">@remaining.ToString("N2")</div>
60 <div class="sa-stat-label">@Localizer["Remaining"]</div>
61 </div>
62 </div>
63 </div>
64 </div>
65
66 <!-- Overall progress -->
67 @if (totalPlanned > 0)
68 {
69 <div class="sa-card-static mb-4">
70 <div class="sa-card-body">
71 <div class="d-flex justify-content-between mb-2">
72 <span style="font-weight: 600; font-size: 0.9rem;">@Localizer["Overall Progress"]</span>
73 <span class="@(overallPct > 100 ? "text-danger fw-bold" : "sa-text-muted")" style="font-size: 0.85rem;">@overallPct%</span>
74 </div>
75 @if (overallPct > 100)
76 {
77 <small style="color: var(--sa-danger); font-weight: 600;">@Localizer["Over budget!"]</small>
78 }
79 @{
80 var barClass = overallPct <= 60 ? "sa-progress-bar-success" : overallPct <= 85 ? "sa-progress-bar-warning" : "sa-progress-bar-danger";
81 }
82 <div class="sa-progress sa-progress-lg">
83 <div class="sa-progress-bar @barClass" data-width="@Math.Min(overallPct, 100)%" style="width: 0%;"></div>
84 </div>
85 </div>
86 </div>
87 }
88
89 @if (!Model.Any())
90 {
91 <div class="sa-empty">
92 <div class="sa-empty-icon"><i class="bi bi-wallet2"></i></div>
93 <div class="sa-empty-title">@Localizer["No budget categories yet"]</div>
94 <p class="sa-empty-text">@Localizer["Add categories to track your trip budget."]</p>
95 <a asp-action="CreateCategory" asp-route-tripId="@tripId" class="sa-btn sa-btn-secondary sa-btn-pill">
96 <i class="bi bi-plus-lg"></i> @Localizer["Add Category"]
97 </a>
98 </div>
99 }
100 else
101 {
102 <div class="d-flex flex-column gap-3">
103 @foreach (var category in Model)
104 {
105 var catPct = category.ProgressPercentage;
106 var catBarClass = catPct <= 60 ? "sa-progress-bar-success" : catPct <= 85 ? "sa-progress-bar-warning" : "sa-progress-bar-danger";
107
108 <div class="sa-card-static">
109 <div class="sa-card-body">
110 <div class="d-flex justify-content-between align-items-center mb-3">
111 <div class="d-flex align-items-center gap-3">
112 <div class="sa-expense-icon sa-category-default">
113 @if (!string.IsNullOrEmpty(category.IconName))
114 {
115 <i class="bi bi-@category.IconName"></i>
116 }
117 else
118 {
119 <i class="bi bi-tag"></i>
120 }
121 </div>
122 <div>
123 <div style="font-weight: 700;">@category.Name</div>
124 <div style="font-size: 0.85rem; color: var(--sa-gray-500);">
125 @category.SpentAmount.ToString("N2") / @category.PlannedAmount.ToString("N2")
126 </div>
127 </div>
128 </div>
129 @if (isOrganizer)
130 {
131 <div class="d-flex gap-2">
132 <a asp-action="EditCategory" asp-route-id="@category.Id" class="sa-btn sa-btn-ghost sa-btn-icon sa-btn-sm" title="@Localizer["Edit"]">
133 <i class="bi bi-pencil"></i>
134 </a>
135 <a asp-action="DeleteCategory" asp-route-id="@category.Id" class="sa-btn sa-btn-ghost sa-btn-icon sa-btn-sm" title="@Localizer["Delete"]" style="color: var(--sa-danger);">
136 <i class="bi bi-trash3"></i>
137 </a>
138 </div>
139 }
140 </div>
141 <div class="d-flex justify-content-between mb-1">
142 <small class="sa-text-muted">@catPct%</small>
143 @if (catPct > 100)
144 {
145 <small style="color: var(--sa-danger); font-weight: 600;">@Localizer["Over budget!"]</small>
146 }
147 </div>
148 <div class="sa-progress">
149 <div class="sa-progress-bar @catBarClass" data-width="@Math.Min(catPct, 100)%"></div>
150 </div>
151 </div>
152 </div>
153 }
154 </div>
155 }
156