diff --git a/frontend/src/Components/TimeRecordEditor.svelte b/frontend/src/Components/TimeRecordEditor.svelte
index 269091a..9b0f88b 100644
--- a/frontend/src/Components/TimeRecordEditor.svelte
+++ b/frontend/src/Components/TimeRecordEditor.svelte
@@ -4,7 +4,17 @@
import MarkdownEditor from './MarkdownEditor.svelte';
import TimeStampInput from './TimeStampInput.svelte';
- let { record = null, onSet = time => {} } = $props();
+ let { record = null, onAbort = () => {}, onDrop = time_id => {}, onSet = time => {} } = $props();
+
+ function cancel(e){
+ e.preventDefault();
+ onAbort();
+ }
+
+ function drop(e){
+ e.preventDefault();
+ if (confirm(t('confirm_delete',{element:record.subject}))) onDrop(record.id);
+ }
function onsubmit(e){
e.preventDefault();
@@ -14,7 +24,7 @@
{#if record}
{record.description.source}
diff --git a/frontend/src/routes/time/Index.svelte b/frontend/src/routes/time/Index.svelte
index d32bc1c..f7b69dd 100644
--- a/frontend/src/routes/time/Index.svelte
+++ b/frontend/src/routes/time/Index.svelte
@@ -64,6 +64,24 @@
}
}
+ function onAbort(){
+ detail = null;
+ }
+
+ async function onDrop(time_id){
+ const url = api(`time/${time_id}`);
+ const res = await fetch(url,{
+ credentials:'include',
+ method:'DELETE'
+ });
+ if (res.ok){
+ delete times[time_id];
+ error = false;
+ } else {
+ error = await res.text();
+ }
+ }
+
function openTask(tid){
router.navigate(`/task/${tid}/view`);
}
@@ -148,7 +166,7 @@
{/if}
{#if detail == time.id}
-
+
|
{:else}
toggleSelect(time.id)}>
diff --git a/time/src/main/java/de/srsoftware/umbrella/time/SqliteDb.java b/time/src/main/java/de/srsoftware/umbrella/time/SqliteDb.java
index e040063..c2bda8c 100644
--- a/time/src/main/java/de/srsoftware/umbrella/time/SqliteDb.java
+++ b/time/src/main/java/de/srsoftware/umbrella/time/SqliteDb.java
@@ -10,6 +10,7 @@ import static de.srsoftware.umbrella.time.Constants.*;
import static java.lang.System.Logger.Level.ERROR;
import static java.text.MessageFormat.format;
+import de.srsoftware.tools.jdbc.Query;
import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Time;
@@ -77,7 +78,20 @@ CREATE TABLE IF NOT EXISTS {0} (
}
}
- @Override
+ @Override
+ public Long delete(long timeId) {
+ try {
+ db.setAutoCommit(false);
+ Query.delete().from(TABLE_TASK_TIMES).where(TIME_ID,equal(timeId)).execute(db);
+ Query.delete().from(TABLE_TIMES).where(ID,equal(timeId)).execute(db);
+ db.setAutoCommit(false);
+ return timeId;
+ } catch (SQLException e) {
+ throw UmbrellaException.databaseException("Failed to delete time with id = {0}",timeId);
+ }
+ }
+
+ @Override
public HashMap listTimes(Collection taskIds, boolean showClosed) throws UmbrellaException {
try {
var rs = select(ALL).from(TABLE_TASK_TIMES).where(TASK_ID,in(taskIds.toArray())).exec(db);
diff --git a/time/src/main/java/de/srsoftware/umbrella/time/TimeDb.java b/time/src/main/java/de/srsoftware/umbrella/time/TimeDb.java
index f259b84..9207e1f 100644
--- a/time/src/main/java/de/srsoftware/umbrella/time/TimeDb.java
+++ b/time/src/main/java/de/srsoftware/umbrella/time/TimeDb.java
@@ -7,6 +7,8 @@ import java.util.Collection;
import java.util.HashMap;
public interface TimeDb {
+ Long delete(long timeId);
+
HashMap listTimes(Collection taskIds, boolean showClosed) throws UmbrellaException;
HashMap listUserTimes(long userId, boolean showClosed);
diff --git a/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java b/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java
index 97ff2b6..eed2351 100644
--- a/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java
+++ b/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java
@@ -34,6 +34,32 @@ public class TimeModule extends BaseHandler implements TimeService {
timeDb = new SqliteDb(connect(dbFile));
}
+
+ private boolean deleteTime(UmbrellaUser user, long timeId, HttpExchange ex) throws IOException {
+ var time = timeDb.load(timeId);
+ if (time.userId() != user.id()) throw forbidden("You are not allowed to delete this time!");
+ timeDb.delete(timeId);
+ return sendContent(ex,time);
+ }
+
+
+ @Override
+ public boolean doDelete(Path path, HttpExchange ex) throws IOException {
+ addCors(ex);
+ try {
+ Optional token = SessionToken.from(ex).map(Token::of);
+ var user = userService().loadUser(token);
+ if (user.isEmpty()) return unauthorized(ex);
+ var head = path.pop();
+ var timeId = Long.parseLong(head);
+ return deleteTime(user.get(),timeId,ex);
+ } catch (NumberFormatException e){
+ return send(ex,invalidFieldException(TIME_ID,"long value"));
+ } catch (UmbrellaException e){
+ return send(ex,e);
+ }
+ }
+
@Override
public boolean doGet(Path path, HttpExchange ex) throws IOException {
addCors(ex);
diff --git a/translations/src/main/resources/de.json b/translations/src/main/resources/de.json
index f7e73a8..d70498c 100644
--- a/translations/src/main/resources/de.json
+++ b/translations/src/main/resources/de.json
@@ -15,6 +15,7 @@
"bookmarks": "Lesezeichen",
"by": "von",
+ "cancel": "abbrechen",
"client_id": "Client-ID",
"client_secret": "Client-Geheimnis",
"close_settings": "Einstellungen schließen",
diff --git a/web/src/main/resources/web/css/default.css b/web/src/main/resources/web/css/default.css
index 644b4a1..e716d0f 100644
--- a/web/src/main/resources/web/css/default.css
+++ b/web/src/main/resources/web/css/default.css
@@ -320,4 +320,8 @@ li > a > p:nth-child(1){
.timetracks ul{
margin: 0
+}
+
+.time.record button.delete{
+ float: right;
}
\ No newline at end of file
|