Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd32779f9f | ||
|
|
1b2cef5bd8 | ||
|
|
de0d9c1797 | ||
|
|
db66097253 | ||
|
|
92faa364c9 | ||
|
|
66bd0e3741 | ||
|
|
c61a2b14b6 | ||
|
|
57bce84e8e | ||
|
|
6e9d32f105 | ||
|
|
6d9e368e43 | ||
|
|
b5b361514d |
@@ -124,16 +124,13 @@ public class SqliteDb extends BaseDb implements AccountDb {
|
|||||||
db.setAutoCommit(false);
|
db.setAutoCommit(false);
|
||||||
Query.delete().from(TABLE_TAGS_TRANSACTIONS).where(TRANSACTION_ID,equal(transaction.id())).execute(db);
|
Query.delete().from(TABLE_TAGS_TRANSACTIONS).where(TRANSACTION_ID,equal(transaction.id())).execute(db);
|
||||||
Query.delete().from(TABLE_TRANSACTIONS).where(ID,equal(transaction.id())).execute(db);
|
Query.delete().from(TABLE_TRANSACTIONS).where(ID,equal(transaction.id())).execute(db);
|
||||||
|
db.setAutoCommit(true);
|
||||||
return transaction;
|
return transaction;
|
||||||
} catch (SQLException e){
|
} catch (SQLException e){
|
||||||
try {
|
try {
|
||||||
db.rollback();
|
db.rollback();
|
||||||
} catch (SQLException ignored){};
|
} catch (SQLException ignored){};
|
||||||
throw failedToDropObject(transaction);
|
throw failedToDropObject(transaction);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ subprojects {
|
|||||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
implementation("de.srsoftware:configuration.api:1.0.2")
|
implementation("de.srsoftware:configuration.api:1.0.2")
|
||||||
implementation("de.srsoftware:tools.jdbc:2.0.8")
|
implementation("de.srsoftware:tools.jdbc:2.0.7")
|
||||||
implementation("de.srsoftware:tools.http:6.0.5")
|
implementation("de.srsoftware:tools.http:6.0.5")
|
||||||
implementation("de.srsoftware:tools.mime:1.1.4")
|
implementation("de.srsoftware:tools.mime:1.1.4")
|
||||||
implementation("de.srsoftware:tools.logging:1.3.2")
|
implementation("de.srsoftware:tools.logging:1.3.2")
|
||||||
|
|||||||
@@ -67,16 +67,9 @@ public class SqliteDb extends BaseDb implements ContactDb{
|
|||||||
db.setAutoCommit(false);
|
db.setAutoCommit(false);
|
||||||
Query.delete().from(TABLE_CONTACTS).where(ID,equal(contact.id())).execute(db);
|
Query.delete().from(TABLE_CONTACTS).where(ID,equal(contact.id())).execute(db);
|
||||||
Query.delete().from(TABLE_CONTACTS_USERS).where(CONTACT_ID,equal(contact.id())).execute(db);
|
Query.delete().from(TABLE_CONTACTS_USERS).where(CONTACT_ID,equal(contact.id())).execute(db);
|
||||||
|
db.setAutoCommit(true);
|
||||||
} catch (SQLException e){
|
} catch (SQLException e){
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
throw failedToDropObject(t(CONTACT_WITH_ID, ID,contact.id())).causedBy(e);
|
throw failedToDropObject(t(CONTACT_WITH_ID, ID,contact.id())).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,15 +69,9 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255)
|
|||||||
update(table).set(STATUS).where(STATUS,equal(20)).prepare(db).apply(40).execute();
|
update(table).set(STATUS).where(STATUS,equal(20)).prepare(db).apply(40).execute();
|
||||||
update(table).set(STATUS).where(STATUS,equal(10)).prepare(db).apply(20).execute();
|
update(table).set(STATUS).where(STATUS,equal(10)).prepare(db).apply(20).execute();
|
||||||
update(table).set(STATUS).where(STATUS,equal(0)).prepare(db).apply(10).execute();
|
update(table).set(STATUS).where(STATUS,equal(0)).prepare(db).apply(10).execute();
|
||||||
|
db.setAutoCommit(true);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored) {}
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ package de.srsoftware.umbrella.core.api;
|
|||||||
|
|
||||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||||
import de.srsoftware.umbrella.core.model.Task;
|
import de.srsoftware.umbrella.core.model.Task;
|
||||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface TaskService {
|
public interface TaskService {
|
||||||
void clonePermissions(long project_id, UmbrellaUser source, UmbrellaUser dest);
|
|
||||||
Map<Long, Task> listCompanyTasks(long companyId) throws UmbrellaException;
|
Map<Long, Task> listCompanyTasks(long companyId) throws UmbrellaException;
|
||||||
Map<Long, Task> listProjectTasks(long projectId) throws UmbrellaException;
|
Map<Long, Task> listProjectTasks(long projectId) throws UmbrellaException;
|
||||||
Map<Long, Task> load(Collection<Long> taskIds);
|
Map<Long, Task> load(Collection<Long> taskIds);
|
||||||
|
|||||||
@@ -77,11 +77,10 @@ public class Field {
|
|||||||
public static final String HEAD = "head";
|
public static final String HEAD = "head";
|
||||||
public static final String HOURS = "hours";
|
public static final String HOURS = "hours";
|
||||||
|
|
||||||
public static final String ID = "id";
|
public static final String ID = "id";
|
||||||
public static final String INHERIT_FROM = "inherit_from";
|
public static final String INSTANTLY = "instantly";
|
||||||
public static final String INSTANTLY = "instantly";
|
public static final String ITEM = "item";
|
||||||
public static final String ITEM = "item";
|
public static final String ITEM_CODE = "item_code";
|
||||||
public static final String ITEM_CODE = "item_code";
|
|
||||||
|
|
||||||
public static final String KEY = "key";
|
public static final String KEY = "key";
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ public class Path {
|
|||||||
public static final String PROPERTY = "property";
|
public static final String PROPERTY = "property";
|
||||||
public static final String PURPOSES = "purposes";
|
public static final String PURPOSES = "purposes";
|
||||||
|
|
||||||
public static final String READ = "read";
|
public static final String READ = "read";
|
||||||
public static final String REDIRECT = "redirect";
|
public static final String REDIRECT = "redirect";
|
||||||
|
|
||||||
public static final String SEARCH = "search";
|
public static final String SEARCH = "search";
|
||||||
public static final String SELECT = "select";
|
public static final String SELECT = "select";
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
*.db-journal
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"umbrella": {
|
||||||
|
"base_url": "http://127.0.0.1:5173",
|
||||||
|
"logging": {
|
||||||
|
"rootLevel": "INFO"
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"port": 8080
|
||||||
|
},
|
||||||
|
"threads": 16,
|
||||||
|
"modules": {
|
||||||
|
"accounting": {
|
||||||
|
"database": "demodata/accounting.db"
|
||||||
|
},
|
||||||
|
"bookmark": {
|
||||||
|
"database": "demodata/bookmark.db"
|
||||||
|
},
|
||||||
|
"company": {
|
||||||
|
"database": "demodata/company.db"
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"database": "demodata/contacts.db"
|
||||||
|
},
|
||||||
|
"document": {
|
||||||
|
"database": "demodata/documents.db",
|
||||||
|
"templates": "demodata/templates"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"database": "demodata/files.db",
|
||||||
|
"base_dir": "demodata/filestore"
|
||||||
|
},
|
||||||
|
"journal": {
|
||||||
|
"database": "demodata/journal.db"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"database": "demodata/message.db",
|
||||||
|
"smtp": {
|
||||||
|
"from": "umbrella@example.com",
|
||||||
|
"host": "none",
|
||||||
|
"pass": "none",
|
||||||
|
"port": 587,
|
||||||
|
"user": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notes": {
|
||||||
|
"database": "demodata/notes.db"
|
||||||
|
},
|
||||||
|
"poll": {
|
||||||
|
"database": "demodata/poll.db"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"database": "demodata/projects.db"
|
||||||
|
},
|
||||||
|
"stock": {
|
||||||
|
"database": "demodata/stock.db"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"database": "demodata/tags.db"
|
||||||
|
},
|
||||||
|
"task": {
|
||||||
|
"database": "demodata/tasks.db"
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"database": "demodata/times.db"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"database": "demodata/users.db"
|
||||||
|
},
|
||||||
|
"wiki": {
|
||||||
|
"database": "demodata/wiki.db"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -198,17 +198,11 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
rs.close();
|
rs.close();
|
||||||
delete().from(TABLE_POSITIONS).where(DOCUMENT_ID,equal(docId)).execute(db);
|
delete().from(TABLE_POSITIONS).where(DOCUMENT_ID,equal(docId)).execute(db);
|
||||||
delete().from(TABLE_DOCUMENTS).where(ID,equal(docId)).execute(db);
|
delete().from(TABLE_DOCUMENTS).where(ID,equal(docId)).execute(db);
|
||||||
|
db.setAutoCommit(true);
|
||||||
if (number != null) return number;
|
if (number != null) return number;
|
||||||
throw failedToDropObject(t(DOCUMENT_WITH_ID, ID,docId));
|
throw failedToDropObject(t(DOCUMENT_WITH_ID, ID,docId));
|
||||||
} catch (SQLException e){
|
} catch (SQLException e){
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
throw failedToDropObject(t(DOCUMENT_WITH_ID, ID,docId)).causedBy(e);
|
throw failedToDropObject(t(DOCUMENT_WITH_ID, ID,docId)).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,16 +217,10 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
stmt.setLong(2,pos);
|
stmt.setLong(2,pos);
|
||||||
stmt.execute();
|
stmt.execute();
|
||||||
stmt.close();
|
stmt.close();
|
||||||
|
db.setAutoCommit(true);
|
||||||
return pos;
|
return pos;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
throw failedToDropObjectFromObject(POSITION,pos,t(Text.DOCUMENT),docId).causedBy(e);
|
throw failedToDropObjectFromObject(POSITION,pos,t(Text.DOCUMENT),docId).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -594,15 +582,9 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
update(TABLE_POSITIONS).set(POS).where(DOCUMENT_ID,equal(docId)).where(POS,equal(pair.left())).prepare(db).apply(-pair.right()).close();
|
update(TABLE_POSITIONS).set(POS).where(DOCUMENT_ID,equal(docId)).where(POS,equal(pair.left())).prepare(db).apply(-pair.right()).close();
|
||||||
update(TABLE_POSITIONS).set(POS).where(DOCUMENT_ID,equal(docId)).where(POS,equal(pair.right())).prepare(db).apply(pair.left()).close();
|
update(TABLE_POSITIONS).set(POS).where(DOCUMENT_ID,equal(docId)).where(POS,equal(pair.right())).prepare(db).apply(pair.left()).close();
|
||||||
update(TABLE_POSITIONS).set(POS).where(DOCUMENT_ID,equal(docId)).where(POS,equal(-pair.right())).prepare(db).apply(pair.right()).close();
|
update(TABLE_POSITIONS).set(POS).where(DOCUMENT_ID,equal(docId)).where(POS,equal(-pair.right())).prepare(db).apply(pair.right()).close();
|
||||||
|
db.setAutoCommit(true);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
throw databaseException(FAILED_TO_SWITCH_POSITIONS,"a",pair.left(),"b",pair.right(),docId).causedBy(e);
|
throw databaseException(FAILED_TO_SWITCH_POSITIONS,"a",pair.left(),"b",pair.right(),docId).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
return pair;
|
return pair;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,6 @@
|
|||||||
<Route path="/stock/location/:location_id" component={Stock} />
|
<Route path="/stock/location/:location_id" component={Stock} />
|
||||||
<Route path="/stock/:item_id/view" component={Stock} />
|
<Route path="/stock/:item_id/view" component={Stock} />
|
||||||
<Route path="/stock/:owner/:owner_id/item/:owner_number" component={Stock} />
|
<Route path="/stock/:owner/:owner_id/item/:owner_number" component={Stock} />
|
||||||
<Route path="/stock/:owner/:owner_id/item/:owner_number/view" component={Stock} />
|
|
||||||
<Route path="/tags" component={TagList} />
|
<Route path="/tags" component={TagList} />
|
||||||
<Route path="/tags/use/:tag" component={TagUses} />
|
<Route path="/tags/use/:tag" component={TagUses} />
|
||||||
<Route path="/task" component={TaskList} />
|
<Route path="/task" component={TaskList} />
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
|
|
||||||
let {
|
let {
|
||||||
enabled = true,
|
|
||||||
id = null,
|
id = null,
|
||||||
autofocus = false,
|
autofocus = false,
|
||||||
getCandidates = dummyGetCandidates,
|
getCandidates = dummyGetCandidates,
|
||||||
@@ -116,7 +115,7 @@
|
|||||||
|
|
||||||
function select(index){
|
function select(index){
|
||||||
candidate = candidates[index];
|
candidate = candidates[index];
|
||||||
disableDropdown();
|
<disableDropdown></disableDropdown>();
|
||||||
onSelect(candidate);
|
onSelect(candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +148,7 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<span class="autocomplete">
|
<span class="autocomplete">
|
||||||
<input type="text" bind:value={candidate.display} {onkeyup} autofocus={autofocus} {id} {onblur} disabled={!enabled} />
|
<input type="text" bind:value={candidate.display} {onkeyup} autofocus={autofocus} {id} {onblur} />
|
||||||
{#if candidates && candidates.length > 0}
|
{#if candidates && candidates.length > 0}
|
||||||
<ul bind:this={list_elem} class="suggestions" tabindex="-1">
|
<ul bind:this={list_elem} class="suggestions" tabindex="-1">
|
||||||
{#each candidates as candidate,i}
|
{#each candidates as candidate,i}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
let {
|
let {
|
||||||
addMember = (entry) => console.log(`no handler for addMember(${entry})`),
|
addMember = (entry) => console.log(`no handler for addMember(${entry})`),
|
||||||
enabled = true,
|
|
||||||
dropMember = (member) => console.log(`no handler for dropMember(${member})`),
|
dropMember = (member) => console.log(`no handler for dropMember(${member})`),
|
||||||
getCandidates = defaultGetCandidates,
|
getCandidates = defaultGetCandidates,
|
||||||
members,
|
members,
|
||||||
@@ -62,7 +61,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{t('add_object',{object:t('member')})}</td>
|
<td>{t('add_object',{object:t('member')})}</td>
|
||||||
<td>
|
<td>
|
||||||
<Autocomplete {getCandidates} onSelect={addMember} {enabled} />
|
<Autocomplete {getCandidates} onSelect={addMember} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -4,47 +4,12 @@
|
|||||||
|
|
||||||
import { api } from '../urls.svelte';
|
import { api } from '../urls.svelte';
|
||||||
import { t } from '../translations.svelte';
|
import { t } from '../translations.svelte';
|
||||||
import { error, yikes } from '../warn.svelte';
|
|
||||||
import { msToTime, now } from '../time.svelte';
|
import { msToTime, now } from '../time.svelte';
|
||||||
import { timetrack } from '../user.svelte';
|
import { timetrack } from '../user.svelte';
|
||||||
|
|
||||||
let interval = null;
|
let interval = null;
|
||||||
const router = useTinyRouter();
|
const router = useTinyRouter();
|
||||||
|
|
||||||
async function addTime(task_id){
|
|
||||||
const url = api(`time/track_task/${task_id}`);
|
|
||||||
const resp = await fetch(url,{
|
|
||||||
credentials : 'include',
|
|
||||||
method : 'POST',
|
|
||||||
body : now()
|
|
||||||
}); // create new time or return time with assigned tasks
|
|
||||||
if (resp.ok) {
|
|
||||||
const track = await resp.json();
|
|
||||||
timetrack.running = track;
|
|
||||||
yikes();
|
|
||||||
} else {
|
|
||||||
error(resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function go(){
|
|
||||||
router.navigate('/time');
|
|
||||||
}
|
|
||||||
|
|
||||||
function ondragover(ev){
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function ondragleave(ev){
|
|
||||||
ev.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function ondrop(ev){
|
|
||||||
let task_id = ev.dataTransfer.getData('task');
|
|
||||||
if (task_id) addTime(task_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function stopTrack(){
|
async function stopTrack(){
|
||||||
if (timetrack.running.id){
|
if (timetrack.running.id){
|
||||||
const url = api(`time/${timetrack.running.id}/stop`);
|
const url = api(`time/${timetrack.running.id}/stop`);
|
||||||
@@ -63,6 +28,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function go(){
|
||||||
|
router.navigate('/time');
|
||||||
|
}
|
||||||
|
|
||||||
function updateElapsed(){
|
function updateElapsed(){
|
||||||
timetrack.elapsed = msToTime(Date.now() - timetrack.start);
|
timetrack.elapsed = msToTime(Date.now() - timetrack.start);
|
||||||
@@ -86,7 +54,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{#if timetrack.running}
|
{#if timetrack.running}
|
||||||
<span class="timetracking" {ondrop} {ondragover} {ondragleave} >
|
<span class="timetracking">
|
||||||
<span onclick={go} >{timetrack.running.subject} {#if timetrack.elapsed}({timetrack.elapsed}){/if}</span>
|
<span onclick={go} >{timetrack.running.subject} {#if timetrack.elapsed}({timetrack.elapsed}){/if}</span>
|
||||||
<button onclick={stopTrack} title={t('stop')} class="symbol"></button>
|
<button onclick={stopTrack} title={t('stop')} class="symbol"></button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import Autocomplete from './Autocomplete.svelte';
|
import Autocomplete from './Autocomplete.svelte';
|
||||||
|
|
||||||
let {
|
let {
|
||||||
enabled = true,
|
|
||||||
getCandidates = async text => {},
|
getCandidates = async text => {},
|
||||||
heading = t('add_object',{object:t('user')}),
|
heading = t('add_object',{object:t('user')}),
|
||||||
users = $bindable({})
|
users = $bindable({})
|
||||||
@@ -38,7 +37,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{heading}</td>
|
<td>{heading}</td>
|
||||||
<td>
|
<td>
|
||||||
<Autocomplete {getCandidates} {onSelect} {enabled} />
|
<Autocomplete {getCandidates} {onSelect} />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { useTinyRouter } from 'svelte-tiny-router';
|
import { useTinyRouter } from 'svelte-tiny-router';
|
||||||
import { t } from '../../translations.svelte';
|
|
||||||
|
|
||||||
import LineEditor from '../../Components/LineEditor.svelte';
|
import LineEditor from '../../Components/LineEditor.svelte';
|
||||||
import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
|
import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
|
||||||
@@ -24,17 +23,13 @@
|
|||||||
function movedown(){
|
function movedown(){
|
||||||
movePos(pos.number,1);
|
movePos(pos.number,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleOptional(ev){
|
|
||||||
const ok = await submit(`${prefix}.optional`,!pos.optional);
|
|
||||||
if (ok) pos.optional = !pos.optional;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.move{
|
.move{
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr > *:nth-child(1){
|
tr > *:nth-child(1){
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@@ -71,7 +66,6 @@
|
|||||||
<span onclick={moveup}>⏫</span>
|
<span onclick={moveup}>⏫</span>
|
||||||
{/if}
|
{/if}
|
||||||
<span onclick={() => drop(pos.number)}>❌</span>
|
<span onclick={() => drop(pos.number)}>❌</span>
|
||||||
<span class="symbol" onclick={toggleOptional} title={t(pos.optional?'optional position':'fixed position')}>{pos.optional?'':''}</span>
|
|
||||||
<span onclick={movedown}>⏬</span>
|
<span onclick={movedown}>⏬</span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -207,11 +207,6 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ondragstart(ev, task){
|
|
||||||
dragged = task;
|
|
||||||
ev.dataTransfer.setData("task",task.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openTask(task_id){
|
function openTask(task_id){
|
||||||
window.open(`/task/${task_id}/view`, '_blank').focus();
|
window.open(`/task/${task_id}/view`, '_blank').focus();
|
||||||
}
|
}
|
||||||
@@ -311,7 +306,7 @@
|
|||||||
<div class={['state_'+state, is_custom(state) ? '':'state_custom' ,highlight.user == u.id && highlight.state == state ? 'highlight':'']} ondragover={ev => hover(ev,u.id,state)} ondragleave={e => delete highlight.user} ondrop={ev => drop(u.id,state)} >
|
<div class={['state_'+state, is_custom(state) ? '':'state_custom' ,highlight.user == u.id && highlight.state == state ? 'highlight':'']} ondragover={ev => hover(ev,u.id,state)} ondragleave={e => delete highlight.user} ondrop={ev => drop(u.id,state)} >
|
||||||
{#each Object.values(tasks[u.id][state]).sort(byName) as task}
|
{#each Object.values(tasks[u.id][state]).sort(byName) as task}
|
||||||
{#if !filter || task.name.toLowerCase().includes(filter) || (task.tags && task.tags.filter(tag => tag.toLowerCase().includes(filter)).length)}
|
{#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 => ondragstart(ev,task)} {task} tag_colors={project.tag_colors} />
|
<Card onclick={e => openTask(task.id)} ondragstart={ev => dragged=task} {task} tag_colors={project.tag_colors} />
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
<div class="add_task" onclick={ev => show_task_form(project.id,u.id,+state)}>
|
<div class="add_task" onclick={ev => show_task_form(project.id,u.id,+state)}>
|
||||||
|
|||||||
@@ -25,13 +25,12 @@
|
|||||||
let tasks = $state(null);
|
let tasks = $state(null);
|
||||||
let show_closed = $state(false);
|
let show_closed = $state(false);
|
||||||
let new_color = $state({tag:null,color:'#00aa00'})
|
let new_color = $state({tag:null,color:'#00aa00'})
|
||||||
let inherit_from = $state(0);
|
|
||||||
|
|
||||||
let new_state = $state({code:null,name:null})
|
let new_state = $state({code:null,name:null})
|
||||||
let state_available=$derived(new_state.name && new_state.code && !project.allowed_states[new_state.code]);
|
let state_available=$derived(new_state.name && new_state.code && !project.allowed_states[new_state.code]);
|
||||||
|
|
||||||
async function addMember(user){
|
async function addMember(user){
|
||||||
return await update({new_member:+user.id,inherit_from});
|
return await update({new_member:+user.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function addState(){
|
async function addState(){
|
||||||
@@ -240,16 +239,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="em">{t('members')}</div>
|
<div class="em">{t('members')}</div>
|
||||||
<div class="em">
|
<div class="em">
|
||||||
<PermissionEditor members={project.members} {updatePermission} {addMember} {dropMember} enabled={inherit_from != 0}/>
|
<PermissionEditor members={project.members} {updatePermission} {addMember} {dropMember} />
|
||||||
<label>
|
|
||||||
└→ Inherit task permissions from:
|
|
||||||
<select bind:value={inherit_from}>
|
|
||||||
<option value={0}>{t('select user')}</option>
|
|
||||||
{#each Object.values(project.members) as member (member.user.id)}
|
|
||||||
<option value={member.user.id}>{member.user.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
{#if project.allowed_states}
|
{#if project.allowed_states}
|
||||||
{#each Object.keys(project.allowed_states) as key,idx}
|
{#each Object.keys(project.allowed_states) as key,idx}
|
||||||
|
|||||||
@@ -265,10 +265,8 @@
|
|||||||
<legend>
|
<legend>
|
||||||
{t('stock')}
|
{t('stock')}
|
||||||
</legend>
|
</legend>
|
||||||
{#if Object.keys(stock.items).length }
|
|
||||||
{t('items')}
|
|
||||||
<ul>
|
<ul>
|
||||||
{#each Object.values(stock.items) as item}
|
{#each Object.values(stock) as item}
|
||||||
<li>
|
<li>
|
||||||
<a href="/stock/{item.owner.type}/{item.owner.id}/item/{item.owner_number}" {onclick} >
|
<a href="/stock/{item.owner.type}/{item.owner.id}/item/{item.owner_number}" {onclick} >
|
||||||
{item.name} [{t('code')}: <span class="code">{item.code}</span>]
|
{item.name} [{t('code')}: <span class="code">{item.code}</span>]
|
||||||
@@ -277,18 +275,6 @@
|
|||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
{/if}
|
|
||||||
{#if Object.keys(stock.locations).length}
|
|
||||||
{t('locations')}
|
|
||||||
<ul>
|
|
||||||
{#each Object.values(stock.locations) as loc (loc.id)}
|
|
||||||
<li>
|
|
||||||
<a href="/stock/location/{loc.id}">{loc.name}</a>
|
|
||||||
<br/> {@html loc.description.rendered}
|
|
||||||
</li>
|
|
||||||
{/each}
|
|
||||||
</ul>
|
|
||||||
{/if}
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{/if}
|
{/if}
|
||||||
{#if times}
|
{#if times}
|
||||||
|
|||||||
@@ -130,10 +130,6 @@
|
|||||||
onMount(load);
|
onMount(load);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Umbrella – {t('Easylist')}: {tag}</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<h2>{t('tasks_for_tag',{tag:decodeURI(tag)})}</h2>
|
<h2>{t('tasks_for_tag',{tag:decodeURI(tag)})}</h2>
|
||||||
|
|
||||||
<div class="easylist">
|
<div class="easylist">
|
||||||
|
|||||||
@@ -81,10 +81,6 @@
|
|||||||
router.navigate(`/project/${project.id}/view`)
|
router.navigate(`/project/${project.id}/view`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function gotoTask(tid){
|
|
||||||
if (id) router.navigate(`/task/${tid}/view`)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleUpdateEvent(evt){
|
function handleUpdateEvent(evt){
|
||||||
let json = JSON.parse(evt.data);
|
let json = JSON.parse(evt.data);
|
||||||
if (json.task) {
|
if (json.task) {
|
||||||
@@ -123,8 +119,7 @@
|
|||||||
if (task.show_closed) show_closed = true;
|
if (task.show_closed) show_closed = true;
|
||||||
loadChildren();
|
loadChildren();
|
||||||
if (task.project_id) loadProject();
|
if (task.project_id) loadProject();
|
||||||
if (task.parent_task_id) await loadParent();
|
if (task.parent_task_id) loadParent();
|
||||||
loadSiblings();
|
|
||||||
} else error(resp);
|
} else error(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,36 +129,13 @@
|
|||||||
if (resp.ok){
|
if (resp.ok){
|
||||||
project = await resp.json();
|
project = await resp.json();
|
||||||
yikes();
|
yikes();
|
||||||
} else error(resp);
|
} else error(await resp.text());
|
||||||
}
|
|
||||||
|
|
||||||
async function loadSiblings(){
|
|
||||||
const url = api(`task/list`);
|
|
||||||
const select = task.parent_task_id ? {parent_task_id:task.parent_task_id} : {project_id: task.project_id};
|
|
||||||
select.show_closed = false;
|
|
||||||
const res = await post(url,select);
|
|
||||||
if (res.ok){
|
|
||||||
task.siblings = await res.json();
|
|
||||||
let last = null;
|
|
||||||
let before = undefined;
|
|
||||||
let after = null;
|
|
||||||
for (let [sid, t] of Object.entries(task.siblings).toSorted((a,b) => a[1].name.localeCompare(b[1].name))){
|
|
||||||
if (before !== undefined) {
|
|
||||||
after = +sid;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (id == sid) before = last;
|
|
||||||
last = +sid;
|
|
||||||
}
|
|
||||||
task.before = before;
|
|
||||||
task.after = after;
|
|
||||||
} else error(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentClick(ev){
|
function parentClick(ev){
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (!task.parent_task_id) return;
|
if (!task.parent_task_id) return;
|
||||||
gotoTask(task.parent_task_id);
|
router.navigate(`/task/${task.parent_task_id}/view`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,17 +232,10 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<button class="symbol" title={t('edit')} onclick={parentRightClick}></button>
|
<button class="symbol" title={t('edit')} onclick={parentRightClick}></button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div>{t('task')}</div>
|
<div>{t('task')}</div>
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<LineEditor bind:value={task.name} editable={true} onSet={val => update({name:val})} />
|
<LineEditor bind:value={task.name} editable={true} onSet={val => update({name:val})} />
|
||||||
{#if task.before}
|
|
||||||
<button class="symbol" title={task.siblings[task.before].name} onclick={e => gotoTask(task.before)}></button>
|
|
||||||
{/if}
|
|
||||||
{#if task.after}
|
|
||||||
<button class="symbol" title={task.siblings[task.after].name} onclick={e => gotoTask(task.after)}></button>
|
|
||||||
{/if}
|
|
||||||
<button class="symbol" title={t('settings')} onclick={toggleSettings}></button>
|
<button class="symbol" title={t('settings')} onclick={toggleSettings}></button>
|
||||||
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
|
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,4 +21,4 @@ export function t(key,args = {}){
|
|||||||
}
|
}
|
||||||
for (var key of Object.keys(args)) set = set.replace(`{${key}}`,args[key]);
|
for (var key of Object.keys(args)) set = set.replace(`{${key}}`,args[key]);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,6 @@ import static de.srsoftware.umbrella.messagebus.MessageBus.messageBus;
|
|||||||
import static de.srsoftware.umbrella.messagebus.events.Event.EventType.CREATE;
|
import static de.srsoftware.umbrella.messagebus.events.Event.EventType.CREATE;
|
||||||
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
||||||
import static java.lang.Boolean.TRUE;
|
import static java.lang.Boolean.TRUE;
|
||||||
import static java.lang.System.Logger.Level.WARNING;
|
|
||||||
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
|
||||||
|
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
@@ -139,7 +138,6 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
|||||||
private void dropMember(Project project, long userId) {
|
private void dropMember(Project project, long userId) {
|
||||||
if (project.members().get(userId).permission() == OWNER) throw forbidden("You may not remove the owner of the project");
|
if (project.members().get(userId).permission() == OWNER) throw forbidden("You may not remove the owner of the project");
|
||||||
projectDb.dropMember(project.id(),userId);
|
projectDb.dropMember(project.id(),userId);
|
||||||
LOG.log(WARNING,"Removing member from project tasks not implemented"); // TODO
|
|
||||||
project.members().remove(userId);
|
project.members().remove(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,16 +223,7 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
|||||||
UmbrellaUser newMember = null;
|
UmbrellaUser newMember = null;
|
||||||
if (json.has(DROP_MEMBER) && json.get(DROP_MEMBER) instanceof Number id) dropMember(project,id.longValue());
|
if (json.has(DROP_MEMBER) && json.get(DROP_MEMBER) instanceof Number id) dropMember(project,id.longValue());
|
||||||
if (json.has(MEMBERS) && json.get(MEMBERS) instanceof JSONObject memberJson) patchMembers(project,memberJson);
|
if (json.has(MEMBERS) && json.get(MEMBERS) instanceof JSONObject memberJson) patchMembers(project,memberJson);
|
||||||
if (json.has(NEW_MEMBER) && json.get(NEW_MEMBER) instanceof Number num) {
|
if (json.has(NEW_MEMBER) && json.get(NEW_MEMBER) instanceof Number num) newMember = addMember(project,num.longValue());
|
||||||
UmbrellaUser inheritFrom = null;
|
|
||||||
if (json.has(INHERIT_FROM)){
|
|
||||||
if (!(json.get(INHERIT_FROM) instanceof Number uid)) throw invalidField(INHERIT_FROM,Text.NUMBER);
|
|
||||||
inheritFrom = userService().loadUser(uid.longValue());
|
|
||||||
if (!project.hasMember(inheritFrom)) throw notAmember(inheritFrom);
|
|
||||||
}
|
|
||||||
newMember = addMember(project,num.longValue());
|
|
||||||
if (inheritFrom != null) taskService().clonePermissions(project.id(),inheritFrom,newMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
project = projectDb.save(project.patch(json), user);
|
project = projectDb.save(project.patch(json), user);
|
||||||
messageBus().dispatch(newMember != null ? new ProjectEvent(user,project,newMember) : new ProjectEvent(user,project, old));
|
messageBus().dispatch(newMember != null ? new ProjectEvent(user,project,newMember) : new ProjectEvent(user,project, old));
|
||||||
|
|||||||
@@ -63,16 +63,10 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
rs.close();
|
rs.close();
|
||||||
if (propertyId == null || propertyId == 0) throw failedToStoreObject(t(PROPERTY));
|
if (propertyId == null || propertyId == 0) throw failedToStoreObject(t(PROPERTY));
|
||||||
insertInto(TABLE_ITEM_PROPERTIES,ITEM_ID,PROPERTY_ID,VALUE).values(itemId,propertyId,value).execute(db).close();
|
insertInto(TABLE_ITEM_PROPERTIES,ITEM_ID,PROPERTY_ID,VALUE).values(itemId,propertyId,value).execute(db).close();
|
||||||
|
db.setAutoCommit(true);
|
||||||
return new Property(propertyId,name,value,unit);
|
return new Property(propertyId,name,value,unit);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
throw failedToStoreObject(t(PROPERTY)).causedBy(e);
|
throw failedToStoreObject(t(PROPERTY)).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +126,7 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
private void createItemsTable() {
|
private void createItemsTable() {
|
||||||
try {
|
try {
|
||||||
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) NOT NULL, {3} TEXT, {4} VARCHAR(255))";
|
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) NOT NULL, {3} TEXT, {4} VARCHAR(255))";
|
||||||
sql = format(sql, TABLE_ITEMS, ID, Field.CODE, NAME, LOCATION_ID);
|
sql = format(sql, TABLE_ITEMS, ID, Field.CODE, NAME, LOCATION_ID);
|
||||||
db.prepareStatement(sql).execute();
|
db.prepareStatement(sql).execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw failedToCreateTable(TABLE_ITEMS).causedBy(e);
|
throw failedToCreateTable(TABLE_ITEMS).causedBy(e);
|
||||||
@@ -142,7 +136,7 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
private void createItemPropsTable() {
|
private void createItemPropsTable() {
|
||||||
try {
|
try {
|
||||||
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} INT NOT NULL, {2} INT NOT NULL, {3} VARCHAR(255) NOT NULL, PRIMARY KEY({1}, {2}))";
|
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} INT NOT NULL, {2} INT NOT NULL, {3} VARCHAR(255) NOT NULL, PRIMARY KEY({1}, {2}))";
|
||||||
sql = format(sql, TABLE_ITEM_PROPERTIES, ITEM_ID, PROPERTY_ID,VALUE);
|
sql = format(sql, TABLE_ITEM_PROPERTIES, ITEM_ID, PROPERTY_ID,VALUE);
|
||||||
db.prepareStatement(sql).execute();
|
db.prepareStatement(sql).execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw failedToCreateTable(TABLE_ITEM_PROPERTIES).causedBy(e);
|
throw failedToCreateTable(TABLE_ITEM_PROPERTIES).causedBy(e);
|
||||||
@@ -152,7 +146,7 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
private void createLocationsTable() {
|
private void createLocationsTable() {
|
||||||
try {
|
try {
|
||||||
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) DEFAULT NULL, {3} VARCHAR(255) NOT NULL, {4} TEXT)";
|
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255) DEFAULT NULL, {3} VARCHAR(255) NOT NULL, {4} TEXT)";
|
||||||
sql = format(sql, TABLE_LOCATIONS, ID, LOCATION_ID, NAME, DESCRIPTION);
|
sql = format(sql, TABLE_LOCATIONS, ID, LOCATION_ID, NAME, DESCRIPTION);
|
||||||
db.prepareStatement(sql).execute();
|
db.prepareStatement(sql).execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw failedToCreateTable(TABLE_LOCATIONS).causedBy(e);
|
throw failedToCreateTable(TABLE_LOCATIONS).causedBy(e);
|
||||||
@@ -162,7 +156,7 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
private void createPropertiesTable() {
|
private void createPropertiesTable() {
|
||||||
try {
|
try {
|
||||||
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT NULL, {3} INT NOT NULL, {4} VARCHAR(255))";
|
var sql = "CREATE TABLE IF NOT EXISTS {0} ( {1} INTEGER PRIMARY KEY, {2} VARCHAR(255) NOT NULL, {3} INT NOT NULL, {4} VARCHAR(255))";
|
||||||
sql = format(sql, TABLE_PROPERTIES, ID, NAME, TYPE, UNIT);
|
sql = format(sql, TABLE_PROPERTIES, ID, NAME, TYPE, UNIT);
|
||||||
db.prepareStatement(sql).execute();
|
db.prepareStatement(sql).execute();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw failedToCreateTable(TABLE_PROPERTIES).causedBy(e);
|
throw failedToCreateTable(TABLE_PROPERTIES).causedBy(e);
|
||||||
@@ -207,18 +201,18 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Long, Item> findItems(Collection<Owner> owners, Collection<String> keys, boolean fulltext) {
|
public Map<Long, Item> find(Collection<Owner> owners, Collection<String> keys, boolean fulltext) {
|
||||||
try {
|
try {
|
||||||
|
var items = new HashMap<Long,Item>();
|
||||||
var ownerCodes = owners.stream().map(Owner::dbCode).toArray();
|
var ownerCodes = owners.stream().map(Owner::dbCode).toArray();
|
||||||
var query = select(ALL).from(TABLE_ITEMS).where(OWNER, in(ownerCodes));
|
var query = select(ALL).from(TABLE_ITEMS).where(OWNER, in(ownerCodes));
|
||||||
if (fulltext) {
|
if (fulltext) {
|
||||||
query.leftJoin(ID,TABLE_ITEM_PROPERTIES,ITEM_ID);
|
query.leftJoin(ID,TABLE_ITEM_PROPERTIES,ITEM_ID);
|
||||||
for (var key : keys) query.where(format("CONCAT({0},\" \",{1},\" \",{2})", NAME, DESCRIPTION,VALUE),like("%"+key+"%"));
|
for (var key : keys) query.where(format("CONCAT({0},\" \",{1},\" \",{2})", NAME, DESCRIPTION,VALUE),like("%"+key+"%"));
|
||||||
} else {
|
} else {
|
||||||
for (var key : keys) query.where(NAME,like("%"+key+"%"));
|
for (var key : keys) query.where(NAME,like("%"+key+"%"));
|
||||||
}
|
}
|
||||||
var rs = query.exec(db);
|
var rs = query.exec(db);
|
||||||
var items = new HashMap<Long,Item>();
|
|
||||||
while (rs.next()){
|
while (rs.next()){
|
||||||
var item = Item.of(rs);
|
var item = Item.of(rs);
|
||||||
items.put(item.id(),item);
|
items.put(item.id(),item);
|
||||||
@@ -230,33 +224,10 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<Long, Location> findLocations(Collection<Owner> owners, Collection<String> keys, boolean fulltext) {
|
|
||||||
try {
|
|
||||||
var ownerCodes = owners.stream().map(Owner::dbCode).toArray();
|
|
||||||
var query = select(ALL).from(TABLE_LOCATIONS).where(OWNER, in(ownerCodes));
|
|
||||||
if (fulltext) {
|
|
||||||
for (var key : keys) query.where(format("CONCAT({0},\" \",{1})", NAME, DESCRIPTION), like("%" + key + "%"));
|
|
||||||
} else {
|
|
||||||
for (var key : keys) query.where(NAME, like("%" + key + "%"));
|
|
||||||
}
|
|
||||||
var rs = query.exec(db);
|
|
||||||
var locations = new HashMap<Long, Location>();
|
|
||||||
while (rs.next()) {
|
|
||||||
var location = DbLocation.of(rs);
|
|
||||||
locations.put(location.id(), location);
|
|
||||||
}
|
|
||||||
rs.close();
|
|
||||||
return locations;
|
|
||||||
} catch (SQLException e){
|
|
||||||
throw databaseException(FAILED_TO_LIST_ENTITIES, TYPE,t(LOCATIONS)).causedBy(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<DbLocation> listChildLocations(long parentId) {
|
public Collection<DbLocation> listChildLocations(long parentId) {
|
||||||
try {
|
try {
|
||||||
var rs = select(ALL).from(TABLE_LOCATIONS).where(PARENT_LOCATION_ID,equal(parentId)).exec(db);
|
var rs = select(ALL).from(TABLE_LOCATIONS).where(PARENT_LOCATION_ID,equal(parentId)).exec(db);
|
||||||
var list = new ArrayList<DbLocation>();
|
var list = new ArrayList<DbLocation>();
|
||||||
while (rs.next()) list.add(DbLocation.of(rs));
|
while (rs.next()) list.add(DbLocation.of(rs));
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -269,7 +240,7 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
@Override
|
@Override
|
||||||
public Collection<DbLocation> listCompanyLocations(Company company) {
|
public Collection<DbLocation> listCompanyLocations(Company company) {
|
||||||
try {
|
try {
|
||||||
var rs = select(ALL).from(TABLE_LOCATIONS).where(OWNER,equal(company.dbCode())).where(PARENT_LOCATION_ID,isNull()).exec(db);
|
var rs = select(ALL).from(TABLE_LOCATIONS).where(OWNER,equal(company.dbCode())).where(PARENT_LOCATION_ID,isNull()).exec(db);
|
||||||
var list = new ArrayList<DbLocation>();
|
var list = new ArrayList<DbLocation>();
|
||||||
while (rs.next()) list.add(DbLocation.of(rs));
|
while (rs.next()) list.add(DbLocation.of(rs));
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -630,16 +601,13 @@ public class SqliteDb extends BaseDb implements StockDb {
|
|||||||
replaceLocationsTable();
|
replaceLocationsTable();
|
||||||
replaceItemsTable();
|
replaceItemsTable();
|
||||||
replaceItemPropsTable();
|
replaceItemPropsTable();
|
||||||
|
db.setAutoCommit(true);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
try {
|
try {
|
||||||
db.rollback();
|
db.rollback();
|
||||||
} catch (SQLException ignored) {
|
} catch (SQLException ignored) {
|
||||||
}
|
}
|
||||||
throw databaseException(FAILED_TO_UPDATE_OBJECT, OBJECT, t(TABLE_WITH_NAME,NAME,TABLE_LOCATIONS)).causedBy(e);
|
throw databaseException(FAILED_TO_UPDATE_OBJECT, OBJECT, t(TABLE_WITH_NAME,NAME,TABLE_LOCATIONS)).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import java.util.Map;
|
|||||||
public interface StockDb {
|
public interface StockDb {
|
||||||
Property addNewProperty(long itemId, String name, Object value, String unit);
|
Property addNewProperty(long itemId, String name, Object value, String unit);
|
||||||
Location delete(DbLocation location);
|
Location delete(DbLocation location);
|
||||||
Map<Long, Item> findItems(Collection<Owner> owners, Collection<String> keys, boolean fulltext);
|
Map<Long, Item> find(Collection<Owner> owners, Collection<String> keys, boolean fulltext);
|
||||||
Map<Long, Location> findLocations(Collection<Owner> owners, Collection<String> keys, boolean fulltext);
|
|
||||||
Collection<DbLocation> listChildLocations(long parentId);
|
Collection<DbLocation> listChildLocations(long parentId);
|
||||||
Collection<DbLocation> listCompanyLocations(Company company);
|
Collection<DbLocation> listCompanyLocations(Company company);
|
||||||
Collection<Item> listItemsAt(Location location);
|
Collection<Item> listItemsAt(Location location);
|
||||||
|
|||||||
@@ -459,13 +459,12 @@ public class StockModule extends BaseHandler implements StockService {
|
|||||||
private boolean postSearch(UmbrellaUser user, HttpExchange ex) throws IOException {
|
private boolean postSearch(UmbrellaUser user, HttpExchange ex) throws IOException {
|
||||||
var json = json(ex);
|
var json = json(ex);
|
||||||
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingField(KEY);
|
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingField(KEY);
|
||||||
var keys = Arrays.asList(key.split(" "));
|
var keys = Arrays.asList(key.split(" "));
|
||||||
var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
|
var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
|
||||||
Set<Owner> owners = new HashSet<>(companyService().listCompaniesOf(user).values());
|
Set<Owner> owners = new HashSet<>(companyService().listCompaniesOf(user).values());
|
||||||
owners.add(user);
|
owners.add(user);
|
||||||
var items = stockDb.findItems(owners,keys,fulltext);
|
var items = stockDb.find(owners,keys,fulltext);
|
||||||
var locations = stockDb.findLocations(owners,keys,fulltext);
|
return sendContent(ex,mapValues(items));
|
||||||
return sendContent(ex,Map.of("items",mapValues(items),"locations",mapValues(locations)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
try {
|
try {
|
||||||
var query = select(ALL).from(TABLE_TASKS).leftJoin(ID,TABLE_TASKS_USERS,TASK_ID).where(USER_ID,equal(userId));
|
var query = select(ALL).from(TABLE_TASKS).leftJoin(ID,TABLE_TASKS_USERS,TASK_ID).where(USER_ID,equal(userId));
|
||||||
if (!showClosed) query.where(STATUS,lessThan(COMPLETE.code()));
|
if (!showClosed) query.where(STATUS,lessThan(COMPLETE.code()));
|
||||||
var rs = query.sort("(CASE due_date WHEN \"\" THEN '9999-99-99' ELSE IFNULL(due_date,'9999-99-99') END), status COLLATE NOCASE").limit(limit == null ? -1 : limit).skip(offset).exec(db);
|
var rs = query.sort("(CASE due_date WHEN \"\" THEN '9999-99-99' ELSE IFNULL(due_date,'9999-99-99') END), status COLLATE NOCASE").limit(limit).skip(offset).exec(db);
|
||||||
var map = new ArrayList<Task>();
|
var map = new ArrayList<Task>();
|
||||||
while (rs.next()) map.add(Task.of(rs));
|
while (rs.next()) map.add(Task.of(rs));
|
||||||
rs.close();
|
rs.close();
|
||||||
@@ -340,6 +340,7 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
var addQuery = replaceInto(TABLE_TASK_DEPENDENCIES, TASK_ID, REQUIRED_TASK_ID);
|
var addQuery = replaceInto(TABLE_TASK_DEPENDENCIES, TASK_ID, REQUIRED_TASK_ID);
|
||||||
for (var reqId : task.requiredTasksIds()) addQuery.values(task.id(), reqId);
|
for (var reqId : task.requiredTasksIds()) addQuery.values(task.id(), reqId);
|
||||||
addQuery.execute(db).close();
|
addQuery.execute(db).close();
|
||||||
|
db.setAutoCommit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
task.clean(REQUIRED_TASKS_IDS);
|
task.clean(REQUIRED_TASKS_IDS);
|
||||||
@@ -353,14 +354,7 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
}
|
}
|
||||||
return task;
|
return task;
|
||||||
} catch (SQLException e){
|
} catch (SQLException e){
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
throw failedToStoreObject(task.name()).causedBy(e);
|
throw failedToStoreObject(task.name()).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,28 +66,6 @@ public class TaskModule extends BaseHandler implements TaskService {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clonePermissions(long project_id, UmbrellaUser source, UmbrellaUser dest) {
|
|
||||||
var taskMap = taskDb.listProjectTasks(project_id,null,true);
|
|
||||||
loadMembers(taskMap.values());
|
|
||||||
for (var task : taskMap.values()) {
|
|
||||||
var memberShip = task.members().get(source.id());
|
|
||||||
|
|
||||||
Permission granted = memberShip == null ? EDIT : switch (memberShip.permission()) {
|
|
||||||
case ASSIGNEE,
|
|
||||||
EDIT,
|
|
||||||
OWNER ->
|
|
||||||
EDIT;
|
|
||||||
case READ_ONLY ->
|
|
||||||
READ_ONLY;
|
|
||||||
};
|
|
||||||
var newMember = new Member(dest, granted);
|
|
||||||
task.members().put(dest.id(), newMember);
|
|
||||||
task.dirty(MEMBERS);
|
|
||||||
taskDb.save(task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean deleteTask(HttpExchange ex, long taskId, UmbrellaUser user) throws IOException {
|
private boolean deleteTask(HttpExchange ex, long taskId, UmbrellaUser user) throws IOException {
|
||||||
var task = loadMembers(taskDb.load(taskId));
|
var task = loadMembers(taskDb.load(taskId));
|
||||||
var member = task.members().get(user.id());
|
var member = task.members().get(user.id());
|
||||||
|
|||||||
@@ -84,16 +84,10 @@ CREATE TABLE IF NOT EXISTS {0} (
|
|||||||
db.setAutoCommit(false);
|
db.setAutoCommit(false);
|
||||||
Query.delete().from(TABLE_TASK_TIMES).where(TIME_ID,equal(timeId)).execute(db);
|
Query.delete().from(TABLE_TASK_TIMES).where(TIME_ID,equal(timeId)).execute(db);
|
||||||
Query.delete().from(TABLE_TIMES).where(ID,equal(timeId)).execute(db);
|
Query.delete().from(TABLE_TIMES).where(ID,equal(timeId)).execute(db);
|
||||||
|
db.setAutoCommit(false);
|
||||||
return timeId;
|
return timeId;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
try {
|
|
||||||
db.rollback();
|
|
||||||
} catch (SQLException ignored) {}
|
|
||||||
throw failedToDropObject(t(TIME_WITH_ID, ID,timeId)).causedBy(e);
|
throw failedToDropObject(t(TIME_WITH_ID, ID,timeId)).causedBy(e);
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
db.setAutoCommit(true);
|
|
||||||
} catch (SQLException ignored){};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,7 +167,6 @@
|
|||||||
"filter": "Filter",
|
"filter": "Filter",
|
||||||
"filter by tags": "Nach Tags filtern",
|
"filter by tags": "Nach Tags filtern",
|
||||||
"first_transaction": "erste Transaktion",
|
"first_transaction": "erste Transaktion",
|
||||||
"fixed position": "fester Posten",
|
|
||||||
"footer": "Fuß-Text",
|
"footer": "Fuß-Text",
|
||||||
"foreign_id": "externe Kennung",
|
"foreign_id": "externe Kennung",
|
||||||
"forgot_pass" : "Password vergessen?",
|
"forgot_pass" : "Password vergessen?",
|
||||||
@@ -279,9 +278,7 @@
|
|||||||
|
|
||||||
"oidc_Login" : "Anmeldung mit OIDC",
|
"oidc_Login" : "Anmeldung mit OIDC",
|
||||||
"old_password": "altes Passwort",
|
"old_password": "altes Passwort",
|
||||||
"offer": "Angebot",
|
|
||||||
"option": "Option",
|
"option": "Option",
|
||||||
"optional position": "optionaler Posten",
|
|
||||||
"options": "Optionen",
|
"options": "Optionen",
|
||||||
"organization": "Organisation",
|
"organization": "Organisation",
|
||||||
"other party": "Gegenseite",
|
"other party": "Gegenseite",
|
||||||
@@ -338,7 +335,6 @@
|
|||||||
"select_customer": "Kunde auswählen",
|
"select_customer": "Kunde auswählen",
|
||||||
"select_property": "Eigenschaft auswählen",
|
"select_property": "Eigenschaft auswählen",
|
||||||
"select_state": "Status wählen",
|
"select_state": "Status wählen",
|
||||||
"select user": "Nutzer auswählen",
|
|
||||||
"send_document": "Dokument versenden",
|
"send_document": "Dokument versenden",
|
||||||
"sender": "Absender",
|
"sender": "Absender",
|
||||||
"sender_bank_account": "Bankverbindung",
|
"sender_bank_account": "Bankverbindung",
|
||||||
|
|||||||
@@ -167,7 +167,6 @@
|
|||||||
"filter": "filter",
|
"filter": "filter",
|
||||||
"filter by tags": "filter by tags",
|
"filter by tags": "filter by tags",
|
||||||
"first_transaction": "first transaction",
|
"first_transaction": "first transaction",
|
||||||
"fixed position": "fixed position",
|
|
||||||
"footer": "footer",
|
"footer": "footer",
|
||||||
"foreign_id": "external ID",
|
"foreign_id": "external ID",
|
||||||
"forgot_pass" : "forgot password?",
|
"forgot_pass" : "forgot password?",
|
||||||
@@ -279,9 +278,7 @@
|
|||||||
|
|
||||||
"oidc_Login" : "Login via OIDC",
|
"oidc_Login" : "Login via OIDC",
|
||||||
"old_password": "old password",
|
"old_password": "old password",
|
||||||
"offer": "offer",
|
|
||||||
"option": "option",
|
"option": "option",
|
||||||
"optional position": "optional position",
|
|
||||||
"options": "options",
|
"options": "options",
|
||||||
"organization": "organization",
|
"organization": "organization",
|
||||||
"other party": "other party",
|
"other party": "other party",
|
||||||
@@ -332,13 +329,12 @@
|
|||||||
"saved": "saved",
|
"saved": "saved",
|
||||||
"save_object": "save {object}",
|
"save_object": "save {object}",
|
||||||
"search": "search",
|
"search": "search",
|
||||||
"searching…": "searching…",
|
"searching…": "searhcing…",
|
||||||
"select a new parent for {entity}": "select a new parent for '{entity}'",
|
"select a new parent for {entity}": "select a new parent for '{entity}'",
|
||||||
"select_company" : "select on of you companies:",
|
"select_company" : "select on of you companies:",
|
||||||
"select_customer": "select customer",
|
"select_customer": "select customer",
|
||||||
"select_property": "select property",
|
"select_property": "select property",
|
||||||
"select_state": "select state",
|
"select_state": "select state",
|
||||||
"select user": "select user",
|
|
||||||
"send_document": "send document",
|
"send_document": "send document",
|
||||||
"sender": "sender",
|
"sender": "sender",
|
||||||
"sender_bank_account": "bank account",
|
"sender_bank_account": "bank account",
|
||||||
@@ -383,7 +379,7 @@
|
|||||||
"subtask": "subtask",
|
"subtask": "subtask",
|
||||||
"subtasks": "subtasks",
|
"subtasks": "subtasks",
|
||||||
"succeeding_document": "succeeding document",
|
"succeeding_document": "succeeding document",
|
||||||
"sum external":"sum of external positions",
|
"sum external";"sum of external positions",
|
||||||
"sum_of_records": "sum of records",
|
"sum_of_records": "sum of records",
|
||||||
"sums": "sums",
|
"sums": "sums",
|
||||||
|
|
||||||
|
|||||||
@@ -341,14 +341,6 @@ tr:hover .taglist .tag button {
|
|||||||
color: yellow;
|
color: yellow;
|
||||||
}
|
}
|
||||||
|
|
||||||
.positions tr:nth-child(2n){
|
|
||||||
border-bottom-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positions tbody tr:last-child{
|
|
||||||
border-top-color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
||||||
#app nav a{
|
#app nav a{
|
||||||
background: black;
|
background: black;
|
||||||
|
|||||||
@@ -356,10 +356,6 @@ span.timetracking {
|
|||||||
grid-column-end: span 2;
|
grid-column-end: span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown svg{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editable:hover{
|
.editable:hover{
|
||||||
border: 1px dotted;
|
border: 1px dotted;
|
||||||
}
|
}
|
||||||
@@ -390,7 +386,6 @@ span.timetracking {
|
|||||||
|
|
||||||
table{
|
table{
|
||||||
min-width: 30vw;
|
min-width: 30vw;
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.start_end{
|
.start_end{
|
||||||
@@ -486,14 +481,6 @@ select.autocomplete{
|
|||||||
width: calc(100% - 50px);
|
width: calc(100% - 50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.positions tr:nth-child(2n){
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positions tbody tr:last-child{
|
|
||||||
border-top: 3px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 900px) {
|
@media screen and (min-width: 900px) {
|
||||||
#app nav > button.symbol{
|
#app nav > button.symbol{
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -26,11 +26,6 @@ input{
|
|||||||
color: orange;
|
color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:disabled{
|
|
||||||
border: 1px solid gray;
|
|
||||||
background: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend.date,
|
legend.date,
|
||||||
legend.time{
|
legend.time{
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@@ -337,14 +332,6 @@ code,
|
|||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.positions tr:nth-child(2n){
|
|
||||||
border-bottom-color: gold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positions tbody tr:last-child{
|
|
||||||
border-top-color: gold;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
||||||
#app nav a{
|
#app nav a{
|
||||||
background: black;
|
background: black;
|
||||||
|
|||||||
@@ -450,10 +450,6 @@ span.timetracking {
|
|||||||
grid-column-end: span 2;
|
grid-column-end: span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown svg{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editable:hover{
|
.editable:hover{
|
||||||
border: 1px dotted;
|
border: 1px dotted;
|
||||||
}
|
}
|
||||||
@@ -484,7 +480,6 @@ span.timetracking {
|
|||||||
|
|
||||||
table{
|
table{
|
||||||
min-width: 30vw;
|
min-width: 30vw;
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.start_end{
|
.start_end{
|
||||||
@@ -590,14 +585,6 @@ select.autocomplete{
|
|||||||
width: calc(100% - 50px);
|
width: calc(100% - 50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.positions tr:nth-child(2n){
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positions tbody tr:last-child{
|
|
||||||
border-top: 3px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 900px) {
|
@media screen and (min-width: 900px) {
|
||||||
#app nav > button.symbol{
|
#app nav > button.symbol{
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
@@ -288,15 +288,6 @@ tr:hover .taglist .tag button {
|
|||||||
background: cyan;
|
background: cyan;
|
||||||
}
|
}
|
||||||
|
|
||||||
.positions tr:nth-child(2n){
|
|
||||||
border-bottom-color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positions tbody tr:last-child{
|
|
||||||
border-top-color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media screen and (max-width: 900px) {
|
@media screen and (max-width: 900px) {
|
||||||
#app nav a{
|
#app nav a{
|
||||||
background: white;
|
background: white;
|
||||||
|
|||||||
@@ -449,10 +449,6 @@ span.timetracking {
|
|||||||
grid-column-end: span 2;
|
grid-column-end: span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.markdown svg{
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editable:hover{
|
.editable:hover{
|
||||||
border: 1px dotted;
|
border: 1px dotted;
|
||||||
}
|
}
|
||||||
@@ -483,7 +479,6 @@ span.timetracking {
|
|||||||
|
|
||||||
table{
|
table{
|
||||||
min-width: 30vw;
|
min-width: 30vw;
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.start_end{
|
.start_end{
|
||||||
@@ -579,14 +574,6 @@ select.autocomplete{
|
|||||||
width: calc(100% - 50px);
|
width: calc(100% - 50px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.positions tr:nth-child(2n){
|
|
||||||
border-bottom: 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.positions tbody tr:last-child{
|
|
||||||
border-top: 3px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 900px) {
|
@media screen and (min-width: 900px) {
|
||||||
#app nav > button.symbol{
|
#app nav > button.symbol{
|
||||||
display: none;
|
display: none;
|
||||||
|
|||||||
Reference in New Issue
Block a user