diff --git a/ClockingApp/Controllers/ClockingController.cs b/ClockingApp/Controllers/ClockingController.cs index 2840931..76b097c 100644 --- a/ClockingApp/Controllers/ClockingController.cs +++ b/ClockingApp/Controllers/ClockingController.cs @@ -114,6 +114,14 @@ public async Task FinishBreak([FromBody] string clockingId) } } + [HttpDelete] + public async Task Clocking([FromBody] string clockingId) + { + bool isDeleted = await _clockingService._clockingRepo.DeleteByIdAsync(clockingId); + return Json(isDeleted); + } + + public async Task GetAllClockingsForUserAndWeek(DateTime weekDate) { int weekNumber = ISOWeek.GetWeekOfYear(weekDate); @@ -145,9 +153,15 @@ public async Task GetClockingInvoiceForWeek(int weekNumber, int we clocking.SetTimeZoneForClockingWorkAndBreaks(specifiedTimeZone); } WeeklyClockingInfo weeklyClockingInfo = new WeeklyClockingInfo(weekClockings); + weeklyClockingInfo.IsInvoiceView = true; return View("ClockingsInvoicePDF", weeklyClockingInfo); } + public async Task EditClocking(string clockingId) + { + return View("EditClocking", model:clockingId); + } + private async Task RetrieveClockingById(string clockingId) { return await _clockingService._clockingRepo.FindByIdAsync(clockingId); diff --git a/ClockingApp/Models/ClockingData/WeeklyClockingInfo.cs b/ClockingApp/Models/ClockingData/WeeklyClockingInfo.cs index e62a89d..87a3787 100644 --- a/ClockingApp/Models/ClockingData/WeeklyClockingInfo.cs +++ b/ClockingApp/Models/ClockingData/WeeklyClockingInfo.cs @@ -7,7 +7,8 @@ public class WeeklyClockingInfo { public IList? WeeklyClockings { get; set; } public double OvertimeHours { get; set; } - public bool HasOvertime => (OvertimeHours > 0); + public bool IsInvoiceView { get; set; } = false; + public bool HasOvertime => (OvertimeHours > 0); public string OvertimeHours_formatted => String.Format("{0}{1}", this.OvertimeHours.ToString("##.##"), "h"); public bool HasClockings => (WeeklyClockings != null && WeeklyClockings.Any()); public int ClockingWeek => HasClockings diff --git a/ClockingApp/Repository/IMongoRepositoryBase.cs b/ClockingApp/Repository/IMongoRepositoryBase.cs index 36773fa..03198a1 100644 --- a/ClockingApp/Repository/IMongoRepositoryBase.cs +++ b/ClockingApp/Repository/IMongoRepositoryBase.cs @@ -11,7 +11,7 @@ public interface IMongoRepositoryBase where TDocument : IDocument Task> FindAllAsync(Expression> filter); Task FindOneAndReplaceAsync(Expression> filter, TDocument document); Task InsertOneAsync(TDocument document); - Task DeleteByIdAsync(string id); + Task DeleteByIdAsync(string id); } } diff --git a/ClockingApp/Repository/MongoRepositoryBase.cs b/ClockingApp/Repository/MongoRepositoryBase.cs index 532cc9f..0e662eb 100644 --- a/ClockingApp/Repository/MongoRepositoryBase.cs +++ b/ClockingApp/Repository/MongoRepositoryBase.cs @@ -7,52 +7,53 @@ namespace ClockingApp.Repository { - public class MongoRepositoryBase : IMongoRepositoryBase where TDocument : IDocument - { - private readonly IMongoCollection _collection; - - public MongoRepositoryBase(IMongoClient mongoClient, IMongoDBSettings mongoSettings) - { - _collection = mongoClient.GetDatabase(mongoSettings.DatabaseName).GetCollection(GetCollectionName(typeof(TDocument))); - } - - private protected string GetCollectionName (Type documentType) - { - var attribute = documentType. - GetCustomAttributes(typeof(BsonCollectionAttribute), true).FirstOrDefault(); - return attribute != null ? ((BsonCollectionAttribute)attribute).CollectionName : ""; - } - - public virtual async Task FindOneAsync (Expression> filter) - { - return (await _collection.FindAsync(filter)).FirstOrDefault(); - } - - public virtual async Task InsertOneAsync (TDocument document) - { - await _collection.InsertOneAsync(document); - } - - public virtual async Task FindOneAndReplaceAsync (Expression> filter, TDocument document) - { - await _collection.FindOneAndReplaceAsync(filter, document); - } + public class MongoRepositoryBase : IMongoRepositoryBase where TDocument : IDocument + { + private readonly IMongoCollection _collection; + + public MongoRepositoryBase(IMongoClient mongoClient, IMongoDBSettings mongoSettings) + { + _collection = mongoClient.GetDatabase(mongoSettings.DatabaseName).GetCollection(GetCollectionName(typeof(TDocument))); + } + + private protected string GetCollectionName(Type documentType) + { + var attribute = documentType. + GetCustomAttributes(typeof(BsonCollectionAttribute), true).FirstOrDefault(); + return attribute != null ? ((BsonCollectionAttribute)attribute).CollectionName : ""; + } + + public virtual async Task FindOneAsync(Expression> filter) + { + return (await _collection.FindAsync(filter)).FirstOrDefault(); + } + + public virtual async Task InsertOneAsync(TDocument document) + { + await _collection.InsertOneAsync(document); + } + + public virtual async Task FindOneAndReplaceAsync(Expression> filter, TDocument document) + { + await _collection.FindOneAndReplaceAsync(filter, document); + } public virtual async Task> FindAllAsync(Expression> filter) - { - return (await _collection.FindAsync(filter)).ToEnumerable(); - } - public virtual async Task FindByIdAsync(string id) - { - ObjectId objectId = new (id); - FilterDefinition filter = Builders.Filter.Eq(doc => doc._id, objectId); - return (await _collection.FindAsync(filter)).SingleOrDefault(); - } - public virtual async Task DeleteByIdAsync(string id) - { + { + return (await _collection.FindAsync(filter)).ToEnumerable(); + } + public virtual async Task FindByIdAsync(string id) + { + ObjectId objectId = new(id); + FilterDefinition filter = Builders.Filter.Eq(doc => doc._id, objectId); + return (await _collection.FindAsync(filter)).SingleOrDefault(); + } + public virtual async Task DeleteByIdAsync(string id) + { ObjectId objectId = new(id); FilterDefinition filter = Builders.Filter.Eq(doc => doc._id, objectId); - await _collection.FindOneAndDeleteAsync(filter); + TDocument deletedDoc = await _collection.FindOneAndDeleteAsync(filter); + return deletedDoc != null; } } diff --git a/ClockingApp/Views/Clocking/EditClocking.cshtml b/ClockingApp/Views/Clocking/EditClocking.cshtml new file mode 100644 index 0000000..11f0113 --- /dev/null +++ b/ClockingApp/Views/Clocking/EditClocking.cshtml @@ -0,0 +1,2 @@ +@model string +

@Model

\ No newline at end of file diff --git a/ClockingApp/Views/Shared/Components/WeeklyClocking/Default.cshtml b/ClockingApp/Views/Shared/Components/WeeklyClocking/Default.cshtml index bdcfeee..7acb786 100644 --- a/ClockingApp/Views/Shared/Components/WeeklyClocking/Default.cshtml +++ b/ClockingApp/Views/Shared/Components/WeeklyClocking/Default.cshtml @@ -32,7 +32,7 @@

Breakdown

- +
@@ -41,28 +41,47 @@ + @if (!Model.IsInvoiceView) + { + + } - @foreach (ClockingApp.Models.ClockingData.Clocking clocking in Model.WeeklyClockings) + @foreach (var item in Model.WeeklyClockings.Select((clocking, index) => new { clocking, index })) { - - - - - + + + + + + @if (!Model.IsInvoiceView) + { + + } } diff --git a/ClockingApp/Views/Shared/_Layout.cshtml b/ClockingApp/Views/Shared/_Layout.cshtml index d6cf1fd..113d56b 100644 --- a/ClockingApp/Views/Shared/_Layout.cshtml +++ b/ClockingApp/Views/Shared/_Layout.cshtml @@ -50,6 +50,7 @@ + @await RenderSectionAsync("Scripts", required: false) diff --git a/ClockingApp/appsettings.json b/ClockingApp/appsettings.json index da0cb9d..fcac5b0 100644 --- a/ClockingApp/appsettings.json +++ b/ClockingApp/appsettings.json @@ -14,7 +14,7 @@ }, "ClockingSettings": { "PaidBreakTime": "15", - "OvertimeThresholdHours": "0,25", + "OvertimeThresholdHours": "0.25", "WeeklyDefaultHours": { "Monday": { "WeekDay": "Monday", diff --git a/ClockingApp/wwwroot/css/site.css b/ClockingApp/wwwroot/css/site.css index ce4da82..8902c73 100644 --- a/ClockingApp/wwwroot/css/site.css +++ b/ClockingApp/wwwroot/css/site.css @@ -106,12 +106,6 @@ body { table th { text-align: left; } -/* table th:hover { - border: 0.5px solid; - border-radius: 15px; - border-color: rgba(0,0,0,0.04); - background-color: rgba(0,0,0,0.04); - }*/ .hasSmallPrint { } @@ -153,3 +147,20 @@ table th { font-size: medium; color: rgba(70,130,180, 30); } + +.inlineEditButton { + font-size: large; + padding: 2px 5px; + +} + +.optionsMenu { + visibility: hidden; + opacity: 0; + transition: visibility 0.2s linear, opacity 0.2s linear; +} + +.clockingTable tbody tr:hover .optionsMenu { + visibility: visible; + opacity: 1; +} \ No newline at end of file diff --git a/ClockingApp/wwwroot/js/Clocking.js b/ClockingApp/wwwroot/js/Clocking.js index db5e11c..55a623d 100644 --- a/ClockingApp/wwwroot/js/Clocking.js +++ b/ClockingApp/wwwroot/js/Clocking.js @@ -46,4 +46,64 @@ function FinishBreak(clockingId) { window.location.href = _dataBack; } }); +} + +function DeleteTableRowByTableIndex(tableId, tableRowIndex) { + /* tableRowIndex starts in zero within tbody and table.deleteRow() + * considers 0 as the table head - Therefore, we increment index always in 1. + * parseInt is used to make sure that it does not join two strings with the sum + * operation. + */ + var dataTableRowIndex = parseInt(tableRowIndex) + 1; + var table = document.getElementById(tableId); + if (table) { + table.deleteRow(dataTableRowIndex); + } +} + +function DeleteClockingRequest(clockingId) { + return new Promise(resolve => { + $.ajax({ + type: "DELETE", + url: "Clocking/Clocking", + dataType: "json", + contentType: "application/json; charset=utf-8", + data: JSON.stringify(clockingId), + success: function (response) { + if (response === true) { + resolve(true); + } else { + resolve(false); + } + } + }); + }); + +} + +async function DeleteClocking(clockingId, clockingDate, tableId, tableRowIndexToDelete) { + let userRequestResponse = await Swal.fire({ + icon: 'question', + title: 'Removing Clocking', + text: `This will remove Clocking for ${clockingDate}`, + showConfirmButton: true, + showCancelButton: true, + confirmButtonText: 'Delete it!' + }).then((result) => { + return result.isConfirmed; + }); + if (userRequestResponse === true) { + const deleteResponse = await DeleteClockingRequest(clockingId); + if (deleteResponse === true) { + DeleteTableRowByTableIndex(tableId, tableRowIndexToDelete); + Swal.fire('Deleted!' + , 'Clocking was deleted' + , 'success'); + } else { + Swal.fire('Error!' + , 'Something went wrong' + , 'error'); + } + } + } \ No newline at end of file
DateWork Duration Breaks Breaks Breakdown
@clocking.ClockingDate.ToString("D")@clocking.WorkDay.StartDate_formatted@clocking.WorkDay.EndDate_formatted@clocking.WorkingHoursPaid_formatted@String.Format("{0} Breaks ({1})", clocking.NumberOfBreaks, clocking.BreakDuration_formatted) - @if (clocking.Breaks != null) + @item.clocking.ClockingDate.ToString("D") + @item.clocking.WorkDay.StartDate_formatted@item.clocking.WorkDay.EndDate_formatted@item.clocking.WorkingHoursPaid_formatted@String.Format("{0} Breaks ({1})", item.clocking.NumberOfBreaks, item.clocking.BreakDuration_formatted) + @if (item.clocking.Breaks != null) {
    - @foreach (var _break in clocking.Breaks) + @foreach (var _break in item.clocking.Breaks) { -
  • @String.Format("{0} → {1} ({2})", _break.StartDate_formatted, _break.EndDate_formatted, _break.Duration_formatted)
  • +
  • + @String.Format("{0} → {1} ({2})", _break.StartDate_formatted + , _break.EndDate_formatted, _break.Duration_formatted) +
  • }
}
+
+
+ +
+
+