Browse Source

working on color customization

feature/global_error_display
Stephan Richter 1 month ago
parent
commit
ccde6e3e1d
  1. 4
      core/src/main/java/de/srsoftware/umbrella/core/model/Project.java
  2. 2
      frontend/src/routes/project/Kanban.svelte
  3. 18
      frontend/src/routes/project/KanbanCard.svelte
  4. 44
      frontend/src/routes/project/View.svelte
  5. 6
      project/src/main/java/de/srsoftware/umbrella/project/SqliteDb.java
  6. 5
      translations/src/main/resources/de.json
  7. 5
      translations/src/main/resources/en.json
  8. 32
      web/src/main/resources/web/css/bloodshed-color.css
  9. 36
      web/src/main/resources/web/css/default-color.css
  10. 20
      web/src/main/resources/web/css/winter-color.css

4
core/src/main/java/de/srsoftware/umbrella/core/model/Project.java

@ -99,6 +99,10 @@ public class Project implements Mappable { @@ -99,6 +99,10 @@ public class Project implements Mappable {
case NAME: name = json.getString(key); break;
case SHOW_CLOSED: showClosed = json.getBoolean(SHOW_CLOSED); break;
case STATUS: status = json.getInt(key); break;
case TAG_COLORS:
tagColors.clear();
json.getJSONObject(TAG_COLORS).toMap().forEach((k,v) -> tagColors.put(k,v.toString()));
break;
default: key = null;
}
if (key != null) dirtyFields.add(key);

2
frontend/src/routes/project/Kanban.svelte

@ -224,7 +224,7 @@ @@ -224,7 +224,7 @@
{#if stateList[state]}
{#each Object.values(stateList[state]).sort((a,b) => a.name.localeCompare(b.name)) as task}
{#if !filter || task.name.toLowerCase().includes(filter) || (task.tags && task.tags.filter(tag => tag.toLowerCase().includes(filter)).length)}
<Card onclick={e => openTask(task.id)} ondragstart={ev => dragged=task} {task} />
<Card onclick={e => openTask(task.id)} ondragstart={ev => dragged=task} {task} tag_colors={project.tag_colors} />
{/if}
{/each}
{/if}

18
frontend/src/routes/project/KanbanCard.svelte

@ -1,8 +1,22 @@ @@ -1,8 +1,22 @@
<script>
let { onclick, ondragstart, task } = $props();
let { onclick, ondragstart, tag_colors = {}, task } = $props();
function calcStyle(){
if (!task.tags || !tag_colors) return '';
for (let tag of task.tags){
let color = tag_colors[tag.toLowerCase()];
if (color) return `background: ${color}`;
}
return '';
}
let style = $derived.by(calcStyle)
</script>
<div draggable="true" class={`box prio_${task.total_prio} p${Math.floor(task.total_prio/10)*10} p${task.total_prio % 10}`} {onclick} {ondragstart} >
<div
draggable="true"
class={`box prio_${task.total_prio} p${Math.floor(task.total_prio/10)*10} p${task.total_prio % 10}`}
{onclick} {ondragstart} style={style}>
<span class="title">{task.name}</span>
{#if task.estimated_time}
<span class="estimate">

44
frontend/src/routes/project/View.svelte

@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
let showSettings = $state(false);
let tasks = $state(null);
let show_closed = $state(false);
let new_color = $state({tag:null,color:'#00aa00'})
let new_state = $state({code:null,name:null})
let state_available=$derived(new_state.name && new_state.code && !project.allowed_states[new_state.code]);
@ -55,6 +56,11 @@ @@ -55,6 +56,11 @@
loadTasks();
}
async function dropColor(tag){
delete project.tag_colors[tag];
update({tag_colors:project.tag_colors});
}
async function dropMember(member){
update({drop_member:member.user.id});
}
@ -112,6 +118,15 @@ @@ -112,6 +118,15 @@
}
}
function saveTagColor(){
project.tag_colors[new_color.tag] = new_color.color;
update({tag_colors:project.tag_colors});
}
function toggleSettings(){
showSettings = !showSettings;
}
async function update(data){
const url = api(`project/${id}`);
const resp = await fetch(url,{
@ -129,11 +144,6 @@ @@ -129,11 +144,6 @@
}
}
function toggleSettings(){
showSettings = !showSettings;
}
function updatePermission(user_id,permission){
let members = {};
members[user_id] = permission.code;
@ -188,8 +198,8 @@ @@ -188,8 +198,8 @@
<input type="checkbox" bind:checked={project.show_closed} onchange={changeClosed} />
{t('display_closed_tasks')}
</label>
<div>{t('members')}</div>
<div>
<div class="em">{t('members')}</div>
<div class="em">
<PermissionEditor members={project.members} {updatePermission} {addMember} {dropMember} {getCandidates} />
</div>
{#if project.allowed_states}
@ -212,7 +222,25 @@ @@ -212,7 +222,25 @@
{/if}
</div>
{/if}
{/if}
<div class="em">
{t('custom_tag_colors')}
<input type="color" bind:value={new_color.color} >
</div>
<div class="em">
<label>
{t('tag_name')}:
<input type="text" bind:value={new_color.tag} />
</label>
<button onclick={saveTagColor}>{t('add_object',{object:t('color')})}</button>
</div>
{#each Object.entries(project.tag_colors) as [k,v]}
<div style="background: {v}">{k}</div>
<div class="em">
<button onclick={e => dropColor(k)}>{t('delete')}</button>
</div>
{/each}
{/if} <!-- settings -->
{#if estimated_time.sum}
<div>{t('estimated_time')}</div>
<div class="estimated_time">{estimated_time.sum} h</div>

6
project/src/main/java/de/srsoftware/umbrella/project/SqliteDb.java

@ -148,7 +148,7 @@ CREATE TABLE IF NOT EXISTS {0} ( @@ -148,7 +148,7 @@ CREATE TABLE IF NOT EXISTS {0} (
rs = select(VALUE).from(TABLE_SETTINGS).where(KEY,equal(colorKey(projectId))).exec(db);
if (rs.next()) {
var map = project.tagColors();
new JSONObject(rs.getString(VALUE)).toMap().forEach((k, v) -> map.put(k, v.toString()));
new JSONObject(rs.getString(VALUE)).toMap().forEach((k, v) -> map.put(k.toLowerCase(), v.toString()));
}
rs.close();
return project;
@ -246,6 +246,10 @@ CREATE TABLE IF NOT EXISTS {0} ( @@ -246,6 +246,10 @@ CREATE TABLE IF NOT EXISTS {0} (
query.execute(db).close();
prj.clean(MEMBERS);
}
if (prj.isDirty(TAG_COLORS)){
replaceInto(TABLE_SETTINGS,KEY,VALUE).values(colorKey(prj.id()),new JSONObject(prj.tagColors()).toString()).execute(db).close();
prj.clean(TAG_COLORS);
}
if (prj.isDirty()){
update(TABLE_PROJECTS).set(NAME,DESCRIPTION,STATUS,COMPANY_ID,SHOW_CLOSED).where(ID,equal(prj.id())).prepare(db)
.apply(prj.name(),prj.description(),prj.status(),prj.companyId(),prj.showClosed())

5
translations/src/main/resources/de.json

@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
"client_secret": "Client-Geheimnis",
"close_settings": "Einstellungen schließen",
"code": "Code",
"color": "Farbe",
"connect_service": "mit Service verbinden",
"connected_services": "verbundene Login-Services",
"confirm_delete": "Soll '{element}' wirklich gelöscht werden?",
@ -40,10 +41,11 @@ @@ -40,10 +41,11 @@
"CREATE_USERS": "Nutzer anlegen",
"create_pdf": "PDF erzeugen",
"created_with": "erzeugt mit {tool} von {producer}",
"customer": "Kunde",
"customer_address": "Adresse",
"customer_email": "Emailadresse des Kunden",
"customer": "Kunde",
"customer_id": "Kundennummer",
"custom_tag_colors": "Nutzerdefinierte Tag-Farben",
"data_sent": "Daten übermittelt",
"date": "Datum",
@ -237,6 +239,7 @@ @@ -237,6 +239,7 @@
"succeeding_document": "Nachfolge-Dokument",
"sum_of_records": "Summe der ausgewählten Einträge",
"tag_name": "Tag-Name",
"tag_uses": "Verwendung des Tags „{tag}“",
"tags": "Tags",
"task": "Aufgabe",

5
translations/src/main/resources/en.json

@ -21,6 +21,7 @@ @@ -21,6 +21,7 @@
"client_secret": "client secret",
"close_settings": "close settings",
"code": "code",
"color": "color",
"connect_service": "connect with service",
"connected_services": "connected login services",
"confirm_delete": "Really delete '{element}'?",
@ -40,10 +41,11 @@ @@ -40,10 +41,11 @@
"CREATE_USERS": "create users",
"create_pdf": "create PDF",
"created_with": "created with {tool} by {producer}",
"customer": "customer",
"customer_address": "address",
"customer_email": "customer email address",
"customer": "customer",
"customer_id": "customer ID",
"custom_tag_colors": "custom tag colors",
"data_sent": "data sent",
"date": "date",
@ -237,6 +239,7 @@ @@ -237,6 +239,7 @@
"succeeding_document": "succeeding document",
"sum_of_records": "sum of records",
"tag_name": "tag name",
"tag_uses": "usage of tag „{tag}“",
"tags": "tags",
"task": "task",

32
web/src/main/resources/web/css/bloodshed-color.css

@ -148,7 +148,7 @@ textarea{ @@ -148,7 +148,7 @@ textarea{
}
.kanban .state_20 .box.p10,
.kanban .state_40 .box.p10{
background-color: #ffa736;
border: 5px solid #ffa736;
}
.task.p20 .name{
@ -156,7 +156,7 @@ textarea{ @@ -156,7 +156,7 @@ textarea{
}
.kanban .state_20 .box.p20,
.kanban .state_40 .box.p20{
background-color: #ff8f00;
border: 5px solid #ff8f00;
}
.task.p30 .name{
@ -164,7 +164,7 @@ textarea{ @@ -164,7 +164,7 @@ textarea{
}
.kanban .state_20 .box.p30,
.kanban .state_40 .box.p30{
background-color: #ff7b06;
border: 5px solid #ff7b06;
}
.task.p40 .name{
@ -172,7 +172,7 @@ textarea{ @@ -172,7 +172,7 @@ textarea{
}
.kanban .state_20 .box.p40,
.kanban .state_40 .box.p40{
background-color: #ff6306;
border: 5px solid #ff6306;
}
.task.p50 .name{
@ -180,7 +180,7 @@ textarea{ @@ -180,7 +180,7 @@ textarea{
}
.kanban .state_20 .box.p50,
.kanban .state_40 .box.p50{
background-color: #ff4c06;
border: 5px solid #ff4c06;
}
.task.p60 .name{
@ -188,7 +188,7 @@ textarea{ @@ -188,7 +188,7 @@ textarea{
}
.kanban .state_20 .box.p60,
.kanban .state_40 .box.p60{
background-color: #ff3506;
border: 5px solid #ff3506;
}
.task.p70 .name{
@ -196,7 +196,7 @@ textarea{ @@ -196,7 +196,7 @@ textarea{
}
.kanban .state_20 .box.p70,
.kanban .state_40 .box.p70{
background-color: #ff0000;
border: 5px solid #ff0000;
}
.task.p80 .name{
@ -204,19 +204,23 @@ textarea{ @@ -204,19 +204,23 @@ textarea{
}
.kanban .state_20 .box.p80,
.kanban .state_40 .box.p80{
background-color: #df153b;
border: 5px solid #df153b;
}
.task.p90 .name,
.kanban .state_20 .box.p90,
.kanban .state_40 .box.p90{
.task.p90 .name{
background-color: #991c34;
color: #ffff00;
}
.kanban .state_20 .box.p90,
.kanban .state_40 .box.p90{
border: 5px solid #991c34;
}
.task.p100 .name,
.kanban .state_20 .box.p100,
.kanban .state_40 .box.p100{
.task.p100 .name{
background-color: #733440;
color: #ffff00;
}
.kanban .state_20 .box.p100,
.kanban .state_40 .box.p100{
border: 5px solid #733440;
}

36
web/src/main/resources/web/css/default-color.css

@ -45,6 +45,10 @@ textarea{ @@ -45,6 +45,10 @@ textarea{
background-color: #333;
}
.em {
background: rgba(255, 215, 0, 0.09);
}
.error {
background-color: red;
color: black;
@ -150,7 +154,7 @@ textarea{ @@ -150,7 +154,7 @@ textarea{
}
.kanban .state_20 .box.p10,
.kanban .state_40 .box.p10{
background-color: #fff066;
border: 5px solid #fff066;
}
.task.p20 .name{
@ -158,7 +162,7 @@ textarea{ @@ -158,7 +162,7 @@ textarea{
}
.kanban .state_20 .box.p20,
.kanban .state_40 .box.p20{
background-color: #ffe706;
border: 5px solid #ffe706;
}
.task.p30 .name{
@ -166,7 +170,7 @@ textarea{ @@ -166,7 +170,7 @@ textarea{
}
.kanban .state_20 .box.p30,
.kanban .state_40 .box.p30{
background-color: #ffa906;
border: 5px solid #ffa906;
}
.task.p40 .name{
@ -174,7 +178,7 @@ textarea{ @@ -174,7 +178,7 @@ textarea{
}
.kanban .state_20 .box.p40,
.kanban .state_40 .box.p40{
background-color: #ff8606;
border: 5px solid #ff8606;
}
.task.p50 .name{
@ -182,7 +186,7 @@ textarea{ @@ -182,7 +186,7 @@ textarea{
}
.kanban .state_20 .box.p50,
.kanban .state_40 .box.p50{
background-color: #ff4c06;
border: 5px solid #ff4c06;
}
.task.p60 .name{
@ -190,7 +194,7 @@ textarea{ @@ -190,7 +194,7 @@ textarea{
}
.kanban .state_20 .box.p60,
.kanban .state_40 .box.p60{
background-color: #ff3506;
border: 5px solid #ff3506;
}
.task.p70 .name{
@ -198,7 +202,7 @@ textarea{ @@ -198,7 +202,7 @@ textarea{
}
.kanban .state_20 .box.p70,
.kanban .state_40 .box.p70{
background-color: #ff0000;
border: 5px solid #ff0000;
}
.task.p80 .name{
@ -206,19 +210,23 @@ textarea{ @@ -206,19 +210,23 @@ textarea{
}
.kanban .state_20 .box.p80,
.kanban .state_40 .box.p80{
background-color: #df153b;
border: 5px solid #df153b;
}
.task.p90 .name,
.kanban .state_20 .box.p90,
.kanban .state_40 .box.p90{
.task.p90 .name{
background-color: #991c34;
color: #ffff00;
}
.kanban .state_20 .box.p90,
.kanban .state_40 .box.p90{
border: 5px solid #991c34;
}
.task.p100 .name,
.kanban .state_20 .box.p100,
.kanban .state_40 .box.p100{
.task.p100 .name{
background-color: #733440;
color: #ffff00;
}
.kanban .state_20 .box.p100,
.kanban .state_40 .box.p100{
border: 5px solid #733440;
}

20
web/src/main/resources/web/css/winter-color.css

@ -142,7 +142,7 @@ textarea{ @@ -142,7 +142,7 @@ textarea{
}
.kanban .state_20 .box.p10,
.kanban .state_40 .box.p10{
background-color: #c9fbb2;
border: 5px solid #c9fbb2;
}
.task.p20 .name{
@ -150,7 +150,7 @@ textarea{ @@ -150,7 +150,7 @@ textarea{
}
.kanban .state_20 .box.p20,
.kanban .state_40 .box.p20{
background-color: #cbff57;
border: 5px solid #cbff57;
}
.task.p30 .name{
@ -158,7 +158,7 @@ textarea{ @@ -158,7 +158,7 @@ textarea{
}
.kanban .state_20 .box.p30,
.kanban .state_40 .box.p30{
background-color: #dfff44;
border: 5px solid #dfff44;
}
.task.p40 .name{
@ -166,7 +166,7 @@ textarea{ @@ -166,7 +166,7 @@ textarea{
}
.kanban .state_20 .box.p40,
.kanban .state_40 .box.p40{
background-color: #f8ff29;
border: 5px solid #f8ff29;
}
.task.p50 .name{
@ -174,7 +174,7 @@ textarea{ @@ -174,7 +174,7 @@ textarea{
}
.kanban .state_20 .box.p50,
.kanban .state_40 .box.p50{
background-color: #ffdb1b;
border: 5px solid #ffdb1b;
}
.task.p60 .name{
@ -182,7 +182,7 @@ textarea{ @@ -182,7 +182,7 @@ textarea{
}
.kanban .state_20 .box.p60,
.kanban .state_40 .box.p60{
background-color: #ff9309;
border: 5px solid #ff9309;
}
.task.p70 .name{
@ -190,7 +190,7 @@ textarea{ @@ -190,7 +190,7 @@ textarea{
}
.kanban .state_20 .box.p70,
.kanban .state_40 .box.p70{
background-color: #ff6c00;
border: 5px solid #ff6c00;
}
.task.p80 .name{
@ -198,7 +198,7 @@ textarea{ @@ -198,7 +198,7 @@ textarea{
}
.kanban .state_20 .box.p80,
.kanban .state_40 .box.p80{
background-color: #ff3c00;
border: 5px solid #ff3c00;
}
.task.p90 .name{
@ -206,7 +206,7 @@ textarea{ @@ -206,7 +206,7 @@ textarea{
}
.kanban .state_20 .box.p90,
.kanban .state_40 .box.p90{
background-color: #ff0000;
border: 5px solid #ff0000;
}
.task.p100 .name{
@ -214,5 +214,5 @@ textarea{ @@ -214,5 +214,5 @@ textarea{
}
.kanban .state_20 .box.p100,
.kanban .state_40 .box.p100{
background-color: #ff0048;
border: 5px solid #ff0048;
}
Loading…
Cancel
Save