diff --git a/frontend/src/routes/search/Search.svelte b/frontend/src/routes/search/Search.svelte
index 83731ab..d51d4c5 100644
--- a/frontend/src/routes/search/Search.svelte
+++ b/frontend/src/routes/search/Search.svelte
@@ -3,6 +3,7 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
+ import { display } from '../../time.svelte';
import Bookmark from '../bookmark/Template.svelte';
@@ -16,6 +17,7 @@
let notes = $state(null);
let projects = $state(null);
let tasks = $state(null);
+ let times = $state(null);
async function setKey(ev){
if (ev) ev.preventDefault();
@@ -44,6 +46,7 @@
fetch(api('notes/search'),options).then(handleNotes);
fetch(api('project/search'),options).then(handleProjects);
fetch(api('task/search'),options).then(handleTasks);
+ fetch(api('time/search'),options).then(handleTimes);
}
function onclick(e){
@@ -100,6 +103,15 @@
}
}
+ async function handleTimes(resp){
+ if (resp.ok){
+ const res = await resp.json();
+ times = Object.keys(res).length ? res : null;
+ } else {
+ error = await resp.text();
+ }
+ }
+
$effect(() => doSearch(key))
@@ -197,3 +209,21 @@
{/if}
+{#if times}
+
+{/if}
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 c2bda8c..2ddcee2 100644
--- a/time/src/main/java/de/srsoftware/umbrella/time/SqliteDb.java
+++ b/time/src/main/java/de/srsoftware/umbrella/time/SqliteDb.java
@@ -16,9 +16,7 @@ import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Time;
import java.sql.Connection;
import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.*;
public class SqliteDb extends BaseDb implements TimeDb {
@@ -91,6 +89,25 @@ CREATE TABLE IF NOT EXISTS {0} (
}
}
+ @Override
+ public Map find(long userId, List keys, boolean fulltext) {
+ try {
+ var query = select(ALL).from(TABLE_TIMES).where(USER_ID,equal(userId));
+ for (var key : keys) query.where(format("CONCAT({0},\" \",{1})",SUBJECT,DESCRIPTION),like("%"+key+"%"));
+ var rs = query.exec(db);
+ var times = new HashMap();
+ while (rs.next()) {
+ var time = Time.of(rs);
+ times.put(time.id(),time);
+ }
+ rs.close();
+ return times;
+ } catch (Exception e) {
+
+ throw new UmbrellaException("Failed to search for times");
+ }
+ }
+
@Override
public HashMap listTimes(Collection taskIds, boolean showClosed) throws UmbrellaException {
try {
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 9207e1f..4d3f64d 100644
--- a/time/src/main/java/de/srsoftware/umbrella/time/TimeDb.java
+++ b/time/src/main/java/de/srsoftware/umbrella/time/TimeDb.java
@@ -5,13 +5,17 @@ import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.Time;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public interface TimeDb {
Long delete(long timeId);
- HashMap listTimes(Collection taskIds, boolean showClosed) throws UmbrellaException;
+ Map find(long userId, List keys, boolean fulltext);
- HashMap listUserTimes(long userId, boolean showClosed);
+ Map listTimes(Collection taskIds, boolean showClosed) throws UmbrellaException;
+
+ Map listUserTimes(long userId, boolean showClosed);
Time load(long timeId);
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 6dd77d6..e852fc3 100644
--- a/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java
+++ b/time/src/main/java/de/srsoftware/umbrella/time/TimeModule.java
@@ -105,6 +105,7 @@ public class TimeModule extends BaseHandler implements TimeService {
return switch (head) {
case JOIN -> joinTimes(ex,user.get());
case LIST -> listTimes(ex,user.get());
+ case SEARCH -> postSearch(ex, user.get());
case TRACK_TASK -> trackTask(user.get(),path,ex);
default -> {
try {
@@ -180,48 +181,6 @@ public class TimeModule extends BaseHandler implements TimeService {
return sendContent(ex,time);
}
- private boolean patchTime(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 alter this time!");
- var json = json(ex);
- timeDb.save(time.patch(json));
- return sendContent(ex,time);
- }
-
- private boolean trackTask(UmbrellaUser user, Path path, HttpExchange ex) throws IOException {
- if (path.empty()) throw missingFieldException(TASK_ID);
- Task task;
- try {
- var taskId = Long.parseLong(path.pop());
- task = taskService().load(List.of(taskId)).get(taskId);
- if (task == null) throw UmbrellaException.notFound("Failed to load task with id = {0}",taskId);
- } catch (NumberFormatException e) {
- throw invalidFieldException(TASK_ID,"long value");
- }
-
- long now;
- try {
- now = Long.parseLong(body(ex));
- } catch (NumberFormatException e) {
- throw unprocessable("request body does not contain a timestamp!");
- }
-
- var opt = getStartedTime(user);
- if (opt.isPresent()){
- var startedTime = opt.get();
-
- if (startedTime.taskIds().contains(task.id())) {
- // if the time started last already belongs to the task, there is nothing left to do
- return sendContent(ex,startedTime);
- }
- timeDb.save(startedTime.stop(now));
- }
- var track = new Time(0,user.id(),task.name(),task.description(),now,null,Started,List.of(task.id()));
- timeDb.save(track);
-
- return sendContent(ex,track);
- }
-
private boolean getStartedTime(UmbrellaUser user, HttpExchange ex) throws IOException {
var startedTime = getStartedTime(user);
if (startedTime.isPresent()) return sendContent(ex,startedTime.get());
@@ -272,4 +231,55 @@ public class TimeModule extends BaseHandler implements TimeService {
}
+ private boolean patchTime(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 alter this time!");
+ var json = json(ex);
+ timeDb.save(time.patch(json));
+ return sendContent(ex,time);
+ }
+
+ private boolean postSearch(HttpExchange ex, UmbrellaUser user) throws IOException {
+ var json = json(ex);
+ if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingFieldException(KEY);
+ var keys = Arrays.asList(key.split(" "));
+ var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
+ var notes = timeDb.find(user.id(),keys,fulltext);
+ return sendContent(ex,mapValues(notes));
+ }
+
+ private boolean trackTask(UmbrellaUser user, Path path, HttpExchange ex) throws IOException {
+ if (path.empty()) throw missingFieldException(TASK_ID);
+ Task task;
+ try {
+ var taskId = Long.parseLong(path.pop());
+ task = taskService().load(List.of(taskId)).get(taskId);
+ if (task == null) throw UmbrellaException.notFound("Failed to load task with id = {0}",taskId);
+ } catch (NumberFormatException e) {
+ throw invalidFieldException(TASK_ID,"long value");
+ }
+
+ long now;
+ try {
+ now = Long.parseLong(body(ex));
+ } catch (NumberFormatException e) {
+ throw unprocessable("request body does not contain a timestamp!");
+ }
+
+ var opt = getStartedTime(user);
+ if (opt.isPresent()){
+ var startedTime = opt.get();
+
+ if (startedTime.taskIds().contains(task.id())) {
+ // if the time started last already belongs to the task, there is nothing left to do
+ return sendContent(ex,startedTime);
+ }
+ timeDb.save(startedTime.stop(now));
+ }
+ var track = new Time(0,user.id(),task.name(),task.description(),now,null,Started,List.of(task.id()));
+ timeDb.save(track);
+
+ return sendContent(ex,track);
+ }
+
}