started to implement project tagging, was interrupted.

next on: saving tags for user = null
This commit is contained in:
2025-07-28 08:49:44 +02:00
parent 0aa7aa67dc
commit 382eae000c
7 changed files with 74 additions and 22 deletions

View File

@@ -66,8 +66,8 @@ public class Application {
var legacyApi = new LegacyApi(userModule.userDb(),config); var legacyApi = new LegacyApi(userModule.userDb(),config);
var markdownApi = new MarkdownApi(userModule); var markdownApi = new MarkdownApi(userModule);
var messageApi = new MessageApi(messageSystem); var messageApi = new MessageApi(messageSystem);
var projectModule = new ProjectModule(config,companyModule);
var tagModule = new TagModule(config,userModule); var tagModule = new TagModule(config,userModule);
var projectModule = new ProjectModule(config,companyModule,tagModule);
var taskModule = new TaskModule(config,projectModule,tagModule); var taskModule = new TaskModule(config,projectModule,tagModule);
var timeModule = new TimeModule(config,taskModule); var timeModule = new TimeModule(config,taskModule);
var webHandler = new WebHandler(); var webHandler = new WebHandler();

View File

@@ -67,6 +67,7 @@ public class Constants {
public static final String PASSWORD = "password"; public static final String PASSWORD = "password";
public static final String PERMISSION = "permission"; public static final String PERMISSION = "permission";
public static final String POST = "POST"; public static final String POST = "POST";
public static final String PROJECT = "project";
public static final String PROJECT_ID = "project_id"; public static final String PROJECT_ID = "project_id";
public static final String RECEIVERS = "receivers"; public static final String RECEIVERS = "receivers";
@@ -86,6 +87,7 @@ public class Constants {
public static final String SUBJECT = "subject"; public static final String SUBJECT = "subject";
public static final String TABLE_SETTINGS = "settings"; public static final String TABLE_SETTINGS = "settings";
public static final String TAGS = "tags";
public static final String TEMPLATE = "template"; public static final String TEMPLATE = "template";
public static final String TEXT = "text"; public static final String TEXT = "text";
public static final String THEME = "theme"; public static final String THEME = "theme";

View File

@@ -5,19 +5,21 @@
import CompanySelector from '../../Components/CompanySelector.svelte'; import CompanySelector from '../../Components/CompanySelector.svelte';
import MarkdownEditor from '../../Components/MarkdownEditor.svelte'; import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
import Settings from './Settings.svelte'; import Settings from './Settings.svelte';
let showSettings = $state(false); import Tags from '../tags/TagList.svelte';
let ready = $derived(!!project.name.trim())
let error = $state(null); let error = $state(null);
let ready = $derived(!!project.name.trim())
const router = useTinyRouter(); const router = useTinyRouter();
let showSettings = $state(false);
let project = $state({ let project = $state({
name:'', name:'',
description : { source : '', rendered : '' }, description : { source : '', rendered : '' },
settings:{ settings:{
show_closed:false show_closed:false
} },
tags: []
}); });
async function onsubmit(ev){ async function onsubmit(ev){
@@ -40,6 +42,11 @@
project.company_id = company.id; project.company_id = company.id;
console.log(project); console.log(project);
} }
function toggleSettings(ev){
ev.preventDefault();
showSettings = !showSettings;
}
</script> </script>
<style> <style>
@@ -82,22 +89,39 @@
<MarkdownEditor bind:value={project.description} simple={true} /> <MarkdownEditor bind:value={project.description} simple={true} />
</td> </td>
</tr> </tr>
{#if !showSettings} {#if showSettings}
<tr> <tr>
<th> <th>
{t('settings')} {t('settings')}
</th> </th>
<td> <td>
<button onclick={() => showSettings = true}>{t('extended_settings')}</button> <label>
<input type="checkbox" bind:checked={project.settings.show_closed} />
{t('display_closed_tasks')}
</label>
</td>
</tr>
{:else}
<tr>
<th>
{t('settings')}
</th>
<td>
<button onclick={toggleSettings} >{t('extended_settings')}</button>
</td> </td>
</tr> </tr>
{/if} {/if}
<tr>
<th>
{t('tags')}
</th>
<td>
<Tags module="project" bind:tags={project.tags} />
</td>
</tr>
</tbody> </tbody>
</table> </table>
</fieldset> </fieldset>
{#if showSettings}
<Settings bind:settings={project.settings}/>
{/if}
<button type="submit" disabled={!ready}>{t('create')}</button> <button type="submit" disabled={!ready}>{t('create')}</button>
</fieldset> </fieldset>
</form> </form>

View File

@@ -6,8 +6,4 @@
</script> </script>
<fieldset> <fieldset>
<legend>{t('settings')}</legend> <legend>{t('settings')}</legend>
<label>
<input type="checkbox" bind:checked={settings.show_closed} />
{t('display_closed_tasks')}
</label>
</fieldset> </fieldset>

View File

@@ -7,12 +7,22 @@
import Editor from '../../Components/LineEditor.svelte'; import Editor from '../../Components/LineEditor.svelte';
let { module, id, user_list = [] } = $props(); let {
id = null,
module,
tags = $bindable([]),
user_list = [],
} = $props();
let error = $state(null); let error = $state(null);
let newTag = $state(''); let newTag = $state('');
let tags = $state([]);
async function addTag(tag){ async function addTag(tag){
if (!id) {
// when creating elements, they don`t have an id, yet
tags.push(tag);
tags = tags.sort();
return;
}
const url = api(`tags/${module}/${id}`); const url = api(`tags/${module}/${id}`);
const resp = await fetch(url,{ const resp = await fetch(url,{
credentials:'include', credentials:'include',
@@ -30,6 +40,10 @@
} }
async function drop(tag){ async function drop(tag){
if (!id){ // then creating new elements, the don`t have an id, yet
tags = tags.filter(item => item !== tag);
return;
}
const url = api(`tags/${module}/${id}`); const url = api(`tags/${module}/${id}`);
const resp = await fetch(url,{ const resp = await fetch(url,{
credentials:'include', credentials:'include',
@@ -46,6 +60,7 @@
} }
async function loadTags(entityId){ async function loadTags(entityId){
if (!entityId) return; // when crating elements, they dont`t have an id, yet.
const url = api(`tags/${module}/${entityId}`); const url = api(`tags/${module}/${entityId}`);
const resp = await fetch(url,{credentials:'include'}); const resp = await fetch(url,{credentials:'include'});
if (resp.ok) { if (resp.ok) {

View File

@@ -20,11 +20,14 @@ import de.srsoftware.tools.SessionToken;
import de.srsoftware.umbrella.core.BaseHandler; import de.srsoftware.umbrella.core.BaseHandler;
import de.srsoftware.umbrella.core.api.CompanyService; import de.srsoftware.umbrella.core.api.CompanyService;
import de.srsoftware.umbrella.core.api.ProjectService; import de.srsoftware.umbrella.core.api.ProjectService;
import de.srsoftware.umbrella.core.api.TagService;
import de.srsoftware.umbrella.core.api.UserService; 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.*; import de.srsoftware.umbrella.core.model.*;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
public class ProjectModule extends BaseHandler implements ProjectService { public class ProjectModule extends BaseHandler implements ProjectService {
@@ -32,12 +35,14 @@ public class ProjectModule extends BaseHandler implements ProjectService {
private final ProjectDb projects; private final ProjectDb projects;
private final CompanyService companies; private final CompanyService companies;
private final UserService users; private final UserService users;
private final TagService tags;
public ProjectModule(Configuration config, CompanyService companyService) throws UmbrellaException { public ProjectModule(Configuration config, CompanyService companyService, TagService tagService) throws UmbrellaException {
var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE)); var dbFile = config.get(CONFIG_DATABASE).orElseThrow(() -> missingFieldException(CONFIG_DATABASE));
projects = new SqliteDb(connect(dbFile)); projects = new SqliteDb(connect(dbFile));
companies = companyService; companies = companyService;
users = companies.userService(); tags = tagService;
users = companies.userService();
} }
private void addMember(Project project, long userId) { private void addMember(Project project, long userId) {
@@ -245,6 +250,12 @@ public class ProjectModule extends BaseHandler implements ProjectService {
var owner = Map.of(user.id(),new Member(user,OWNER)); var owner = Map.of(user.id(),new Member(user,OWNER));
var prj = new Project(0,name,description, OPEN,companyId,showClosed, owner); var prj = new Project(0,name,description, OPEN,companyId,showClosed, owner);
prj = projects.save(prj); prj = projects.save(prj);
if (json.has(TAGS) && json.get(TAGS) instanceof JSONArray arr){
var tagList = arr.toList().stream().filter(elem -> elem instanceof String).map(String.class::cast).toList();
tags.save(PROJECT,prj.id(),null,tagList);
}
return sendContent(ex,prj); return sendContent(ex,prj);
} }

View File

@@ -130,8 +130,12 @@ CREATE TABLE IF NOT EXISTS "{0}" (
public void save(Collection<Long> userIds, String module, long entityId, Collection<String> tags) { public void save(Collection<Long> userIds, String module, long entityId, Collection<String> tags) {
try { try {
var query = replaceInto(TABLE_TAGS,USER_ID,MODULE,ID,TAG); var query = replaceInto(TABLE_TAGS,USER_ID,MODULE,ID,TAG);
for (var userId : userIds) { for (var tag : tags) {
for (var tag : tags) query.values(userId,module,entityId,tag); if (userIds == null) { // tags not assigned to a user are available to all users
query.values(null, module, entityId, tag);
} else for (var userId : userIds) {
query.values(userId,module,entityId,tag);
}
} }
query.execute(db).close(); query.execute(db).close();
} catch (SQLException e){ } catch (SQLException e){