diff --git a/Aberwyn/Controllers/BudgetApiController.cs b/Aberwyn/Controllers/BudgetApiController.cs index 17c0432..098129a 100644 --- a/Aberwyn/Controllers/BudgetApiController.cs +++ b/Aberwyn/Controllers/BudgetApiController.cs @@ -1,10 +1,9 @@ using Microsoft.AspNetCore.Mvc; -using Aberwyn.Data; // Adjust based on your project namespace -using Aberwyn.Models; // Adjust based on your project namespace +using Aberwyn.Data; +using Aberwyn.Models; using System.Collections.Generic; -using System.Globalization; // For month name indexing -namespace Aberwyn.Controllers // Adjust namespace based on your project +namespace Aberwyn.Controllers { [Route("api/[controller]")] [ApiController] @@ -18,23 +17,58 @@ namespace Aberwyn.Controllers // Adjust namespace based on your project } [HttpGet("items")] - public IActionResult GetBudgetItems(string month = null, int? year = null) + public IActionResult GetBudgetItems([FromQuery] int month, [FromQuery] int year) { - // Fetch all budget items from the service - var items = _budgetService.GetBudgetItems(); - - // Filter the items based on the provided month and year - if (!string.IsNullOrEmpty(month) && year.HasValue) - { - // Convert month name to month number (1 for January, 2 for February, etc.) - int monthNumber = Array.IndexOf(CultureInfo.CurrentCulture.DateTimeFormat.MonthNames, month) + 1; - - // Filter items where the month and year match - items = items.Where(item => item.Month == monthNumber && item.Year == year.Value).ToList(); - } - - // Return the filtered (or unfiltered) list as JSON + var items = _budgetService.GetBudgetItems(month, year); return Ok(items); } + + // New endpoint to get categories + [HttpGet("categories")] + public IActionResult GetCategories() + { + var categories = _budgetService.GetCategories(); + return Ok(categories); + } + + [HttpPut("items")] + public IActionResult UpdateBudgetItem([FromBody] BudgetItem item) + { + if (item == null || item.ID <= 0) + { + return BadRequest("Invalid budget item data."); + } + + // Assuming you have a method in your BudgetService to update an item + var result = _budgetService.UpdateBudgetItem(item); + + if (result) + { + return Ok("Item updated successfully."); + } + + return StatusCode(500, "Error updating item."); + } + + [HttpPost("items")] + public IActionResult AddBudgetItem([FromBody] BudgetItem item) + { + if (item == null || string.IsNullOrEmpty(item.Name) || item.Amount <= 0) + { + return BadRequest("Invalid budget item data."); + } + + // Assuming you have a method in your BudgetService to add an item + var result = _budgetService.AddBudgetItem(item); + + if (result) + { + return CreatedAtAction(nameof(GetBudgetItems), new { id = item.ID }, item); + } + + return StatusCode(500, "Error adding item."); + } + } + } diff --git a/Aberwyn/Controllers/HomeController.cs b/Aberwyn/Controllers/HomeController.cs index 632db3f..3137eec 100644 --- a/Aberwyn/Controllers/HomeController.cs +++ b/Aberwyn/Controllers/HomeController.cs @@ -29,32 +29,35 @@ namespace Aberwyn.Controllers return View(); } - // Action method to handle budget requests + public IActionResult RealEstate() + { + return View(); + } + + // Optimized Budget Action to fetch filtered data directly from the database public IActionResult Budget(string month, int? year) { - // Load all budget items from the database - var allItems = _budgetService.GetBudgetItems(); + // Default to current month and year if parameters are not provided + int selectedMonth = !string.IsNullOrEmpty(month) + ? Array.IndexOf(CultureInfo.CurrentCulture.DateTimeFormat.MonthNames, month) + 1 + : DateTime.Now.Month; - // Filter items by selected month and year (if provided) - if (!string.IsNullOrEmpty(month) && year.HasValue) + int selectedYear = year ?? DateTime.Now.Year; + + // Fetch budget items for the selected month and year directly from the database + var budgetItems = _budgetService.GetBudgetItems(selectedMonth, selectedYear); + + // Create the BudgetModel + var budgetModel = new BudgetModel { - int monthNumber = Array.IndexOf( - CultureInfo.CurrentCulture.DateTimeFormat.MonthNames, month) + 1; + BudgetItems = budgetItems.ToList() // Ensure this is a list + }; - allItems = allItems - .Where(item => item.Month == monthNumber && item.Year == year.Value) - .ToList(); - } - - // Group items by category - var groupedItems = allItems - .GroupBy(item => item.Category ?? "Uncategorized") // Handle null categories - .ToDictionary(g => g.Key, g => g.ToList()); - - // Pass the grouped items to the view - return View(groupedItems); + // Pass the BudgetModel to the view + return View(budgetModel); } + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { diff --git a/Aberwyn/Controllers/RealEstateApiController.cs b/Aberwyn/Controllers/RealEstateApiController.cs new file mode 100644 index 0000000..88c76b5 --- /dev/null +++ b/Aberwyn/Controllers/RealEstateApiController.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Mvc; +using System.Net.Http; +using System.Threading.Tasks; + +[Route("api/[controller]")] +[ApiController] +public class RealEstateApiController : ControllerBase +{ + private readonly HttpClient _httpClient; + + public RealEstateApiController(HttpClient httpClient) + { + _httpClient = httpClient; + } + + [HttpPost("download")] + public async Task DownloadWebsite([FromBody] WebsiteRequest request) + { + if (string.IsNullOrWhiteSpace(request.Url)) + { + return BadRequest("URL cannot be empty."); + } + + try + { + var response = await _httpClient.GetAsync(request.Url); + response.EnsureSuccessStatusCode(); + var content = await response.Content.ReadAsStringAsync(); + + // Optional: Save content to a file + System.IO.File.WriteAllText("DownloadedWebsite.html", content); + + return Ok("Website downloaded successfully."); + } + catch (HttpRequestException ex) + { + return StatusCode(500, $"Error downloading website: {ex.Message}"); + } + } +} + +public class WebsiteRequest +{ + public string Url { get; set; } +} diff --git a/Aberwyn/Data/BudgetService.cs b/Aberwyn/Data/BudgetService.cs index 92c43f3..4677935 100644 --- a/Aberwyn/Data/BudgetService.cs +++ b/Aberwyn/Data/BudgetService.cs @@ -1,10 +1,9 @@ -namespace Aberwyn.Data -{ - using MySql.Data.MySqlClient; // Make sure you have this package installed - using System.Collections.Generic; - using Microsoft.Extensions.Configuration; // Add this for IConfiguration - using Aberwyn.Models; // Adjust the namespace +using MySql.Data.MySqlClient; +using System.Collections.Generic; +using Aberwyn.Models; +namespace Aberwyn.Data +{ public class BudgetService { private readonly IConfiguration _configuration; @@ -14,42 +13,130 @@ _configuration = configuration; } - public List GetBudgetItems() + public MySqlConnection GetConnection() { - List budgetItems = new List(); - string connectionString = _configuration.GetConnectionString("DefaultConnection"); // Use connection string from config - - using (var connection = new MySqlConnection(connectionString)) + var connectionString = _configuration.GetConnectionString("DefaultConnection"); + return new MySqlConnection(connectionString); + } + public bool UpdateBudgetItem(BudgetItem item) + { + using (var connection = GetConnection()) { connection.Open(); - string query = @" - SELECT b.Name, b.Amount, b.Month, b.Year, - c1.Name AS Category, c2.Name AS SubCategoryName - FROM tblBudgetItems b - LEFT JOIN tblCategories c1 ON b.Category = c1.idtblCategories - LEFT JOIN tblCategories c2 ON b.SubCategory = c2.idtblCategories"; - using (MySqlCommand cmd = new MySqlCommand(query, connection)) + string query = @" + UPDATE tblBudgetItems + SET Name = @name, Amount = @amount + WHERE idtblBudgetItems = @id"; + + using (var cmd = new MySqlCommand(query, connection)) { - using (MySqlDataReader reader = cmd.ExecuteReader()) + cmd.Parameters.AddWithValue("@name", item.Name); + cmd.Parameters.AddWithValue("@amount", item.Amount); + cmd.Parameters.AddWithValue("@id", item.ID); + return cmd.ExecuteNonQuery() > 0; // Returns true if one or more rows are updated + } + } + } + + public List GetBudgetItems(int month, int year) + { + var budgetItems = new List(); + + using (var connection = GetConnection()) + { + connection.Open(); + + string query = @" + SELECT + b.idtblBudgetItems AS id, + b.Name AS item_name, + b.Amount AS amount, + c1.Name AS category, + b.Month, + b.Year, + b.Description AS description + FROM tblBudgetItems b + LEFT JOIN tblCategories c1 ON b.Category = c1.idtblCategories + WHERE b.Month = @month AND b.Year = @year"; + + using (var cmd = new MySqlCommand(query, connection)) + { + cmd.Parameters.AddWithValue("@month", month); + cmd.Parameters.AddWithValue("@year", year); + + using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { budgetItems.Add(new BudgetItem { - Name = reader.GetString("Name"), - Amount = reader.GetDecimal("Amount"), // Changed to GetDecimal for Amount + ID = reader.GetInt32("id"), + Name = reader.GetString("item_name"), // Updated alias + Amount = reader.GetDecimal("amount"), + Category = reader.GetString("category"), Month = reader.GetInt32("Month"), Year = reader.GetInt32("Year"), - Category = reader.IsDBNull(reader.GetOrdinal("Category")) ? null : reader.GetString("Category"), // Retrieve as string - SubCategory = reader.IsDBNull(reader.GetOrdinal("SubCategoryName")) ? null : reader.GetString("SubCategoryName") // SubCategory name + Description = reader.IsDBNull(reader.GetOrdinal("description")) + ? null + : reader.GetString("description") }); } } } } - return budgetItems; // Return the list of budget items + return budgetItems; + } + + public bool AddBudgetItem(BudgetItem item) + { + using (var connection = GetConnection()) + { + connection.Open(); + + string query = @" + INSERT INTO tblBudgetItems (Name, Amount, Category, Month, Year) + VALUES (@name, @amount, @category, @month, @year)"; + + using (var cmd = new MySqlCommand(query, connection)) + { + cmd.Parameters.AddWithValue("@name", item.Name); + cmd.Parameters.AddWithValue("@amount", item.Amount); + cmd.Parameters.AddWithValue("@category", item.Category); + cmd.Parameters.AddWithValue("@month", item.Month); + cmd.Parameters.AddWithValue("@year", item.Year); + return cmd.ExecuteNonQuery() > 0; // Returns true if a row was inserted + } + } + } + + + // New method to fetch all categories + public List GetCategories() + { + var categories = new List(); + + using (var connection = GetConnection()) + { + connection.Open(); + + string query = "SELECT Name FROM tblCategories"; // Adjust based on your table structure + + using (var cmd = new MySqlCommand(query, connection)) + { + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + categories.Add(reader.GetString("Name")); + } + } + } + } + + return categories; } } + } diff --git a/Aberwyn/Models/BudgetItem.cs b/Aberwyn/Models/BudgetItem.cs index d86b1de..ac55af8 100644 --- a/Aberwyn/Models/BudgetItem.cs +++ b/Aberwyn/Models/BudgetItem.cs @@ -2,8 +2,9 @@ { public class BudgetModel { - public List BudgetItems { get; set; } + public List BudgetItems { get; set; } = new List(); // Initialize with an empty list } + public class BudgetItem { public int ID { get; set; } diff --git a/Aberwyn/Program.cs b/Aberwyn/Program.cs index 3dc4a63..6ffd945 100644 --- a/Aberwyn/Program.cs +++ b/Aberwyn/Program.cs @@ -7,6 +7,8 @@ var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); // Add this line to enable Razor Pages +builder.Services.AddHttpClient(); // Register HttpClient + // Configure your DbContext with MySQL builder.Services.AddDbContext(options => diff --git a/Aberwyn/Views/Home/Budget.cshtml b/Aberwyn/Views/Home/Budget.cshtml index d14b9d1..84ed2b4 100644 --- a/Aberwyn/Views/Home/Budget.cshtml +++ b/Aberwyn/Views/Home/Budget.cshtml @@ -1,151 +1,115 @@ - +@model Aberwyn.Models.BudgetModel + +@{ + Layout = "_Layout"; // Assuming you have a layout file named _Layout.cshtml +} + + - + Budget Overview - + -

Budget Overview

+
+

Budget Overview

- -
- - + + + - - + + - -
+ + - - - - - - - - - - - - - - - - - - - -
CategoryItem Name {{ $index + 1 }}Amount {{ $index + 1 }}
{{ category }} - {{ getItemByCategory(category, $index) ? getItemByCategory(category, $index).name : '-' }} - - {{ getItemByCategory(category, $index) && getItemByCategory(category, $index).amount !== 0 ? getItemByCategory(category, $index).amount : '-' }} -
Total - {{ sumForCategory(category) }} -
+
+
+
+ {{ category }} + Total: {{ sumForCategory(category) }} +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+
diff --git a/Aberwyn/Views/Home/RealEstate.cshtml b/Aberwyn/Views/Home/RealEstate.cshtml new file mode 100644 index 0000000..6531710 --- /dev/null +++ b/Aberwyn/Views/Home/RealEstate.cshtml @@ -0,0 +1,38 @@ +@{ + ViewData["Title"] = "Real Estate Downloader"; +} + +

Real Estate Website Downloader

+ +
+ + + +
+ +
+

{{ downloadStatus }}

+
+ + + diff --git a/Aberwyn/Views/Shared/_Layout.cshtml b/Aberwyn/Views/Shared/_Layout.cshtml index 51b4691..eb95389 100644 --- a/Aberwyn/Views/Shared/_Layout.cshtml +++ b/Aberwyn/Views/Shared/_Layout.cshtml @@ -1,50 +1,93 @@ - - - - - - - @ViewData["Title"] - Aberwyn - - - - - -
- -
-
-
- @RenderBody() -
-
+@using Aberwyn.Models +@model Aberwyn.Models.BudgetModel -
-
- © 2024 - Aberwyn - Privacy -
-
- - - - @await RenderSectionAsync("Scripts", required: false) + + + + + + + + Your Page Title + +
+
+

+ Louise + Elias + William + Elin + Ludwig +

+
+
+ + +
+ +
+ @RenderBody() +
+ +
+ + + +@await RenderSectionAsync("Scripts", required: false) diff --git a/Aberwyn/wwwroot/css/site.css b/Aberwyn/wwwroot/css/site.css index f27e5ad..7b4524b 100644 --- a/Aberwyn/wwwroot/css/site.css +++ b/Aberwyn/wwwroot/css/site.css @@ -1,18 +1,257 @@ -html { - font-size: 14px; -} - -@media (min-width: 768px) { - html { - font-size: 16px; - } -} - -html { - position: relative; - min-height: 100%; -} - +/* Base Styles */ body { - margin-bottom: 60px; -} \ No newline at end of file + background-color: #1F2C3C; + color: #4b004b; + font-family: 'Roboto', sans-serif; + margin: 0; + padding: 20px; /* Keep padding for general layout */ +} + +/* Container for Centering and Max Width */ +.budget-page { + width: 100%; /* Make the page full width */ + max-width: 1900px; /* You can adjust or remove this */ + margin: 0 auto; /* Centering */ + padding: 0; /* Removed extra padding for budget page */ +} + +/* Grid Layout */ +.grid-container { + display: grid; + grid-template-columns: 250px 1fr 250px; + gap: 10px; /* Keep existing gap */ + padding: 0; /* Removed padding for grid container */ + margin: 0; /* Removed margin for grid container */ +} + +.left-sidebar, .sidebar-budget-right { + background-color: #f4f4f4; + border-radius: 12px; + padding: 10px; /* Adjusted padding for sidebars */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.main-content { + padding: 10px; + background-color: #f4f4f4; /* Light gray instead of white */ + border-radius: 12px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.nav-link { + color: #4b004b; +} + + .nav-link:hover { + text-decoration: underline; + } + +@media (max-width: 768px) { + .grid-container { + grid-template-columns: 1fr; /* Responsive layout */ + } +} + + +/* Budget Container */ +.budget-container { + display: flex; + flex-wrap: wrap; + gap: 10px; /* Reduced gap for tighter layout */ + padding: 0; /* Removed padding */ + margin: 0; /* Removed margin */ +} + +/* Category Block */ +.category-block { + flex: 1 1 300px; + max-width: 230px; + min-width: 180px; + margin: 0; /* Removed margin */ +} + +.category-header { + display: flex; + justify-content: space-between; /* Aligns the category name and total */ + align-items: center; /* Center vertically */ + padding: 5px; /* Retain padding */ + background-color: #6a0dad; /* Adjust color as needed */ + color: #ffffff; + border-radius: 8px; + margin-bottom: 10px; + font-weight: bold; + max-width: 100%; /* Ensures the header doesn't exceed the width of its container */ + box-sizing: border-box; /* Include padding in width calculation */ +} + +.total { + font-weight: bold; + color: #ffffff; /* Keep white for visibility */ + background-color: rgba(106, 13, 173, 0.7); /* Add a semi-transparent background for contrast */ + padding: 5px 10px; /* Add some padding for spacing */ + border-radius: 5px; /* Rounded corners */ +} + +/* Items Layout */ +.items-wrapper { + display: flex; + min-width: 200px; + flex-direction: column; + padding: 0; /* Removed padding */ + margin: 0; /* Removed margin */ +} + +/* Item Styles */ +.item { + display: flex; + align-items: center; + margin-bottom: 5px; /* Space between items */ + gap: 5px; /* Reduced gap between item components */ +} + +/* Input Styles */ +.item-name input { + flex: 1; /* Compact size for amount field */ + max-width: 20ch; /* Allow up to 7 characters */ + padding: 6px; /* Slightly reduced padding */ + border: none; + border-radius: 5px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + background-color: #f9f9f9; + transition: background-color 0.3s; +} + +.item-amount input { + flex: 1; /* Compact size for amount field */ + max-width: 7ch; /* Allow up to 7 characters */ + padding: 6px; /* Slightly reduced padding */ + border: none; + border-radius: 5px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + background-color: #f9f9f9; + transition: background-color 0.3s; +} + + /* Focus Effect */ + .item-name input:focus, + .item-amount input:focus { + background-color: #e6e6e6; + } + +/* Add Item Button */ +.add-item { + display: flex; /* Use flex to center the "+" */ + align-items: center; /* Center the "+" vertically */ + justify-content: center; /* Center the "+" horizontally */ + height: 25px; /* Adjusted height to match input fields */ + cursor: pointer; + color: #ffffff; /* White text color for contrast */ + background-color: #6a0dad; /* Use the same color as the header for consistency */ + margin-top: 5px; /* Reduced space above the button */ + border: none; /* No border */ + border-radius: 5px; /* Rounded corners */ + font-size: 15px; /* Adjusted font size for better fit */ + transition: background-color 0.3s, transform 0.3s; /* Transition for hover effect */ +} + + /* Hover Effect for Add Item Button */ + .add-item:hover { + background-color: #5a0c9a; /* Darker shade on hover */ + transform: scale(1.05); /* Slightly enlarge on hover for emphasis */ + } + +.header-container { + display: flex; + justify-content: flex-start; /* Aligns the header to the left */ + align-items: center; /* Centers items vertically */ + margin-bottom: 0px; /* Adjust space below the header as needed */ +} + +.family-header { + text-align: left; /* Ensures text aligns to the left */ + margin: 0; /* Removes default margin */ +} + +.initial { + font-weight: bold; /* Adjust as needed */ + font-size: 1.2em; /* Adjust size for initials */ +} + +.name { + color: #FFFFFF; /* White for names */ + font-size: 0.4em; /* Adjust size as needed */ +} + +/* Specific colors for initials */ +.initial.L { + color: #FF0000; /* Red for first L */ +} + +.initial.E { + color: #FF7F00; /* Orange for first E */ +} + +.initial.W { + color: #FFFF00; /* Yellow */ +} + +.initial.E2 { /* Second E */ + color: #00FF00; /* Green */ +} + +.initial.L2 { /* Second L */ + color: #0088CC; /* Blue */ +} + +.navbar-nav { + list-style-type: none; /* Remove bullet points */ + padding: 0; /* Remove padding */ +} + +.nav-item { + margin: 10px 0; /* Adjust spacing between links */ +} + +.nav-link { + text-decoration: none; /* Remove underline */ + color: #000; /* Link color */ + display: flex; /* Use flexbox to align icon and text */ + align-items: center; /* Center items vertically */ +} + + .nav-link i { + margin-right: 8px; /* Space between icon and text */ + font-size: 1.2em; /* Adjust icon size */ + } + + +/* Sidebar Right Styles */ +.right-sidebar { + background-color: #f8f9fa; /* Use your existing background color */ + padding: 10px; /* Reduce padding for compactness */ + margin: 0; /* Remove any margins */ + border-radius: 8px; /* Optional: Add rounded corners */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Optional: Add shadow for depth */ +} + +.sidebar-budget-header { + cursor: pointer; /* Indicate clickable header */ + font-weight: bold; /* Make header bold */ + margin: 0; /* Remove default margin */ + padding-bottom: 5px; /* Space below header */ +} + +.sidebar-budget-container { + margin-top: 10px; /* Space above budget items */ +} + +.sidebar-budget-item { + display: flex; + justify-content: space-between; /* Space out name and amount */ + padding: 5px 0; /* Reduced padding for compactness */ + border-bottom: 1px solid #e0e0e0; /* Optional: Divider line */ +} + + .sidebar-budget-item:last-child { + border-bottom: none; /* Remove border from last item */ + } \ No newline at end of file