profileShare

rasmusjy / splitapp-backend-microservices

Read-only snapshot

No repository description.

main default branch 501 files Expires Sep 13, 2026, 9:06 AM
Details.cshtml 14,481 bytes
1 @model SplitApp.WebApp.Application.DTO.TripBllDto
2
3 @{
4 ViewData["Title"] = Model.Name;
5 var tripId = (Guid)ViewData["TripId"]!;
6 var participantCount = (int)ViewData["ParticipantCount"]!;
7 var recentExpenses = (List<SplitApp.WebApp.Application.DTO.ExpenseBllDto>)ViewData["RecentExpenses"]!;
8 var totalExpenses = (decimal)ViewData["TotalExpenses"]!;
9 var userRole = (EParticipantRole)ViewData["UserRole"]!;
10 var balances = (List<SplitApp.WebApp.Controllers.SettlementBalanceViewModel>)ViewData["Balances"]!;
11 var currentUserBalance = (decimal)ViewData["CurrentUserBalance"]!;
12 var budgetUsedPct = (int)ViewData["BudgetUsedPct"]!;
13 var totalPlanned = (decimal)ViewData["TotalPlanned"]!;
14 var currencySymbol = (string)ViewData["CurrencySymbol"]!;
15 }
16
17 <!-- Hero Header -->
18 <div class="sa-gradient-header">
19 <div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
20 <div>
21 <div class="d-flex align-items-center gap-3 mb-2">
22 <h1 style="margin: 0;">@Model.Name</h1>
23 @{
24 var statusBadge = Model.Status switch
25 {
26 ETripStatus.Active => "sa-badge-success",
27 ETripStatus.Finalizing => "sa-badge-warning",
28 ETripStatus.Settled => "sa-badge-info",
29 _ => "sa-badge-neutral"
30 };
31 }
32 <span class="sa-badge @statusBadge">@SplitApp.WebApp.Hosting.Helpers.EnumHelper.GetDisplayName(Model.Status)</span>
33 </div>
34 @if (!string.IsNullOrEmpty(Model.Destination))
35 {
36 <p class="text-muted mb-1" style="font-size: 1.05rem;">
37 <i class="bi bi-geo-alt-fill me-1"></i> @Model.Destination
38 </p>
39 }
40 @if (Model.StartDate.HasValue || Model.EndDate.HasValue)
41 {
42 <p class="text-muted mb-0" style="font-size: 0.9rem;">
43 <i class="bi bi-calendar-event me-1"></i>
44 @(Model.StartDate?.ToString("MMM dd, yyyy") ?? "?") — @(Model.EndDate?.ToString("MMM dd, yyyy") ?? "?")
45 </p>
46 }
47 @if (Model.DefaultCurrency != null)
48 {
49 <p class="text-muted mb-0" style="font-size: 0.9rem; margin-top: 4px;">
50 <i class="bi bi-currency-exchange me-1"></i> @Model.DefaultCurrency.Code (@Model.DefaultCurrency.Symbol)
51 </p>
52 }
53 </div>
54 <div class="d-flex gap-2">
55 @if (userRole == EParticipantRole.Organizer)
56 {
57 <a asp-action="Edit" asp-route-id="@tripId" class="sa-btn sa-btn-sm" style="background: rgba(255,255,255,0.2); color: #fff;">
58 <i class="bi bi-pencil"></i> @Localizer["Edit"]
59 </a>
60 <a asp-action="Delete" asp-route-id="@tripId" class="sa-btn sa-btn-sm" style="background: rgba(220,38,38,0.3); color: #fff;">
61 <i class="bi bi-trash3"></i> @Localizer["Delete"]
62 </a>
63 }
64 <a asp-action="Index" class="sa-btn sa-btn-sm" style="background: rgba(255,255,255,0.15); color: rgba(255,255,255,0.9);">
65 <i class="bi bi-arrow-left"></i> @Localizer["Back"]
66 </a>
67 </div>
68 </div>
69
70 <!-- Participant Avatars -->
71 @if (Model.Participants != null && Model.Participants.Any())
72 {
73 <div class="sa-avatar-stack mt-3">
74 @{ var pIndex = 0; }
75 @foreach (var participant in Model.Participants.Where(p => p.IsActive).Take(6))
76 {
77 var name = participant.User != null ? $"{participant.User.FirstName} {participant.User.LastName}" : "?";
78 var initial = participant.User != null ? $"{participant.User.FirstName?[0]}{participant.User.LastName?[0]}" : "?";
79 <span class="sa-avatar sa-avatar-sm sa-avatar-@((pIndex % 8) + 1)" title="@name">@initial</span>
80 pIndex++;
81 }
82 @if (Model.Participants.Count(p => p.IsActive) > 6)
83 {
84 <span class="sa-avatar sa-avatar-sm sa-avatar-overflow">+@(Model.Participants.Count(p => p.IsActive) - 6)</span>
85 }
86 </div>
87 }
88 </div>
89
90 <!-- Stats Row -->
91 <div class="row g-3 mb-4">
92 <div class="col-6 col-md-3">
93 <div class="sa-card-static">
94 <div class="sa-stat">
95 <div class="sa-stat-icon" style="color: var(--sa-primary);"><i class="bi bi-receipt"></i></div>
96 <div class="sa-stat-value">@currencySymbol@totalExpenses.ToString("N0")</div>
97 <div class="sa-stat-label">@Localizer["Total Expenses"]</div>
98 </div>
99 </div>
100 </div>
101 <div class="col-6 col-md-3">
102 <div class="sa-card-static">
103 <div class="sa-stat">
104 <div class="sa-stat-icon" style="color: var(--sa-success);"><i class="bi bi-pie-chart-fill"></i></div>
105 <div class="sa-stat-value" style="color: @(budgetUsedPct > 90 ? "var(--sa-danger)" : budgetUsedPct > 70 ? "var(--sa-accent)" : "var(--sa-success)");">
106 @(totalPlanned > 0 ? $"{budgetUsedPct}%" : "—")
107 </div>
108 <div class="sa-stat-label">@Localizer["Budget Used"]</div>
109 </div>
110 </div>
111 </div>
112 <div class="col-6 col-md-3">
113 <div class="sa-card-static" style="@(currentUserBalance >= 0 ? "border-bottom: 3px solid var(--sa-success);" : "border-bottom: 3px solid var(--sa-danger);")">
114 <div class="sa-stat">
115 <div class="sa-stat-icon" style="color: @(currentUserBalance >= 0 ? "var(--sa-success)" : "var(--sa-danger)");"><i class="bi bi-wallet2"></i></div>
116 <div class="sa-stat-value" style="color: @(currentUserBalance >= 0 ? "var(--sa-success)" : "var(--sa-danger)");">
117 @(currentUserBalance >= 0 ? "+" : "")@currencySymbol@currentUserBalance.ToString("N2")
118 </div>
119 <div class="sa-stat-label">@Localizer["Your Balance"]</div>
120 </div>
121 </div>
122 </div>
123 <div class="col-6 col-md-3">
124 <div class="sa-card-static">
125 <div class="sa-stat">
126 <div class="sa-stat-icon" style="color: #6366f1;"><i class="bi bi-people-fill"></i></div>
127 <div class="sa-stat-value">@participantCount</div>
128 <div class="sa-stat-label">@Localizer["Members"]</div>
129 </div>
130 </div>
131 </div>
132 </div>
133
134 <!-- Navigation Grid -->
135 <div class="row g-3 mb-4">
136 <div class="col-4 col-md-2">
137 <a asp-controller="Expenses" asp-action="Index" asp-route-tripId="@tripId" class="sa-nav-card">
138 <div class="sa-nav-card-icon sa-nav-icon-expenses"><i class="bi bi-receipt-cutoff"></i></div>
139 <span class="sa-nav-card-label">@Localizer["Expenses"]</span>
140 </a>
141 </div>
142 <div class="col-4 col-md-2">
143 <a asp-controller="Members" asp-action="Index" asp-route-tripId="@tripId" class="sa-nav-card">
144 <div class="sa-nav-card-icon sa-nav-icon-members"><i class="bi bi-people-fill"></i></div>
145 <span class="sa-nav-card-label">@Localizer["Members"]</span>
146 </a>
147 </div>
148 <div class="col-4 col-md-2">
149 <a asp-controller="Budget" asp-action="Index" asp-route-tripId="@tripId" class="sa-nav-card">
150 <div class="sa-nav-card-icon sa-nav-icon-budget"><i class="bi bi-wallet2"></i></div>
151 <span class="sa-nav-card-label">@Localizer["Budget"]</span>
152 </a>
153 </div>
154 <div class="col-4 col-md-2">
155 <a asp-controller="WishlistClient" asp-action="Index" asp-route-tripId="@tripId" class="sa-nav-card">
156 <div class="sa-nav-card-icon sa-nav-icon-wishlist"><i class="bi bi-star-fill"></i></div>
157 <span class="sa-nav-card-label">@Localizer["Wishlist"]</span>
158 </a>
159 </div>
160 <div class="col-4 col-md-2">
161 <a asp-controller="PollsClient" asp-action="Index" asp-route-tripId="@tripId" class="sa-nav-card">
162 <div class="sa-nav-card-icon sa-nav-icon-polls"><i class="bi bi-bar-chart-fill"></i></div>
163 <span class="sa-nav-card-label">@Localizer["Polls"]</span>
164 </a>
165 </div>
166 <div class="col-4 col-md-2">
167 <a asp-controller="Settlement" asp-action="Index" asp-route-tripId="@tripId" class="sa-nav-card">
168 <div class="sa-nav-card-icon sa-nav-icon-settlement"><i class="bi bi-check2-circle"></i></div>
169 <span class="sa-nav-card-label">@Localizer["Settlement"]</span>
170 </a>
171 </div>
172 </div>
173
174 <!-- Balances -->
175 @if (balances.Any())
176 {
177 <div class="row g-3 mb-4">
178 <!-- Budget Progress -->
179 @if (totalPlanned > 0)
180 {
181 <div class="col-md-6">
182 <div class="sa-card-static h-100">
183 <div class="sa-card-header">
184 <i class="bi bi-pie-chart-fill me-2"></i>@Localizer["Budget Progress"]
185 </div>
186 <div class="sa-card-body">
187 @{
188 var budgetCategories = balances; // just for the section - actual categories loaded separately
189 }
190 <div class="d-flex justify-content-between mb-2">
191 <span style="font-weight: 600; font-size: 0.9rem;">@currencySymbol@totalExpenses.ToString("N0") / @currencySymbol@totalPlanned.ToString("N0")</span>
192 <span class="@(budgetUsedPct > 100 ? "text-danger fw-bold" : "sa-text-muted")" style="font-size: 0.85rem;">@budgetUsedPct%</span>
193 </div>
194 @{
195 var overallBarClass = budgetUsedPct <= 60 ? "sa-progress-bar-success" : budgetUsedPct <= 85 ? "sa-progress-bar-warning" : "sa-progress-bar-danger";
196 }
197 <div class="sa-progress sa-progress-lg">
198 <div class="sa-progress-bar @overallBarClass" data-width="@Math.Min(budgetUsedPct, 100)%" style="width: 0%;"></div>
199 </div>
200 </div>
201 </div>
202 </div>
203 }
204
205 <!-- Balances -->
206 <div class="@(totalPlanned > 0 ? "col-md-6" : "col-12")">
207 <div class="sa-card-static h-100">
208 <div class="sa-card-header">
209 <i class="bi bi-people-fill me-2"></i>@Localizer["Balances"]
210 </div>
211 <div class="sa-card-body" style="padding: var(--sa-space-3) var(--sa-space-5);">
212 @{ var bIdx = 0; }
213 @foreach (var balance in balances)
214 {
215 var initial = balance.UserName.Length >= 2
216 ? $"{balance.UserName.Split(' ')[0][0]}{(balance.UserName.Split(' ').Length > 1 ? balance.UserName.Split(' ')[1][0] : ' ')}"
217 : balance.UserName[0].ToString();
218 <div class="d-flex align-items-center gap-3 py-2">
219 <span class="sa-avatar sa-avatar-sm sa-avatar-@((bIdx % 8) + 1)" style="border: none; font-size: 0.65rem;">@initial.Trim()</span>
220 <span style="flex: 1; font-weight: 500; font-size: 0.9rem;">@balance.UserName</span>
221 <span class="sa-amount @(balance.NetBalance >= 0 ? "sa-amount-positive" : "sa-amount-negative")" style="font-size: 0.95rem;">
222 @(balance.NetBalance >= 0 ? "+" : "")@currencySymbol@balance.NetBalance.ToString("N2")
223 </span>
224 </div>
225 bIdx++;
226 }
227 </div>
228 </div>
229 </div>
230 </div>
231 }
232
233 <!-- Recent Expenses -->
234 @if (recentExpenses.Any())
235 {
236 <div class="sa-card-static mb-4">
237 <div class="sa-card-header d-flex justify-content-between align-items-center">
238 <span><i class="bi bi-receipt me-2"></i>@Localizer["Recent Expenses"]</span>
239 <a asp-controller="Expenses" asp-action="Index" asp-route-tripId="@tripId"
240 class="sa-btn sa-btn-ghost sa-btn-sm">
241 @Localizer["View All"] <i class="bi bi-arrow-right ms-1"></i>
242 </a>
243 </div>
244 <div>
245 @foreach (var expense in recentExpenses)
246 {
247 <div class="sa-expense-item">
248 <div class="sa-expense-icon sa-category-default">
249 <i class="bi bi-receipt"></i>
250 </div>
251 <div class="sa-expense-details">
252 <div class="sa-expense-desc">@(expense.Description ?? Localizer["No description"].Value)</div>
253 <div class="sa-expense-meta">
254 @(expense.PaidByUser != null ? $"{expense.PaidByUser.FirstName} {expense.PaidByUser.LastName}" : "?")
255 &middot; @expense.ExpenseDate.ToString("MMM dd")
256 </div>
257 </div>
258 <div class="sa-expense-amount">
259 @expense.Amount.ToString("N2")
260 </div>
261 </div>
262 }
263 </div>
264 </div>
265 }
266 else
267 {
268 <div class="sa-card-static mb-4">
269 <div class="sa-empty" style="padding: var(--sa-space-8) var(--sa-space-4);">
270 <div class="sa-empty-icon" style="font-size: 2.5rem;"><i class="bi bi-receipt"></i></div>
271 <div class="sa-empty-title" style="font-size: 1rem;">@Localizer["No expenses yet"]</div>
272 <p class="sa-empty-text" style="font-size: 0.875rem;">@Localizer["Start tracking your group expenses."]</p>
273 <a asp-controller="Expenses" asp-action="Create" asp-route-tripId="@tripId" class="sa-btn sa-btn-primary sa-btn-sm sa-btn-pill">
274 <i class="bi bi-plus-lg"></i> @Localizer["Add Expense"]
275 </a>
276 </div>
277 </div>
278 }
279
280 <!-- Description -->
281 @if (!string.IsNullOrEmpty(Model.Description))
282 {
283 <div class="sa-card-static">
284 <div class="sa-card-header">
285 <i class="bi bi-text-paragraph me-2"></i>@Localizer["Description"]
286 </div>
287 <div class="sa-card-body">
288 <p style="margin: 0; color: var(--sa-gray-600); line-height: 1.7;">@Model.Description</p>
289 </div>
290 </div>
291 }
292