implemented adding notes to projects and tasks
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -34,7 +34,7 @@ public record Note(long id, String module, long entityId, long authorId, String
|
|||||||
USER_ID,authorId,
|
USER_ID,authorId,
|
||||||
TEXT,text,
|
TEXT,text,
|
||||||
RENDERED,markdown(text),
|
RENDERED,markdown(text),
|
||||||
TIMESTAMP,timestamp
|
TIMESTAMP,timestamp.withNano(0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,12 @@
|
|||||||
if (simple) {
|
if (simple) {
|
||||||
value.source = editValue.source;
|
value.source = editValue.source;
|
||||||
value.rendered = editValue.rendered;
|
value.rendered = editValue.rendered;
|
||||||
} else if (ev.keyCode == 13 && ev.ctrlKey) applyEdit();
|
}
|
||||||
|
if (ev.keyCode == 13 && ev.ctrlKey){
|
||||||
|
if (simple){
|
||||||
|
onSet(editValue.source);
|
||||||
|
} else applyEdit();
|
||||||
|
}
|
||||||
if (ev.keyCode == 27) resetEdit();
|
if (ev.keyCode == 27) resetEdit();
|
||||||
|
|
||||||
if (timer) clearTimeout(timer);
|
if (timer) clearTimeout(timer);
|
||||||
|
|||||||
@@ -2,16 +2,16 @@
|
|||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { api } from '../../urls.svelte.js';
|
import { api } from '../../urls.svelte.js';
|
||||||
import { t } from '../../translations.svelte.js';
|
import { t } from '../../translations.svelte.js';
|
||||||
|
import { user } from '../../user.svelte.js';
|
||||||
import Editor from '../../Components/MarkdownEditor.svelte';
|
import Editor from '../../Components/MarkdownEditor.svelte';
|
||||||
|
|
||||||
let error = $state(null);
|
let error = $state(null);
|
||||||
let { module = null, entity_id = null } = $props();
|
let { module = null, entity_id = null } = $props();
|
||||||
let note = $state({source:null,rendered:null});
|
let note = $state({source:null,rendered:null});
|
||||||
let notes = $state(null);
|
let notes = $state(null);
|
||||||
|
let authors = $state(null);
|
||||||
|
|
||||||
async function onclick(){
|
async function saveNote(){
|
||||||
alert(note.source);
|
|
||||||
const url = api(`notes/${module}/${entity_id}`);
|
const url = api(`notes/${module}/${entity_id}`);
|
||||||
const resp = await fetch(url,{
|
const resp = await fetch(url,{
|
||||||
credentials:'include',
|
credentials:'include',
|
||||||
@@ -19,8 +19,15 @@
|
|||||||
body:note.source
|
body:note.source
|
||||||
});
|
});
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
|
let newNote = await resp.json();
|
||||||
|
authors[user.id] = user;
|
||||||
|
notes[newNote.id] = newNote;
|
||||||
|
note = {source:'',rendered:''};
|
||||||
|
error = null;
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
error = await resp.text();
|
error = await resp.text();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +35,9 @@
|
|||||||
const url = api(`notes/${module}/${entity_id}`);
|
const url = api(`notes/${module}/${entity_id}`);
|
||||||
const resp = await fetch(url,{credentials:'include'});
|
const resp = await fetch(url,{credentials:'include'});
|
||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
notes = await resp.json();
|
const data = await resp.json();
|
||||||
|
notes = data.notes;
|
||||||
|
authors = data.authors;
|
||||||
} else {
|
} else {
|
||||||
error = await resp.text();
|
error = await resp.text();
|
||||||
}
|
}
|
||||||
@@ -37,19 +46,31 @@
|
|||||||
onMount(load)
|
onMount(load)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>{t('notes')}</h1>
|
<style>
|
||||||
|
fieldset{
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
legend.time{
|
||||||
|
position: absolute;
|
||||||
|
top: -19px;
|
||||||
|
right: 20px;
|
||||||
|
background: black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<span class="error">{error}</span>
|
<span class="error">{error}</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if notes}
|
{#if notes}
|
||||||
{#each Object.entries(notes) as [a,b]}
|
{#each Object.entries(notes) as [a,b]}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>User {b.user_id} – {b.timestamp.replace('T',' ')}</legend>
|
<legend class="author">{authors[b.user_id].name}</legend>
|
||||||
|
<legend class="time">{b.timestamp.replace('T',' ')}</legend>
|
||||||
{@html b.rendered}
|
{@html b.rendered}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="editor">
|
||||||
<Editor simple={true} bind:value={note} />
|
<Editor simple={true} bind:value={note} onSet={saveNote} />
|
||||||
<button {onclick}>{t('save_note')}</button>
|
<button onclick={saveNote}>{t('save_note')}</button>
|
||||||
|
</div>
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
import LineEditor from '../../Components/LineEditor.svelte';
|
import LineEditor from '../../Components/LineEditor.svelte';
|
||||||
import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
|
import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
|
||||||
import MemberEditor from '../../Components/MemberEditor.svelte';
|
import MemberEditor from '../../Components/MemberEditor.svelte';
|
||||||
|
import Notes from '../notes/List.svelte';
|
||||||
import StateSelector from '../../Components/StateSelector.svelte';
|
import StateSelector from '../../Components/StateSelector.svelte';
|
||||||
import Tags from '../tags/TagList.svelte';
|
import Tags from '../tags/TagList.svelte';
|
||||||
import TaskList from '../task/TaskList.svelte';
|
import TaskList from '../task/TaskList.svelte';
|
||||||
@@ -211,3 +212,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="notes">
|
||||||
|
<h3>{t('notes')}</h3>
|
||||||
|
<Notes module="project" entity_id={id} />
|
||||||
|
</div>
|
||||||
@@ -294,12 +294,10 @@
|
|||||||
<TagList module="task" {id} user_list={Object.keys(task.members).map(id => +id)} />
|
<TagList module="task" {id} user_list={Object.keys(task.members).map(id => +id)} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th>{t('notes')}</th>
|
|
||||||
<td>
|
|
||||||
<Notes module="task" entity_id={id} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div class="notes">
|
||||||
|
<h3>{t('notes')}</h3>
|
||||||
|
<Notes module="task" entity_id={id} />
|
||||||
|
</div>
|
||||||
@@ -19,12 +19,14 @@ import de.srsoftware.umbrella.core.api.UserService;
|
|||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.Note;
|
import de.srsoftware.umbrella.core.model.Note;
|
||||||
import de.srsoftware.umbrella.core.model.Token;
|
import de.srsoftware.umbrella.core.model.Token;
|
||||||
|
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NoteModule extends BaseHandler implements NoteService {
|
public class NoteModule extends BaseHandler implements NoteService {
|
||||||
private final NotesDb notesDb;
|
private final NotesDb notesDb;
|
||||||
@@ -71,7 +73,9 @@ public class NoteModule extends BaseHandler implements NoteService {
|
|||||||
if (module == null) throw unprocessable("Module missing in path.");
|
if (module == null) throw unprocessable("Module missing in path.");
|
||||||
var head = path.pop();
|
var head = path.pop();
|
||||||
long entityId = Long.parseLong(head);
|
long entityId = Long.parseLong(head);
|
||||||
return sendContent(ex, mapValues(getNotes(module,entityId)));
|
var notes = getNotes(module,entityId);
|
||||||
|
var authors = notes.values().stream().map(Note::authorId).distinct().map(users::loadUser).collect(Collectors.toMap(UmbrellaUser::id,UmbrellaUser::toMap));
|
||||||
|
return sendContent(ex, Map.of("notes",mapValues(notes),"authors",authors));
|
||||||
} catch (NumberFormatException e){
|
} catch (NumberFormatException e){
|
||||||
return sendContent(ex,HTTP_UNPROCESSABLE,"Entity id missing in path.");
|
return sendContent(ex,HTTP_UNPROCESSABLE,"Entity id missing in path.");
|
||||||
} catch (UmbrellaException e){
|
} catch (UmbrellaException e){
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
"data_sent": "Daten übermittelt",
|
"data_sent": "Daten übermittelt",
|
||||||
"date": "Datum",
|
"date": "Datum",
|
||||||
"delete": "löschen",
|
"delete": "löschen",
|
||||||
|
"delete_task": "Aufgabe löschen ",
|
||||||
"DELETE_USERS": "Nutzer löschen",
|
"DELETE_USERS": "Nutzer löschen",
|
||||||
"delivery_date": "Lieferdatum",
|
"delivery_date": "Lieferdatum",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
@@ -187,6 +188,7 @@
|
|||||||
"show_closed": "geschlossene anzeigen",
|
"show_closed": "geschlossene anzeigen",
|
||||||
"show_kanban": "Kanban-Ansicht",
|
"show_kanban": "Kanban-Ansicht",
|
||||||
"start_date": "Startdatum",
|
"start_date": "Startdatum",
|
||||||
|
"started": "angefangen",
|
||||||
"state": "Status",
|
"state": "Status",
|
||||||
"state_cancelled": "abgebrochen",
|
"state_cancelled": "abgebrochen",
|
||||||
"state_complete": "abgeschlossen",
|
"state_complete": "abgeschlossen",
|
||||||
|
|||||||
Reference in New Issue
Block a user