feat: Enhance UI/UX across multiple views with consistent styling and improved layout
This commit is contained in:
parent
9931687419
commit
36d4ae00dd
16 changed files with 609 additions and 60 deletions
|
|
@ -14,19 +14,15 @@ namespace turf_tasker.Controllers
|
||||||
_context = context;
|
_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: LawnCareEvents
|
|
||||||
public async Task<IActionResult> Index(string sortOrder, string searchString)
|
public async Task<IActionResult> Index(string sortOrder, string searchString)
|
||||||
{
|
{
|
||||||
// Use ViewData to pass sort order info to the view
|
|
||||||
ViewData["DateSortParm"] = String.IsNullOrEmpty(sortOrder) ? "date_desc" : "";
|
ViewData["DateSortParm"] = String.IsNullOrEmpty(sortOrder) ? "date_desc" : "";
|
||||||
ViewData["TypeSortParm"] = sortOrder == "Type" ? "type_desc" : "Type";
|
ViewData["TypeSortParm"] = sortOrder == "Type" ? "type_desc" : "Type";
|
||||||
ViewData["CurrentFilter"] = searchString;
|
ViewData["CurrentFilter"] = searchString;
|
||||||
|
|
||||||
// Start with a base query that can be modified
|
|
||||||
var events = from e in _context.LawnCareEvents
|
var events = from e in _context.LawnCareEvents
|
||||||
select e;
|
select e;
|
||||||
|
|
||||||
// Apply the filter if a search string is provided
|
|
||||||
if (!String.IsNullOrEmpty(searchString))
|
if (!String.IsNullOrEmpty(searchString))
|
||||||
{
|
{
|
||||||
events = events.Where(e =>
|
events = events.Where(e =>
|
||||||
|
|
@ -35,7 +31,6 @@ namespace turf_tasker.Controllers
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the sorting based on the sortOrder parameter
|
|
||||||
switch (sortOrder)
|
switch (sortOrder)
|
||||||
{
|
{
|
||||||
case "date_desc":
|
case "date_desc":
|
||||||
|
|
@ -51,12 +46,9 @@ namespace turf_tasker.Controllers
|
||||||
events = events.OrderBy(e => e.EventDate);
|
events = events.OrderBy(e => e.EventDate);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the query and return the view with the filtered/sorted list
|
|
||||||
return View(await events.AsNoTracking().ToListAsync());
|
return View(await events.AsNoTracking().ToListAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: LawnCareEvents/Details/5
|
|
||||||
public async Task<IActionResult> Details(int? id)
|
public async Task<IActionResult> Details(int? id)
|
||||||
{
|
{
|
||||||
if (id == null)
|
if (id == null)
|
||||||
|
|
@ -74,15 +66,13 @@ namespace turf_tasker.Controllers
|
||||||
return View(lawnCareEvent);
|
return View(lawnCareEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: LawnCareEvents/Create
|
|
||||||
// MODIFIED: We now create a model with a default date.
|
|
||||||
public IActionResult Create()
|
public IActionResult Create()
|
||||||
{
|
{
|
||||||
var model = new LawnCareEvent { EventDate = DateTime.Now };
|
var model = new LawnCareEvent { EventDate = DateTime.Now };
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST: LawnCareEvents/Create
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Create([Bind("Id,EventType,EventDate,MowingPattern,Notes")] LawnCareEvent lawnCareEvent)
|
public async Task<IActionResult> Create([Bind("Id,EventType,EventDate,MowingPattern,Notes")] LawnCareEvent lawnCareEvent)
|
||||||
|
|
@ -96,7 +86,6 @@ namespace turf_tasker.Controllers
|
||||||
return View(lawnCareEvent);
|
return View(lawnCareEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: LawnCareEvents/Edit/5
|
|
||||||
public async Task<IActionResult> Edit(int? id)
|
public async Task<IActionResult> Edit(int? id)
|
||||||
{
|
{
|
||||||
if (id == null)
|
if (id == null)
|
||||||
|
|
@ -112,7 +101,6 @@ namespace turf_tasker.Controllers
|
||||||
return View(lawnCareEvent);
|
return View(lawnCareEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST: LawnCareEvents/Edit/5
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> Edit(int id, [Bind("Id,EventType,EventDate,MowingPattern,Notes")] LawnCareEvent lawnCareEvent)
|
public async Task<IActionResult> Edit(int id, [Bind("Id,EventType,EventDate,MowingPattern,Notes")] LawnCareEvent lawnCareEvent)
|
||||||
|
|
@ -131,7 +119,7 @@ namespace turf_tasker.Controllers
|
||||||
}
|
}
|
||||||
catch (DbUpdateConcurrencyException)
|
catch (DbUpdateConcurrencyException)
|
||||||
{
|
{
|
||||||
if (!LawnCareEventExists(lawnCareEvent.Id))
|
if (!LawnCareEventExists(lawnCareEvent.Id)) // This uses the private method below
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +133,6 @@ namespace turf_tasker.Controllers
|
||||||
return View(lawnCareEvent);
|
return View(lawnCareEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: LawnCareEvents/Delete/5
|
|
||||||
public async Task<IActionResult> Delete(int? id)
|
public async Task<IActionResult> Delete(int? id)
|
||||||
{
|
{
|
||||||
if (id == null)
|
if (id == null)
|
||||||
|
|
@ -163,7 +150,6 @@ namespace turf_tasker.Controllers
|
||||||
return View(lawnCareEvent);
|
return View(lawnCareEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST: LawnCareEvents/Delete/5
|
|
||||||
[HttpPost, ActionName("Delete")]
|
[HttpPost, ActionName("Delete")]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public async Task<IActionResult> DeleteConfirmed(int id)
|
public async Task<IActionResult> DeleteConfirmed(int id)
|
||||||
|
|
@ -182,5 +168,36 @@ namespace turf_tasker.Controllers
|
||||||
{
|
{
|
||||||
return _context.LawnCareEvents.Any(e => e.Id == id);
|
return _context.LawnCareEvents.Any(e => e.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IActionResult Calendar()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<JsonResult> GetEventsJson()
|
||||||
|
{
|
||||||
|
var events = await _context.LawnCareEvents.AsNoTracking().ToListAsync();
|
||||||
|
|
||||||
|
var calendarEvents = events.Select(e => new
|
||||||
|
{
|
||||||
|
id = e.Id,
|
||||||
|
title = $"{e.EventType} {((e.EventType == LawnCareEventType.Mowing && e.MowingPattern.HasValue) ? $"({e.MowingPattern})" : "")}",
|
||||||
|
start = e.EventDate.ToString("yyyy-MM-dd"),
|
||||||
|
allDay = true,
|
||||||
|
url = $"/LawnCareEvents/Details/{e.Id}",
|
||||||
|
color = e.EventType switch
|
||||||
|
{
|
||||||
|
LawnCareEventType.Mowing => "#28a745",
|
||||||
|
LawnCareEventType.Watering => "#007bff",
|
||||||
|
LawnCareEventType.Fertilizing => "#ffc107",
|
||||||
|
LawnCareEventType.Aeration => "#6f42c1",
|
||||||
|
LawnCareEventType.WeedControl => "#dc3545",
|
||||||
|
_ => "#6c757d"
|
||||||
|
}
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
return Json(calendarEvents);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
72
Migrations/20250619022142_AddNotApplicableMowingPattern.Designer.cs
generated
Normal file
72
Migrations/20250619022142_AddNotApplicableMowingPattern.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using turf_tasker.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace turf_tasker.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20250619022142_AddNotApplicableMowingPattern")]
|
||||||
|
partial class AddNotApplicableMowingPattern
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.6");
|
||||||
|
|
||||||
|
modelBuilder.Entity("turf_tasker.Models.LawnCareEvent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("EventDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("EventType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("MowingPattern")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LawnCareEvents");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("turf_tasker.Models.LawnCareTip", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Category")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Content")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LawnCareTips");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Migrations/20250619022142_AddNotApplicableMowingPattern.cs
Normal file
22
Migrations/20250619022142_AddNotApplicableMowingPattern.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace turf_tasker.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddNotApplicableMowingPattern : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
Migrations/20250621212709_AddEventDetails.Designer.cs
generated
Normal file
86
Migrations/20250621212709_AddEventDetails.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using turf_tasker.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace turf_tasker.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20250621212709_AddEventDetails")]
|
||||||
|
partial class AddEventDetails
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.6");
|
||||||
|
|
||||||
|
modelBuilder.Entity("turf_tasker.Models.LawnCareEvent", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal?>("AppliedAmount")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("EventDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("EventType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal?>("MowerHeightInches")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("MowingPattern")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasMaxLength(500)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProblemObserved")
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProductUsed")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LawnCareEvents");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("turf_tasker.Models.LawnCareTip", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Category")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Content")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("LawnCareTips");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
Migrations/20250621212709_AddEventDetails.cs
Normal file
60
Migrations/20250621212709_AddEventDetails.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace turf_tasker.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddEventDetails : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "AppliedAmount",
|
||||||
|
table: "LawnCareEvents",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "MowerHeightInches",
|
||||||
|
table: "LawnCareEvents",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ProblemObserved",
|
||||||
|
table: "LawnCareEvents",
|
||||||
|
type: "TEXT",
|
||||||
|
maxLength: 250,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ProductUsed",
|
||||||
|
table: "LawnCareEvents",
|
||||||
|
type: "TEXT",
|
||||||
|
maxLength: 200,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AppliedAmount",
|
||||||
|
table: "LawnCareEvents");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "MowerHeightInches",
|
||||||
|
table: "LawnCareEvents");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ProblemObserved",
|
||||||
|
table: "LawnCareEvents");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ProductUsed",
|
||||||
|
table: "LawnCareEvents");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,12 +23,18 @@ namespace turf_tasker.Migrations
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal?>("AppliedAmount")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<DateTime>("EventDate")
|
b.Property<DateTime>("EventDate")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("EventType")
|
b.Property<int>("EventType")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal?>("MowerHeightInches")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("MowingPattern")
|
b.Property<int?>("MowingPattern")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
|
@ -36,6 +42,14 @@ namespace turf_tasker.Migrations
|
||||||
.HasMaxLength(500)
|
.HasMaxLength(500)
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProblemObserved")
|
||||||
|
.HasMaxLength(250)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ProductUsed")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("LawnCareEvents");
|
b.ToTable("LawnCareEvents");
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,17 @@ public enum LawnCareEventType
|
||||||
Fertilizing,
|
Fertilizing,
|
||||||
Aeration,
|
Aeration,
|
||||||
WeedControl,
|
WeedControl,
|
||||||
|
PestControl,
|
||||||
|
DiseaseControl,
|
||||||
|
Topdressing,
|
||||||
|
Overseeding,
|
||||||
Other
|
Other
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MowingPattern
|
public enum MowingPattern
|
||||||
{
|
{
|
||||||
|
[Display(Name = "N/A")]
|
||||||
|
NotApplicable,
|
||||||
Vertical,
|
Vertical,
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Diagonal,
|
Diagonal,
|
||||||
|
|
@ -30,17 +36,30 @@ public class LawnCareEvent
|
||||||
[Display(Name = "Activity Type")]
|
[Display(Name = "Activity Type")]
|
||||||
public LawnCareEventType EventType { get; set; }
|
public LawnCareEventType EventType { get; set; }
|
||||||
|
|
||||||
// --- THIS IS THE LINE TO CHECK ---
|
|
||||||
// Make sure this property exists and is spelled correctly.
|
|
||||||
[Required]
|
[Required]
|
||||||
[DataType(DataType.Date)]
|
[DataType(DataType.Date)]
|
||||||
[Display(Name = "Date")]
|
[Display(Name = "Date")]
|
||||||
public DateTime EventDate { get; set; }
|
public DateTime EventDate { get; set; }
|
||||||
// ---------------------------------
|
|
||||||
|
|
||||||
[Display(Name = "Mowing Pattern")]
|
[Display(Name = "Mowing Pattern")]
|
||||||
public MowingPattern? MowingPattern { get; set; }
|
public MowingPattern? MowingPattern { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Product Used")]
|
||||||
|
[StringLength(200)]
|
||||||
|
public string? ProductUsed { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Applied Amount")]
|
||||||
|
[Range(0.01, 1000.0, ErrorMessage = "Amount must be positive.")] // Example range
|
||||||
|
public decimal? AppliedAmount { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Mower Height (inches)")]
|
||||||
|
[Range(0.5, 6.0, ErrorMessage = "Height must be between 0.5 and 6 inches.")]
|
||||||
|
public decimal? MowerHeightInches { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Problem Observed")]
|
||||||
|
[StringLength(250)]
|
||||||
|
public string? ProblemObserved { get; set; }
|
||||||
|
|
||||||
[StringLength(500)]
|
[StringLength(500)]
|
||||||
public string? Notes { get; set; }
|
public string? Notes { get; set; }
|
||||||
}
|
}
|
||||||
137
Views/LawnCareEvents/Calendar.cshtml
Normal file
137
Views/LawnCareEvents/Calendar.cshtml
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Lawn Care Calendar";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1>@ViewData["Title"]</h1>
|
||||||
|
<a asp-action="Create" asp-controller="LawnCareEvents" class="btn btn-primary">
|
||||||
|
<i class="bi bi-plus-circle-fill"></i> Create New Event
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="calendar-container">
|
||||||
|
<div class="calendar-header">
|
||||||
|
<button id="prevMonth" class="btn btn-secondary">< Prev</button>
|
||||||
|
<h2 id="currentMonthYear"></h2>
|
||||||
|
<button id="nextMonth" class="btn btn-secondary">Next ></button>
|
||||||
|
</div>
|
||||||
|
<div class="calendar-grid">
|
||||||
|
<div class="day-name">Sun</div>
|
||||||
|
<div class="day-name">Mon</div>
|
||||||
|
<div class="day-name">Tue</div>
|
||||||
|
<div class="day-name">Wed</div>
|
||||||
|
<div class="day-name">Thu</div>
|
||||||
|
<div class="day-name">Fri</div>
|
||||||
|
<div class="day-name">Sat</div>
|
||||||
|
</div>
|
||||||
|
<div id="calendarDays" class="calendar-grid">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@section Scripts {
|
||||||
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function () {
|
||||||
|
const calendarDaysEl = $('#calendarDays');
|
||||||
|
const currentMonthYearEl = $('#currentMonthYear');
|
||||||
|
const prevMonthBtn = $('#prevMonth');
|
||||||
|
const nextMonthBtn = $('#nextMonth');
|
||||||
|
|
||||||
|
let currentMonth = new Date().getMonth();
|
||||||
|
let currentYear = new Date().getFullYear();
|
||||||
|
let allEvents = [];
|
||||||
|
|
||||||
|
function renderCalendar(month, year) {
|
||||||
|
calendarDaysEl.empty();
|
||||||
|
currentMonthYearEl.text(new Date(year, month).toLocaleString('default', { month: 'long', year: 'numeric' }));
|
||||||
|
|
||||||
|
const firstDayOfMonth = new Date(year, month, 1);
|
||||||
|
const lastDayOfMonth = new Date(year, month + 1, 0);
|
||||||
|
const daysInMonth = lastDayOfMonth.getDate();
|
||||||
|
|
||||||
|
const startDayOfWeek = firstDayOfMonth.getDay();
|
||||||
|
|
||||||
|
const prevMonthLastDay = new Date(year, month, 0).getDate();
|
||||||
|
for (let i = startDayOfWeek; i > 0; i--) {
|
||||||
|
const dayNum = prevMonthLastDay - i + 1;
|
||||||
|
calendarDaysEl.append(`<div class="calendar-day other-month"><span class="day-number">${dayNum}</span></div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let day = 1; day <= daysInMonth; day++) {
|
||||||
|
const date = new Date(year, month, day);
|
||||||
|
const dateString = date.toISOString().split('T')[0];
|
||||||
|
|
||||||
|
let dayHtml = `<div class="calendar-day" data-date="${dateString}">`;
|
||||||
|
dayHtml += `<span class="day-number">${day}</span>`;
|
||||||
|
|
||||||
|
const eventsOnThisDay = allEvents.filter(event => event.start === dateString);
|
||||||
|
|
||||||
|
eventsOnThisDay.forEach(event => {
|
||||||
|
const typeClass = event.title.split(' ')[0].toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
|
dayHtml += `<div class="event-indicator ${typeClass}" data-event-id="${event.id}" data-event-url="${event.url}">${event.title}</div>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
dayHtml += `</div>`;
|
||||||
|
calendarDaysEl.append(dayHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalDaysDisplayed = startDayOfWeek + daysInMonth;
|
||||||
|
const remainingCells = 42 - totalDaysDisplayed;
|
||||||
|
for (let i = 1; i <= remainingCells && totalDaysDisplayed + i <= 42; i++) {
|
||||||
|
calendarDaysEl.append(`<div class="calendar-day other-month"><span class="day-number">${i}</span></div>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.calendar-day .event-indicator').on('click', function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const url = $(this).data('event-url');
|
||||||
|
if (url) {
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.calendar-day:not(.other-month)').on('click', function () {
|
||||||
|
const date = $(this).data('date');
|
||||||
|
window.location.href = `/LawnCareEvents/Create?date=${date}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchEvents() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/LawnCareEvents/GetEventsJson');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
allEvents = await response.json();
|
||||||
|
renderCalendar(currentMonth, currentYear);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching events:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevMonthBtn.on('click', function () {
|
||||||
|
currentMonth--;
|
||||||
|
if (currentMonth < 0) {
|
||||||
|
currentMonth = 11;
|
||||||
|
currentYear--;
|
||||||
|
}
|
||||||
|
renderCalendar(currentMonth, currentYear);
|
||||||
|
});
|
||||||
|
|
||||||
|
nextMonthBtn.on('click', function () {
|
||||||
|
currentMonth++;
|
||||||
|
if (currentMonth > 11) {
|
||||||
|
currentMonth = 0;
|
||||||
|
currentYear++;
|
||||||
|
}
|
||||||
|
renderCalendar(currentMonth, currentYear);
|
||||||
|
});
|
||||||
|
|
||||||
|
fetchEvents();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
}
|
||||||
|
|
||||||
|
@section Styles {
|
||||||
|
<link href="~/css/calendar.css" rel="stylesheet" asp-append-version="true" />
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,11 @@
|
||||||
|
|
||||||
<form asp-action="Delete">
|
<form asp-action="Delete">
|
||||||
<input type="hidden" asp-for="Id" />
|
<input type="hidden" asp-for="Id" />
|
||||||
<input type="submit" value="Delete" class="btn btn-danger" /> |
|
<input type="submit" value="Delete" class="btn btn-danger" />
|
||||||
<a asp-action="Index">Back to List</a>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<a asp-action="Index">Back to List</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="submit" value="Save" class="btn btn-primary" />
|
<input type="submit" value="Save" class="btn btn-primary" />
|
||||||
|
<a asp-action="Delete" asp-route-id="@Model.Id" class="btn btn-danger ms-2">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,74 @@
|
||||||
@model turf_tasker.Models.LawnCareEvent
|
@model turf_tasker.Models.LawnCareEvent
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Edit";
|
ViewData["Title"] = "Edit";
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>Edit</h1>
|
<h1>Edit</h1>
|
||||||
|
|
||||||
<h4>LawnCareEvent</h4>
|
<h4>Lawn Care Event</h4>
|
||||||
<hr />
|
<hr />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<form asp-action="Edit">
|
<form asp-action="Edit">
|
||||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||||
<input type="hidden" asp-for="Id" />
|
<input type="hidden" asp-for="Id" />
|
||||||
<div class="form-group">
|
<div class="form-group mb-3">
|
||||||
<label asp-for="EventType" class="control-label"></label>
|
<label asp-for="EventType" class="control-label"></label>
|
||||||
<select asp-for="EventType" class="form-control" asp-items="Html.GetEnumSelectList<LawnCareEventType>()"></select>
|
<select asp-for="EventType" class="form-control" asp-items="Html.GetEnumSelectList<LawnCareEventType>()"></select>
|
||||||
<span asp-validation-for="EventType" class="text-danger"></span>
|
<span asp-validation-for="EventType" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group mb-3">
|
||||||
<label asp-for="EventDate" class="control-label"></label>
|
<label asp-for="EventDate" class="control-label"></label>
|
||||||
<input asp-for="EventDate" class="form-control" />
|
<input asp-for="EventDate" class="form-control" />
|
||||||
<span asp-validation-for="EventDate" class="text-danger"></span>
|
<span asp-validation-for="EventDate" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div id="mowingPatternGroup">
|
<div id="mowingPatternGroup">
|
||||||
<div class="form-group">
|
<div class="form-group mb-3">
|
||||||
<label asp-for="MowingPattern" class="control-label"></label>
|
<label asp-for="MowingPattern" class="control-label"></label>
|
||||||
<select asp-for="MowingPattern" class="form-control" asp-items="Html.GetEnumSelectList<MowingPattern>()"></select>
|
<select asp-for="MowingPattern" class="form-control" asp-items="Html.GetEnumSelectList<MowingPattern>()"></select>
|
||||||
<span asp-validation-for="MowingPattern" class="text-danger"></span>
|
<span asp-validation-for="MowingPattern" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group mb-3">
|
||||||
<label asp-for="Notes" class="control-label"></label>
|
<label asp-for="Notes" class="control-label"></label>
|
||||||
<textarea asp-for="Notes" class="form-control"></textarea>
|
<textarea asp-for="Notes" class="form-control"></textarea>
|
||||||
<span asp-validation-for="Notes" class="text-danger"></span>
|
<span asp-validation-for="Notes" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mt-3">
|
<div class="form-group mt-3">
|
||||||
<input type="submit" value="Save" class="btn btn-primary" />
|
<input type="submit" value="Save" class="btn btn-primary" />
|
||||||
|
<!-- NEW: Add a space/margin and use Razor syntax to pass the Id -->
|
||||||
|
<a asp-action="Delete" asp-route-id="@Model.Id" class="btn btn-danger">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="mt-3">
|
||||||
<a asp-action="Index">Back to List</a>
|
<a asp-action="Index">Back to List</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@section Scripts {
|
@section Scripts {
|
||||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
|
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
function toggleMowingPattern() {
|
function toggleMowingPattern() {
|
||||||
var selectedEventType = $("#EventType").val();
|
var selectedEventType = $("#EventType").val();
|
||||||
|
|
||||||
if (selectedEventType == '0') { // '0' corresponds to Mowing
|
if (selectedEventType == '0') { // '0' corresponds to Mowing
|
||||||
$("#mowingPatternGroup").show();
|
$("#mowingPatternGroup").show();
|
||||||
} else {
|
} else {
|
||||||
$("#mowingPatternGroup").hide();
|
$("#mowingPatternGroup").hide();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMowingPattern();
|
||||||
|
|
||||||
|
$("#EventType").on("change", function () {
|
||||||
toggleMowingPattern();
|
toggleMowingPattern();
|
||||||
|
|
||||||
$("#EventType").on("change", function () {
|
|
||||||
toggleMowingPattern();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
});
|
||||||
|
</script>
|
||||||
}
|
}
|
||||||
|
|
@ -4,11 +4,14 @@
|
||||||
ViewData["Title"] = "Lawn Care Log";
|
ViewData["Title"] = "Lawn Care Log";
|
||||||
}
|
}
|
||||||
|
|
||||||
<h1>@ViewData["Title"]</h1>
|
<div class="text-center">
|
||||||
|
<h1>@ViewData["Title"]</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a asp-action="Create" class="btn btn-primary">Create New Event</a>
|
<a asp-action="Create" class="btn btn-primary">Create New Event</a>
|
||||||
</p>
|
<a asp-action="Calendar" class="btn btn-info ms-2">View Calendar</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Add the search form -->
|
<!-- Add the search form -->
|
||||||
<form asp-action="Index" method="get">
|
<form asp-action="Index" method="get">
|
||||||
|
|
@ -16,8 +19,8 @@
|
||||||
<p>
|
<p>
|
||||||
Find by type or note:
|
Find by type or note:
|
||||||
<input type="text" name="searchString" value="@ViewData["CurrentFilter"]" />
|
<input type="text" name="searchString" value="@ViewData["CurrentFilter"]" />
|
||||||
<input type="submit" value="Search" class="btn btn-secondary" /> |
|
<input type="submit" value="Search" class="btn btn-secondary" />
|
||||||
<a asp-action="Index">Back to Full List</a>
|
<a asp-action="Index" class="btn btn-primary">Reset Filter</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -63,9 +66,11 @@
|
||||||
@Html.DisplayFor(modelItem => item.Notes)
|
@Html.DisplayFor(modelItem => item.Notes)
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
|
<div class="d-flex flex-wrap gap-2">
|
||||||
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
|
<a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||||
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
|
<a asp-action="Details" asp-route-id="@item.Id" class="btn btn-sm btn-outline-info">Details</a>
|
||||||
|
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-sm btn-outline-danger">Delete</a>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<h1>Index</h1>
|
<h1>Index</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a asp-action="Create">Create New</a>
|
<a asp-action="Create" class="btn btn-primary">Create New Tip</a>
|
||||||
</p>
|
</p>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
@ -36,10 +36,11 @@
|
||||||
<td>
|
<td>
|
||||||
@Html.DisplayFor(modelItem => item.Content)
|
@Html.DisplayFor(modelItem => item.Content)
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="col-actions">
|
||||||
<a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
|
<div class="d-flex flex-wrap gap-2">
|
||||||
<a asp-action="Details" asp-route-id="@item.Id">Details</a> |
|
<a asp-action="Edit" asp-route-id="@item.Id" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||||
<a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
|
<a asp-action="Delete" asp-route-id="@item.Id" class="btn btn-sm btn-outline-danger">Delete</a>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - MowLog</title>
|
<title>@ViewData["Title"] - MowLog</title>
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||||
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
|
@await RenderSectionAsync("Styles", required: false)
|
||||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||||
<link rel="stylesheet" href="~/MowLog.styles.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/MowLog.styles.css" asp-append-version="true" />
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -46,6 +48,5 @@
|
||||||
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
||||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="~/js/site.js" asp-append-version="true"></script>
|
<script src="~/js/site.js" asp-append-version="true"></script>
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
95
wwwroot/css/calendar.css
Normal file
95
wwwroot/css/calendar.css
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
.calendar-container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 20px auto;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(7, 1fr);
|
||||||
|
gap: 1px;
|
||||||
|
background-color: #ddd;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-name {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
padding: 10px 5px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 10px 5px;
|
||||||
|
min-height: 100px;
|
||||||
|
text-align: right;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day:hover {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day.other-month {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day .day-number {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #333;
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-day .event-indicator {
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.75em;
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-top: 25px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.event-indicator.mowing { background-color: #28a745; }
|
||||||
|
.event-indicator.watering { background-color: #007bff; }
|
||||||
|
.event-indicator.fertilizing { background-color: #ffc107; }
|
||||||
|
.event-indicator.aeration { background-color: #6f42c1; }
|
||||||
|
.event-indicator.weedcontrol { background-color: #dc3545; }
|
||||||
|
.event-indicator.other { background-color: #6c757d; }
|
||||||
|
|
||||||
|
.card.h-100 {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
@ -34,3 +34,16 @@ body {
|
||||||
.navbar-custom .nav-link:hover {
|
.navbar-custom .nav-link:hover {
|
||||||
color: #d4d4d4; /* A light gray for hover effect */
|
color: #d4d4d4; /* A light gray for hover effect */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For tables, to control column widths */
|
||||||
|
.table .col-content {
|
||||||
|
max-width: 400px; /* Adjust as needed */
|
||||||
|
word-wrap: break-word; /* Break long words */
|
||||||
|
white-space: normal; /* Allow text to wrap normally */
|
||||||
|
}
|
||||||
|
|
||||||
|
.table .col-actions {
|
||||||
|
width: 150px; /* Fixed width for action buttons, adjust as needed */
|
||||||
|
min-width: 120px; /* Ensure minimum space */
|
||||||
|
white-space: nowrap; /* Keep buttons on one line, prevent wrapping */
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue