profileShare

rasmusjy / splitapp-backend-modular-monolith

Read-only snapshot

No repository description.

main default branch 418 files Expires Sep 13, 2026, 9:06 AM
WishlistClientController.cs 8,927 bytes
1 using SplitApp.WebApp.Application.DTO;
2 using SplitApp.WebApp.Application.Services;
3 using SplitApp.Modules.Trips.Domain.Entities;
4 using SplitApp.Modules.Trips.Domain.Enums;
5 using SplitApp.Modules.Expenses.Domain.Entities;
6 using SplitApp.Modules.Expenses.Domain.Enums;
7 using SplitApp.Modules.Users.Domain.Entities;
8 using SplitApp.Modules.Users.Domain.Entities;
9 using Microsoft.AspNetCore.Authorization;
10 using Microsoft.AspNetCore.Identity;
11 using Microsoft.AspNetCore.Mvc;
12 using Microsoft.AspNetCore.Mvc.Rendering;
13
14 namespace SplitApp.WebApp.Controllers;
15
16 [Authorize]
17 public class WishlistClientController : Controller
18 {
19 private readonly ITripService _tripService;
20 private readonly IWishlistService _wishlistService;
21 private readonly UserManager<AppUser> _userManager;
22
23 public WishlistClientController(
24 ITripService tripService,
25 IWishlistService wishlistService,
26 UserManager<AppUser> userManager)
27 {
28 _tripService = tripService;
29 _wishlistService = wishlistService;
30 _userManager = userManager;
31 }
32
33 private Guid GetUserId() => Guid.Parse(_userManager.GetUserId(User)!);
34
35 // GET: WishlistClient?tripId=xxx
36 public async Task<IActionResult> Index(Guid tripId)
37 {
38 var userId = GetUserId();
39 if (!await _tripService.IsParticipantAsync(tripId, userId)) return Forbid();
40
41 var trip = await _tripService.GetByIdAsync(tripId, userId);
42 if (trip == null) return NotFound();
43
44 var items = await _wishlistService.GetByTripIdAsync(tripId, userId);
45
46 ViewData["CurrentUserId"] = userId;
47
48 var model = items.Select(item => new WishlistItemViewModel
49 {
50 Id = item.Id,
51 AddedByUserId = item.AddedByUserId,
52 Title = item.Title,
53 Description = item.Description,
54 Category = item.Category,
55 Priority = item.Priority,
56 EstimatedCost = item.EstimatedCost,
57 Url = item.Url,
58 Location = item.Location,
59 IsCompleted = item.IsCompleted,
60 AddedByName = item.AddedByUserFullName ?? "Unknown",
61 VoteCount = item.VoteCount,
62 UserHasVoted = item.VoterUserIds.Contains(userId)
63 }).ToList();
64
65 ViewData["TripId"] = tripId;
66 ViewData["TripName"] = trip.Name;
67
68 return View(model);
69 }
70
71 // GET: WishlistClient/Create?tripId=xxx
72 public async Task<IActionResult> Create(Guid tripId)
73 {
74 var userId = GetUserId();
75 if (!await _tripService.IsParticipantAsync(tripId, userId)) return Forbid();
76
77 ViewData["TripId"] = tripId;
78 PopulateEnumDropdowns();
79 return View(new TripWishlistItemBllDto { TripId = tripId });
80 }
81
82 // POST: WishlistClient/Create
83 [HttpPost]
84 [ValidateAntiForgeryToken]
85 public async Task<IActionResult> Create(TripWishlistItemBllDto item)
86 {
87 var userId = GetUserId();
88
89 if (ModelState.IsValid)
90 {
91 var (created, errorCode) = await _wishlistService.CreateAsync(item, userId);
92 if (created == null)
93 {
94 if (errorCode == "forbidden") return Forbid();
95 return NotFound();
96 }
97 return RedirectToAction(nameof(Index), new { tripId = item.TripId });
98 }
99
100 ViewData["TripId"] = item.TripId;
101 PopulateEnumDropdowns();
102 return View(item);
103 }
104
105 // POST: WishlistClient/Vote/5
106 [HttpPost]
107 [ValidateAntiForgeryToken]
108 public async Task<IActionResult> Vote(Guid id)
109 {
110 var userId = GetUserId();
111
112 var item = await _wishlistService.GetByIdRawAsync(id);
113 if (item == null) return NotFound();
114
115 var (ok, errorCode) = await _wishlistService.ToggleVoteAsync(id, userId);
116 if (!ok)
117 {
118 return errorCode switch
119 {
120 "notfound" => NotFound(),
121 "forbidden" => Forbid(),
122 _ => NotFound()
123 };
124 }
125
126 return RedirectToAction(nameof(Index), new { tripId = item.TripId });
127 }
128
129 // POST: WishlistClient/Complete/5
130 [HttpPost]
131 [ValidateAntiForgeryToken]
132 public async Task<IActionResult> Complete(Guid id)
133 {
134 var userId = GetUserId();
135
136 var item = await _wishlistService.GetByIdRawAsync(id);
137 if (item == null) return NotFound();
138
139 var (ok, errorCode) = await _wishlistService.ToggleCompleteAsync(id, userId);
140 if (!ok)
141 {
142 return errorCode switch
143 {
144 "notfound" => NotFound(),
145 "forbidden" => Forbid(),
146 _ => NotFound()
147 };
148 }
149
150 return RedirectToAction(nameof(Index), new { tripId = item.TripId });
151 }
152
153 // GET: WishlistClient/Edit/5
154 public async Task<IActionResult> Edit(Guid id)
155 {
156 var userId = GetUserId();
157
158 var item = await _wishlistService.GetByIdAsync(id, userId);
159 if (item == null)
160 {
161 var raw = await _wishlistService.GetByIdRawAsync(id);
162 if (raw == null) return NotFound();
163 return Forbid();
164 }
165
166 if (item.AddedByUserId != userId) return Forbid();
167
168 ViewData["TripId"] = item.TripId;
169 PopulateEnumDropdowns();
170 return View(item);
171 }
172
173 // POST: WishlistClient/Edit/5
174 [HttpPost]
175 [ValidateAntiForgeryToken]
176 public async Task<IActionResult> Edit(Guid id, TripWishlistItemBllDto item)
177 {
178 if (id != item.Id) return NotFound();
179
180 var userId = GetUserId();
181
182 if (ModelState.IsValid)
183 {
184 var preUpdate = await _wishlistService.GetByIdRawAsync(id);
185 if (preUpdate == null) return NotFound();
186
187 var (ok, errorCode) = await _wishlistService.UpdateAsync(id, item, userId);
188 if (!ok)
189 {
190 return errorCode switch
191 {
192 "notfound" => NotFound(),
193 "forbidden" => Forbid(),
194 _ => NotFound()
195 };
196 }
197
198 return RedirectToAction(nameof(Index), new { tripId = preUpdate.TripId });
199 }
200
201 var raw = await _wishlistService.GetByIdRawAsync(id);
202 if (raw == null) return NotFound();
203 ViewData["TripId"] = raw.TripId;
204 PopulateEnumDropdowns();
205 return View(item);
206 }
207
208 // GET: WishlistClient/Delete/5
209 public async Task<IActionResult> Delete(Guid id)
210 {
211 var userId = GetUserId();
212
213 var item = await _wishlistService.GetByIdAsync(id, userId);
214 if (item == null)
215 {
216 var raw = await _wishlistService.GetByIdRawAsync(id);
217 if (raw == null) return NotFound();
218 return Forbid();
219 }
220 if (item.AddedByUserId != userId) return Forbid();
221
222 // Re-fetch with includes via trip items list
223 var tripItems = await _wishlistService.GetByTripIdAsync(item.TripId, userId);
224 var itemWithDetails = tripItems.FirstOrDefault(w => w.Id == id);
225
226 ViewData["TripId"] = item.TripId;
227 return View(itemWithDetails ?? item);
228 }
229
230 // POST: WishlistClient/Delete/5
231 [HttpPost, ActionName("Delete")]
232 [ValidateAntiForgeryToken]
233 public async Task<IActionResult> DeleteConfirmed(Guid id)
234 {
235 var userId = GetUserId();
236
237 var existing = await _wishlistService.GetByIdRawAsync(id);
238 if (existing == null) return NotFound();
239 var tripId = existing.TripId;
240
241 var (ok, errorCode) = await _wishlistService.DeleteAsync(id, userId);
242 if (!ok)
243 {
244 return errorCode switch
245 {
246 "notfound" => NotFound(),
247 "forbidden" => Forbid(),
248 _ => NotFound()
249 };
250 }
251
252 return RedirectToAction(nameof(Index), new { tripId });
253 }
254
255 private void PopulateEnumDropdowns()
256 {
257 ViewData["Categories"] = new SelectList(
258 Enum.GetValues<EWishlistCategory>().Select(e => new { Value = (int)e, Text = e.ToString() }),
259 "Value", "Text");
260 ViewData["Priorities"] = new SelectList(
261 Enum.GetValues<EWishlistPriority>().Select(e => new { Value = (int)e, Text = e.ToString() }),
262 "Value", "Text");
263 }
264 }
265
266 public class WishlistItemViewModel
267 {
268 public Guid Id { get; set; }
269 public Guid AddedByUserId { get; set; }
270 public string Title { get; set; } = default!;
271 public string? Description { get; set; }
272 public EWishlistCategory Category { get; set; }
273 public EWishlistPriority Priority { get; set; }
274 public decimal? EstimatedCost { get; set; }
275 public string? Url { get; set; }
276 public string? Location { get; set; }
277 public bool IsCompleted { get; set; }
278 public string AddedByName { get; set; } = default!;
279 public int VoteCount { get; set; }
280 public bool UserHasVoted { get; set; }
281 }
282