Css
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
45
Aberwyn/Controllers/RealEstateApiController.cs
Normal file
45
Aberwyn/Controllers/RealEstateApiController.cs
Normal file
@@ -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<IActionResult> 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; }
|
||||
}
|
||||
@@ -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<BudgetItem> GetBudgetItems()
|
||||
public MySqlConnection GetConnection()
|
||||
{
|
||||
List<BudgetItem> budgetItems = new List<BudgetItem>();
|
||||
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<BudgetItem> GetBudgetItems(int month, int year)
|
||||
{
|
||||
var budgetItems = new List<BudgetItem>();
|
||||
|
||||
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<string> GetCategories()
|
||||
{
|
||||
var categories = new List<string>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
{
|
||||
public class BudgetModel
|
||||
{
|
||||
public List<BudgetItem> BudgetItems { get; set; }
|
||||
public List<BudgetItem> BudgetItems { get; set; } = new List<BudgetItem>(); // Initialize with an empty list
|
||||
}
|
||||
|
||||
public class BudgetItem
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
@@ -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<BudgetContext>(options =>
|
||||
|
||||
@@ -1,151 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
@model Aberwyn.Models.BudgetModel
|
||||
|
||||
@{
|
||||
Layout = "_Layout"; // Assuming you have a layout file named _Layout.cshtml
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="budgetApp">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta charset="utf-8">
|
||||
<title>Budget Overview</title>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
|
||||
<style>
|
||||
.spreadsheet-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f1f1f1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-amount {
|
||||
color: #007bff;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="~/css/site.css">
|
||||
</head>
|
||||
<body ng-controller="BudgetController">
|
||||
|
||||
<h1>Budget Overview</h1>
|
||||
<div class="budget-page">
|
||||
<h1>Budget Overview</h1>
|
||||
|
||||
<!-- Month and Year Dropdowns -->
|
||||
<form ng-submit="filterBudget()">
|
||||
<label for="month">Month:</label>
|
||||
<select id="month" ng-model="selectedMonth">
|
||||
<option ng-repeat="month in months" value="{{ month }}">{{ month }}</option>
|
||||
</select>
|
||||
<form ng-submit="filterBudget()">
|
||||
<label for="month">Month:</label>
|
||||
<select id="month" ng-model="selectedMonth">
|
||||
<option ng-repeat="month in months" value="{{ month }}">{{ month }}</option>
|
||||
</select>
|
||||
|
||||
<label for="year">Year:</label>
|
||||
<select id="year" ng-model="selectedYear">
|
||||
<option ng-repeat="year in years" value="{{ year }}">{{ year }}</option>
|
||||
</select>
|
||||
<label for="year">Year:</label>
|
||||
<select id="year" ng-model="selectedYear">
|
||||
<option ng-repeat="year in years" value="{{ year }}">{{ year }}</option>
|
||||
</select>
|
||||
|
||||
<button type="submit">Filter</button>
|
||||
</form>
|
||||
<button type="submit">Filter</button>
|
||||
</form>
|
||||
|
||||
<table class="spreadsheet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Category</th>
|
||||
<th ng-repeat="i in [].constructor(maxRows()) track by $index">Item Name {{ $index + 1 }}</th>
|
||||
<th ng-repeat="i in [].constructor(maxRows()) track by $index">Amount {{ $index + 1 }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="category in categories">
|
||||
<td><strong>{{ category }}</strong></td>
|
||||
<td ng-repeat="i in [].constructor(maxRows()) track by $index">
|
||||
<span class="item-name">{{ getItemByCategory(category, $index) ? getItemByCategory(category, $index).name : '-' }}</span>
|
||||
</td>
|
||||
<td ng-repeat="i in [].constructor(maxRows()) track by $index">
|
||||
<span class="item-amount">{{ getItemByCategory(category, $index) && getItemByCategory(category, $index).amount !== 0 ? getItemByCategory(category, $index).amount : '-' }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total</td>
|
||||
<td ng-repeat="category in categories">
|
||||
{{ sumForCategory(category) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="budget-container">
|
||||
<div ng-repeat="category in categories" class="category-block">
|
||||
<div class="category-header">
|
||||
{{ category }}
|
||||
<span class="total">Total: {{ sumForCategory(category) }}</span> <!-- Total moved to the header -->
|
||||
</div>
|
||||
|
||||
<div class="items-wrapper">
|
||||
<div ng-repeat="item in getItemsByCategory(category)" class="item">
|
||||
<div class="item-name">
|
||||
<input type="text" ng-model="item.name" ng-blur="updateItem(item)" placeholder="Name" />
|
||||
</div>
|
||||
<div class="item-amount">
|
||||
<input type="number" ng-model="item.amount" ng-blur="updateItem(item)" placeholder="Amount" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="add-item" ng-click="addNewItem(category)">+</div> <!-- Button for adding items -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
angular.module('budgetApp', [])
|
||||
.controller('BudgetController', function($scope, $http) {
|
||||
.controller('BudgetController', function ($scope, $http) {
|
||||
const today = new Date();
|
||||
|
||||
// Set initial values
|
||||
$scope.months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
// 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);
|
||||
$scope.categories = ['House', 'Bills', 'Services', 'Unplanned', 'Car', 'Planned', 'Save'];
|
||||
|
||||
// Default to current month and year
|
||||
$scope.selectedMonth = $scope.months[today.getMonth()];
|
||||
$scope.selectedMonth = (today.getMonth() + 1).toString();
|
||||
$scope.selectedYear = today.getFullYear();
|
||||
$scope.categories = [];
|
||||
$scope.budgetItems = @Html.Raw(Json.Serialize(Model.BudgetItems)); // Initialize budgetItems with server-side data
|
||||
|
||||
$scope.budgetItems = [];
|
||||
$scope.loadCategories = function () {
|
||||
$http.get('/api/budgetapi/categories').then(response => {
|
||||
$scope.categories = response.data;
|
||||
}).catch(error => console.error("Error fetching categories:", error));
|
||||
};
|
||||
|
||||
// Load budget items on page load
|
||||
loadBudgetItems();
|
||||
|
||||
function loadBudgetItems() {
|
||||
$http.get('/api/budgetapi/items').then(function(response) {
|
||||
$scope.budgetItems = response.data;
|
||||
console.log("Initial items:", response.data); // Log initial items
|
||||
}).catch(function(error) {
|
||||
console.error("Error loading budget items:", error);
|
||||
});
|
||||
}
|
||||
|
||||
// Filter budget items
|
||||
$scope.filterBudget = function() {
|
||||
$scope.filterBudget = function () {
|
||||
$http.get('/api/budgetapi/items', {
|
||||
params: {
|
||||
month: $scope.selectedMonth,
|
||||
year: $scope.selectedYear
|
||||
}
|
||||
}).then(function(response) {
|
||||
params: { month: $scope.selectedMonth, year: $scope.selectedYear }
|
||||
}).then(response => {
|
||||
$scope.budgetItems = response.data;
|
||||
console.log("Filtered items:", response.data); // Log filtered items
|
||||
}).catch(function(error) {
|
||||
console.error("Error fetching budget items:", error);
|
||||
});
|
||||
}).catch(error => console.error("Error fetching budget items:", error));
|
||||
};
|
||||
|
||||
// Get item by category and index
|
||||
$scope.getItemByCategory = function(category, index) {
|
||||
const items = $scope.budgetItems.filter(item => item.category === category);
|
||||
return items[index] || null; // Return null if no item exists
|
||||
$scope.getItemsByCategory = function (category) {
|
||||
return $scope.budgetItems.filter(item => item.category === category);
|
||||
};
|
||||
|
||||
// Calculate the maximum number of rows needed
|
||||
$scope.maxRows = function() {
|
||||
return Math.max(...$scope.categories.map(category =>
|
||||
$scope.budgetItems.filter(item => item.category === category).length
|
||||
));
|
||||
};
|
||||
|
||||
// Function to sum amounts for each category
|
||||
$scope.sumForCategory = function(category) {
|
||||
$scope.sumForCategory = function (category) {
|
||||
return $scope.budgetItems
|
||||
.filter(item => item.category === category)
|
||||
.reduce((total, item) => total + item.amount, 0);
|
||||
};
|
||||
|
||||
$scope.updateItem = function (item) {
|
||||
$http.put('/api/budgetapi/items', item)
|
||||
.then(response => console.log("Item updated:", response.data))
|
||||
.catch(error => console.error("Error updating item:", error));
|
||||
};
|
||||
|
||||
$scope.addNewItem = function (category) {
|
||||
const newItem = {
|
||||
name: '',
|
||||
amount: 0,
|
||||
category: category,
|
||||
month: $scope.selectedMonth,
|
||||
year: $scope.selectedYear
|
||||
};
|
||||
$scope.budgetItems.push(newItem);
|
||||
};
|
||||
|
||||
$scope.loadCategories();
|
||||
$scope.filterBudget();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
38
Aberwyn/Views/Home/RealEstate.cshtml
Normal file
38
Aberwyn/Views/Home/RealEstate.cshtml
Normal file
@@ -0,0 +1,38 @@
|
||||
@{
|
||||
ViewData["Title"] = "Real Estate Downloader";
|
||||
}
|
||||
|
||||
<h2>Real Estate Website Downloader</h2>
|
||||
|
||||
<form method="post" ng-submit="downloadWebsite()">
|
||||
<label for="urlInput">Enter Website URL:</label>
|
||||
<input type="text" id="urlInput" ng-model="websiteUrl" placeholder="https://example.com" required />
|
||||
<button type="submit">Download</button>
|
||||
</form>
|
||||
|
||||
<div ng-if="downloadStatus">
|
||||
<p>{{ downloadStatus }}</p>
|
||||
</div>
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
|
||||
<script>
|
||||
angular.module('realEstateApp', [])
|
||||
.controller('RealEstateController', function ($scope, $http) {
|
||||
$scope.websiteUrl = '';
|
||||
$scope.downloadStatus = '';
|
||||
|
||||
$scope.downloadWebsite = function () {
|
||||
if ($scope.websiteUrl) {
|
||||
$http.post('/api/realestate/download', { url: $scope.websiteUrl })
|
||||
.then(function (response) {
|
||||
$scope.downloadStatus = 'Download successful!';
|
||||
console.log(response.data); // Optional: Handle the downloaded data
|
||||
})
|
||||
.catch(function (error) {
|
||||
$scope.downloadStatus = 'Error downloading website.';
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
@@ -1,50 +1,93 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - Aberwyn</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/Aberwyn.styles.css" asp-append-version="true" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Aberwyn</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
||||
<ul class="navbar-nav flex-grow-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main role="main" class="pb-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
</div>
|
||||
@using Aberwyn.Models
|
||||
@model Aberwyn.Models.BudgetModel
|
||||
|
||||
<footer class="border-top footer text-muted">
|
||||
<div class="container">
|
||||
© 2024 - Aberwyn - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<title>Your Page Title</title>
|
||||
</head>
|
||||
<header class="header">
|
||||
<div class="header-container">
|
||||
<h1 class="family-header">
|
||||
<span class="initial L">L</span><span class="name">ouise</span>
|
||||
<span class="initial E">E</span><span class="name">lias</span>
|
||||
<span class="initial W">W</span><span class="name">illiam</span>
|
||||
<span class="initial E2">E</span><span class="name">lin</span>
|
||||
<span class="initial L2">L</span><span class="name">udwig</span>
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
<body>
|
||||
|
||||
<div class="grid-container">
|
||||
<aside class="left-sidebar"> <!-- Left sidebar -->
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-area="" asp-controller="Home" asp-action="Index">
|
||||
<i class="fas fa-home"></i> Home
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-area="" asp-controller="Home" asp-action="Budget">
|
||||
<i class="fas fa-wallet"></i> Budget
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" asp-area="" asp-controller="Home" asp-action="RealEstate">
|
||||
<i class="fas fa-building"></i> RealEstate
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
<main role="main" class="main-content"> <!-- Main content area -->
|
||||
@RenderBody() <!-- This is where the content of each page will be rendered -->
|
||||
</main>
|
||||
<aside class="right-sidebar"> <!-- Updated Right Sidebar -->
|
||||
@if (Model != null && Model.BudgetItems != null)
|
||||
{
|
||||
<h3 class="sidebar-budget-header">Budget this month: <span class="total-budget-amount">@Model.BudgetItems.Sum(item => item.Amount)</span></h3>
|
||||
<div class="sidebar-budget-container"> <!-- New Container -->
|
||||
@if (Model.BudgetItems.Any())
|
||||
{
|
||||
foreach (var categoryGroup in Model.BudgetItems.GroupBy(item => item.Category))
|
||||
{
|
||||
<div class="sidebar-budget-item">
|
||||
<span class="budget-item-name">@categoryGroup.Key</span>
|
||||
<span class="budget-item-amount">@categoryGroup.Sum(item => item.Amount)</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>No categories available.</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h3>No budget data available.</h3>
|
||||
}
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const budgetHeader = document.querySelector('.sidebar-budget-header');
|
||||
const budgetContainer = document.querySelector('.sidebar-budget-container');
|
||||
|
||||
budgetHeader.addEventListener('click', function () {
|
||||
budgetContainer.style.display = budgetContainer.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
|
||||
// Initially hide the budget items container
|
||||
budgetContainer.style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
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 */
|
||||
}
|
||||
Reference in New Issue
Block a user