Merge branch 'feature/global_error_display' into module/files

This commit is contained in:
2025-09-28 12:13:32 +02:00
42 changed files with 274 additions and 325 deletions

View File

@@ -1,8 +1,9 @@
<script>
import { Router, Route } from 'svelte-tiny-router';
import { loadTranslation } from './translations.svelte.js';
import { checkUser, user } from './user.svelte.js';
import { messages } from './warn.svelte';
import { loadTranslation } from './translations.svelte';
import { checkUser, user } from './user.svelte';
import AddDoc from "./routes/document/Add.svelte";
import AddTask from "./routes/task/Add.svelte";
@@ -70,6 +71,12 @@
<Router beforeEach={[testGuard]}>
<!-- https://github.com/notnotsamuel/svelte-tiny-router -->
<Menu />
{#if messages.error}
<span class="error">{@html messages.error}</span>
{/if}
{#if messages.warning}
<span class="error">{@html messages.warning}</span>
{/if}
<Route path="/" component={User} />
<Route path="/bookmark" component={Bookmarks} />
<Route path="/bookmark/:id/view" component={Bookmark} />

View File

@@ -2,22 +2,21 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../urls.svelte.js';
import { checkUser, tryLogin } from '../user.svelte.js';
import { t } from '../translations.svelte.js';
import { api } from '../urls.svelte';
import { error, yikes } from '../warn.svelte';
import { checkUser, tryLogin } from '../user.svelte';
import { t } from '../translations.svelte';
let credentials = $state({ username : null, password : null });
const router = useTinyRouter();
let services = $state([]);
let error = $state(null);
async function doLogin(ev){
ev.preventDefault();
const json = await tryLogin(credentials);
if (json) {
json.release_time = json.release_time.replace('T',' ');
error = t('failed_login_attempts',json);
error(t('failed_login_attempts',json));
}
}
@@ -43,13 +42,14 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
const json = await resp.json();
yikes();
if (json.authorization_endpoint) {
const endpoint = json.authorization_endpoint;
delete json.authorization_endpoint;
location.href = endpoint + '?' + new URLSearchParams(json);
}
} else {
error = await resp.text();
error(resp);
if (btn) btn.disabled = false;
}
}
@@ -85,9 +85,6 @@
<form onsubmit={doLogin}>
<fieldset>
{#if error}
<span class="error">{error}</span>
{/if}
<legend>{t('login')}</legend>
<label>
<input type="text" bind:value={credentials.username} required use:init />

View File

@@ -1,8 +1,8 @@
<script>
import { onMount } from 'svelte';
import { api } from '../urls.svelte.js';
import { t } from '../translations.svelte.js';
import { api } from '../urls.svelte';
import { t } from '../translations.svelte';
import Autocomplete from './Autocomplete.svelte';
import PermissionSelector from './PermissionSelector.svelte';
@@ -15,7 +15,6 @@
updatePermission = (uid,perm) => console.log(`no handler for updatePermission(${uid}, ${perm})`)
} = $props();
let error = $state(null);
let permissions = $state(null);
let sortedMembers = $derived.by(() => Object.values(members).sort((a, b) => a.user.name.localeCompare(b.user.name)));
@@ -36,9 +35,6 @@
onMount(loadPermissions);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<table>
<tbody>
{#each sortedMembers as member,i}

View File

@@ -5,7 +5,6 @@
import Autocomplete from './Autocomplete.svelte';
let error = $state(null);
let {
getCandidates = async text => {},
heading = t('add_object',{object:t('user')}),
@@ -25,9 +24,6 @@
let sortedUsers = $derived.by(() => Object.values(users).sort((a, b) => a.name.localeCompare(b.name)));
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<table>
<tbody>
{#each sortedUsers as usr,idx}

View File

@@ -1,8 +1,9 @@
<script>
import { onMount } from 'svelte';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import Editor from '../../Components/MarkdownEditor.svelte';
import Users from '../../Components/UserSelector.svelte';
@@ -24,7 +25,6 @@
url:null,
users:{}
});
let error = $state(null);
async function getCandidates(text){
const url = api('user/search');
@@ -34,13 +34,13 @@
body : text
});
if (resp.ok){
error = null;
yikes();
const input = await resp.json();
return Object.fromEntries(
Object.entries(input).map(([key, value]) => [key, value.name])
);
} else {
error = await resp.text();
error(resp);
return {};
}
}
@@ -55,10 +55,10 @@
bookmarks = Object.values(merged).sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
loader.offset += loader.limit;
loader.active = false;
error = null;
yikes();
if (Object.keys(raw).length) onscroll(null); // when bookmarks were received, check whether they fill up the page
} else {
error = await resp.html();
error(resp)
}
}
@@ -81,7 +81,7 @@
const bookmark = await resp.json();
bookmarks.unshift(bookmark);
} else {
error = await resp.text();
error(resp);
}
}
@@ -102,9 +102,6 @@
<svelte:window {onscroll} />
<fieldset>
<legend>{t('Bookmarks')}</legend>
{#if error}
<span class="error">{error}</span>
{/if}
<label>
{t('URL')}
<input bind:value={new_bookmark.url} autofocus />

View File

@@ -3,14 +3,14 @@
import Bookmark from './Template.svelte';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import Editor from '../../Components/MarkdownEditor.svelte';
import Editor from '../../Components/MarkdownEditor.svelte';
import Template from './Template.svelte';
let bookmark = $state(null);
let error = $state(null);
let { id } = $props();
async function load(){
@@ -18,15 +18,13 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
bookmark = await resp.json();
yikes();
} else {
error = await resp.text();
error(resp.text);
}
}
onMount(load);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<Template {bookmark} />

View File

@@ -1,13 +1,14 @@
<script>
import {api} from '../../urls.svelte.js'
import {t} from '../../translations.svelte.js';
import {api} from '../../urls.svelte'
import { error, yikes } from '../../warn.svelte';
import {t} from '../../translations.svelte';
import LineEditor from '../../Components/LineEditor.svelte';
import Multiline from '../../Components/MultilineEditor.svelte';
import Users from '../../Components/UserSelector.svelte'
let caption = $state(t('save_object',{object:t('users')}));
let { company } = $props();
let error = $state(null);
let btnEnabled = $state(true);
let memberCopy = $state(JSON.parse(JSON.stringify(company.members)));
@@ -19,13 +20,13 @@
body : text
});
if (resp.ok){
error = null;
yikes();
const input = await resp.json();
return Object.fromEntries(
Object.entries(input).map(([key, value]) => [key, value.name])
);
} else {
error = await resp.text();
error(resp);
return {};
}
}
@@ -42,7 +43,7 @@
for (let key of Object.keys(patched)) company[key] = patched[key];
return true;
}
error = await resp.text();
error(resp);
return false;
}

View File

@@ -1,13 +1,13 @@
<script>
import {onMount} from 'svelte';
import {api} from '../../urls.svelte.js';
import {t} from '../../translations.svelte.js';
import {api} from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import {t} from '../../translations.svelte';
import Editor from './Editor.svelte';
import LineEditor from '../../Components/LineEditor.svelte';
import Notes from '../notes/RelatedNotes.svelte';
let error = $state(null);
let companies = $state(null);
let new_company = $state({name:null,email:null});
let selected = $state(0);
@@ -23,9 +23,9 @@
if (resp.ok){
const company = await resp.json();
companies[company.id] = company;
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -38,9 +38,9 @@
});
if (resp.ok){
delete companies[company.id];
error = null;
yikes();
} else {
error = await resp.text();
error(await resp.text());
}
return false;
}
@@ -50,9 +50,9 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
companies = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -71,9 +71,6 @@
<legend>
{t('companies')}
</legend>
{#if error}
<span class="error">{error}</span>
{/if}
{#if companies}
<table class="companies">
<thead>

View File

@@ -2,14 +2,14 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import ContactSelector from '../../Components/ContactSelector.svelte';
let company = $state(null);
let docType = $state(null);
let error = $state(null);
let router = useTinyRouter();
let document = $state({
@@ -36,7 +36,7 @@
if (company.bank_account) document.sender.bank_account = company.bank_account;
if (company.court) document.sender.court = company.court;
} else {
error = await resp.text();
error(resp);
}
}
@@ -47,11 +47,12 @@
const types = await resp.json();
docType = t('type_'+types[document.type]);
} else {
error = await resp.text();
error(resp);
}
}
function load(){
yikes();
loadCompanies();
loadDocType();
}
@@ -83,7 +84,7 @@
const json = await resp.json();
router.navigate(`/document/${json.id}/view`);
} else {
error = await resp.text();
error(resp);
}
}
@@ -95,9 +96,6 @@
</style>
<fieldset>
{#if error}
<span class="error">{error}</span>
{/if}
{#if docType}
<legend>{t('add_object',{object:docType})}</legend>
{/if}

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import EstimatedTask from '../../Components/EstimatedTask.svelte';
@@ -11,7 +12,6 @@
onSelect = (task) => {}
} = $props();
let error = $state(null);
let projects = $state(null);
async function loadItems(){
@@ -24,8 +24,9 @@
});
if (resp.ok){
projects = await resp.json();
yikes();
} else {
error = await resp.body();
error(resp);
}
}
@@ -43,9 +44,6 @@
<div>
<h1>{t('estimated_times')}</h1>
{#if error}
<span class="error">{error}</span>
{/if}
{#if projects}
{#each projects as project,pid}
<fieldset>

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import Item from '../../Components/Item.svelte';
@@ -12,8 +13,6 @@
} = $props();
let items = $state(null);
let error = $state(null);
async function loadItems(){
const url = api('items/list');
@@ -25,8 +24,9 @@
});
if (resp.ok){
items = await resp.json();
yikes();
} else {
error = await resp.body();
error(resp);
}
}
@@ -36,9 +36,6 @@
<div>
<h1>{t('items')}</h1>
{#if error}
<span class="error">{error}</span>
{/if}
{#if items}
{#each items as item,id}
<Item item={item} onclick={() => onSelect(item)} />

View File

@@ -3,11 +3,11 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import TypeSelector from './TypeSelector.svelte';
let error = null;
let companies = {};
let router = useTinyRouter();
let company_id = +router.query.company_id;
@@ -28,7 +28,7 @@
if (resp.ok){
load(selected_company); // relaod docs
} else {
error = await resp.text();
error(resp);
}
}
}
@@ -45,7 +45,7 @@
if (resp.ok){
documents = await resp.json();
} else {
error = await resp.text();
error(resp);
}
}
@@ -54,8 +54,9 @@
const resp = await fetch(url,{ credentials: 'include'});
if (resp.ok){
companies = await resp.json();
yikes();
} else {
error = await resp.text();
error(resp);
}
if (company_id) {
for (let comp of Object.values(companies)){

View File

@@ -3,13 +3,13 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import Position from './Position.svelte';
var {
document = $bindable(null),
error = $bindable(null),
submit = (key,newVal) => {},
} = $props();
@@ -18,7 +18,6 @@
async function updatePositions(resp){
let json = await resp.json();
document.positions = {};
error = null;
setTimeout(() => document.positions = json,100)
}
@@ -32,7 +31,7 @@
if (resp.ok){
updatePositions(resp);
} else {
error = await resp.text();
error(resp);
}
}
@@ -48,7 +47,7 @@
if (resp.ok){
updatePositions(resp);
} else {
error = await resp.text();
error(resp);
}
}
</script>

View File

@@ -3,12 +3,12 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
let content = $state(null);
let doc = $state(null);
let email = $state(null);
let error = $state(null);
const router = useTinyRouter();
let subject = $state(null);
let { id } = $props();
@@ -20,9 +20,9 @@
doc = await resp.json();
email = doc.customer.email;
subject = t('new_document_from',{type:t(doc.type),sender:doc.company.name,number:doc.number}),
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
return;
}
url = api(`document/${id}/settings`);
@@ -31,7 +31,7 @@
const settings = await resp.json();
content = settings.content;
} else {
error = await resp.text();
error(resp);
}
}
@@ -46,17 +46,13 @@
if (resp.ok){
router.navigate(`/document?company_id=${doc.company.id}`);
} else {
error = await resp.text();
error(resp);
}
}
onMount(loadDoc);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<fieldset>
<legend>{t('customer_email')} / {t('subject')}</legend>
<input type="text" bind:value={email} />

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { api, target } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
let {
@@ -9,7 +10,6 @@
onSelect = (time) => {}
} = $props();
let error = $state(null);
let projects = $state(null);
let times = $state(null);
@@ -23,8 +23,9 @@
});
if (resp.ok){
projects = await resp.json();
yikes();
} else {
error = await resp.body();
error(resp);
}
}
@@ -39,7 +40,7 @@
if (resp.ok){
times = await resp.json();
} else {
error = await resp.body();
error(resp);
}
}

View File

@@ -3,6 +3,7 @@
import { useTinyRouter } from 'svelte-tiny-router';
import {api} from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
@@ -20,7 +21,6 @@
let doc = $state(null);
let editable = $derived(doc.state == 1);
let error = $state(null);
let { id } = $props();
let pdfDisabled = $state(false);
let position_select = $state(false);
@@ -36,9 +36,9 @@
});
if (resp.ok){
doc.positions = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -64,12 +64,12 @@
body : type
});
if (res.ok) {
error = null;
yikes();
let json = await res.json();
router.navigate(`/document/${json.id}/view`);
loadDoc();
} else {
error = await res.text();
error(res);
}
}
@@ -78,9 +78,9 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
doc = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -90,7 +90,7 @@
const url = api(`document/${doc.id}/pdf`);
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
error = null;
yikes();
const blob = await resp.blob();
const parts = resp.headers.get("Content-disposition").split(";");
const filename = parts[1].split('=')[1].split('"').join('');
@@ -99,7 +99,7 @@
fileLink.download = filename;
fileLink.click();
} else {
error = await resp.text();
error(resp);
}
pdfDisabled = false;
}
@@ -133,10 +133,6 @@
<title>Umbrella {t('document')} {doc?.number}</title>
</svelte:head>
{#if error}
<span class="error">{error}</span>
{/if}
{#if doc}
<fieldset class="customer">
<legend>{t('customer')}</legend>
@@ -248,7 +244,7 @@
<button onclick={() => position_select = true}>{t('add_object',{object:t('position')})}</button>
{/if}
</legend>
<PositionList bind:document={doc} submit={update} bind:error={error} />
<PositionList bind:document={doc} submit={update} />
</fieldset>
<fieldset>
<legend>{t('footer')}</legend>

View File

@@ -3,13 +3,13 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
import List from './List.svelte';
let authors = $state({});
let error = $state(null);
let loader = {
offset : 0,
limit : 20,
@@ -22,9 +22,6 @@
entity_id = null
} = $props();
async function loadNotes(){
const url = api(`notes?offset=${loader.offset}&limit=${loader.limit}`);
@@ -36,11 +33,11 @@
authors = {...authors, ...data.authors};
loader.offset += loader.limit;
loader.active = false;
error = null;
yikes();
if (Object.keys(data.notes).length) onscroll(null); // when notes were received, check whether they fill up the page
} else {
error = await resp.text();
error(resp);
}
}
@@ -56,10 +53,10 @@
authors[user.id] = user;
notes[newNote.id] = newNote;
note = {source:'',rendered:''};
error = null;
yikes();
return true;
} else {
error = await resp.text();
error(resp);
return false;
}
}
@@ -81,7 +78,4 @@
</svelte:head>
<svelte:window {onscroll} />
{#if error}
<span class="error">{error}</span>
{/if}
<List {notes} />

View File

@@ -2,13 +2,13 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
import Editor from '../../Components/MarkdownEditor.svelte';
let { authors, module, notes = $bindable() } = $props();
let error = $state(null);
const router = useTinyRouter();
async function drop(nid){
@@ -19,10 +19,10 @@
method : 'DELETE'
});
if (resp.ok) {
error = false;
yikes();
notes = notes.filter((itm,idx) => itm.id != nid);
} else {
error = await resp.text();
error(resp);
}
}
@@ -46,10 +46,10 @@
body : src
});
if (resp.ok) {
error = false;
yikes();
return true;
} else {
error = await resp.text();
error(resp);
return false;
}
}

View File

@@ -3,6 +3,7 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
@@ -10,7 +11,6 @@
import List from './List.svelte';
let authors = $state(null);
let error = $state(null);
let note = $state({source:null,rendered:null});
let notes = $state(null);
const router = useTinyRouter();
@@ -27,10 +27,10 @@
method : 'DELETE'
});
if (resp.ok) {
error = false;
yikes();
delete notes[nid];
} else {
error = await resp.text();
error(resp);
}
}
@@ -46,7 +46,7 @@
notes = Object.values(data.notes).sort((a, b) => a.id - b.id);
authors = data.authors;
} else {
error = await resp.text();
error(resp);
}
}
@@ -61,12 +61,12 @@
let newNote = await resp.json();
authors[user.id] = user;
notes.push(newNote);
error = null;
yikes();
note.source = null;
note.rendered = null;
return true;
} else {
error = await resp.text();
error(resp);
return false;
}
}
@@ -79,10 +79,10 @@
body : src
});
if (resp.ok) {
error = false;
yikes();
return true;
} else {
error = await resp.text();
error(resp);
return true;
}
}
@@ -90,9 +90,6 @@
onMount(load);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<List {authors} {module} {notes} />
<div class="editor">
<Editor simple={true} bind:value={note} onSet={saveNote} />

View File

@@ -2,13 +2,13 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import CompanySelector from '../../Components/CompanySelector.svelte';
import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
import Tags from '../tags/TagList.svelte';
let error = $state(null);
let ready = $derived(!!project.name.trim())
const router = useTinyRouter();
let showSettings = $state(false);
@@ -32,7 +32,7 @@
var newProject = await resp.json();
router.navigate(`/project/${newProject.id}/view`);
} else {
error = await resp.text();
error(resp);
}
}
@@ -50,9 +50,6 @@
label{ display: block }
</style>
{#if error}
<span class="error">{error}</span>
{/if}
<form {onsubmit}>
<fieldset>
<legend>

View File

@@ -3,6 +3,7 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
@@ -14,7 +15,6 @@
let router = useTinyRouter();
if (router.hasQueryParam('filter')) filter_input = router.getQueryParam('filter');
let dragged = null;
let error = $state(null);
let highlight = $state({});
let filter = $derived(filter_input.toLowerCase());
let project = $state(null);
@@ -53,9 +53,9 @@
if (!tasks[user_id]) tasks[user_id] = {};
if (!tasks[user_id][state]) tasks[user_id][state] = {};
tasks[user_id][state][task.id] = task;
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -81,9 +81,9 @@
tasks[user_id][state][task.id] = task;
task.assignee = user_id;
task.status = state;
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -104,9 +104,9 @@
users[uid] = member.user.name;
if (!tasks[uid]) tasks[uid] = {};
}
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -140,7 +140,7 @@
tasks[assignee][state][task_id] = task;
}
} else {
error = await resp.text();
error(resp);
}
}
@@ -164,9 +164,6 @@
{#if project}
<h1 onclick={ev => router.navigate(`/project/${project.id}/view`)}>{project.name}</h1>
{/if}
{#if error}
<span class="error">{error}</span>
{/if}
{#if project}
<div class="kanban" style="display: grid; grid-template-columns: {`repeat(${columns}, auto)`}">

View File

@@ -3,10 +3,10 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
const router = useTinyRouter();
let error = $state(null);
let projects = $state(null);
let companies = $state(null);
let showClosed = $state(router.query.closed == "show");
@@ -26,11 +26,12 @@
});
if (resp.ok){
projects = await resp.json();
yikes();
} else {
error = await resp.text();
error(resp);
}
} else {
error = await resp.text();
error(resp);
}
}
@@ -44,8 +45,9 @@
if (resp.ok){
var prj = await resp.json();
projects[prj.id].status = prj.status;
yikes();
} else {
error = await resp.text();
error(await resp.text());
}
}
@@ -69,9 +71,6 @@
<title>Umbrella {t('Projects')}</title>
</svelte:head>
{#if error}
<span class="error">{error}</span>
{/if}
<fieldset>
<legend>
{t('projects')}

View File

@@ -2,8 +2,9 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import LineEditor from '../../Components/LineEditor.svelte';
import MarkdownEditor from '../../Components/MarkdownEditor.svelte';
@@ -14,7 +15,6 @@
import TaskList from '../task/TaskList.svelte';
let { id } = $props();
let error = $state(null);
let estimated_time = $state({sum:0});
let project = $state(null);
let router = useTinyRouter();
@@ -41,9 +41,9 @@
if (resp.ok){
const json = await resp.json();
project.allowed_states[json.code] = json.name;
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -89,11 +89,10 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
project = await resp.json();
// console.log(project);
error = null;
yikes();
loadTasks();
} else {
error = await resp.text();
error(resp);
}
}
@@ -112,9 +111,9 @@
tasks = {};
estimated_time.sum = 0;
tasks = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -135,11 +134,11 @@
body : JSON.stringify(data)
});
if (resp.ok){
error = null;
yikes();
project = await resp.json();
return true;
} else {
error = await resp.text();
error(resp);
return false;
}
}
@@ -162,10 +161,6 @@
<title>Umbrella {project?.name}</title>
</svelte:head>
{#if error}
<span class="error">{error}</span>
{/if}
{#if project}
<div class="project grid2">
<div>{t('project')}</div>

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api, target } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { display } from '../../time.svelte';
@@ -11,7 +12,6 @@
let bookmarks = $state(null);
let companies = $state(null);
let documents = $state(null);
let error = $state(null);
let fulltext = false;
let key = $state(router.getQueryParam('key'));
let input = $state(router.getQueryParam('key'));
@@ -67,7 +67,7 @@
const res = await resp.json();
bookmarks = Object.keys(res).length ? res : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -76,7 +76,7 @@
const json = await resp.json();
companies = Object.keys(json).length ? json : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -85,7 +85,7 @@
const json = await resp.json();
documents = Object.keys(json).length ? json : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -94,7 +94,7 @@
const json = await resp.json();
notes = Object.keys(json).length ? json : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -103,7 +103,7 @@
const res = await resp.json();
projects = Object.keys(res).length ? res : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -112,7 +112,7 @@
const res = await resp.json();
tasks = Object.keys(res).length ? res : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -121,7 +121,7 @@
const res = await resp.json();
times = Object.keys(res).length ? res : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -130,7 +130,7 @@
const res = await resp.json();
pages = Object.keys(res).length ? res : null;
} else {
error = await resp.text();
error(resp);
}
}
@@ -144,9 +144,6 @@
<fieldset class="search">
<legend>{t('search')}</legend>
{#if error}
<span class="error">{error}</span>
{/if}
<form onsubmit={setKey}>
<label>
{t('key')}

View File

@@ -3,10 +3,10 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api, target } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
let { module, id } = $props();
let error = $state(null);
let object = $state(null);
let router = useTinyRouter();
let task = $state(null);
@@ -21,7 +21,7 @@
if (resp.ok) {
object = await resp.json();
} else {
error = await resp.text();
error(resp);
}
}
@@ -32,9 +32,6 @@
onMount(load);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
{#if object}
{#if module=='bookmark'}
{@html target(object.comment.rendered).replace(/<p>(.*?)<\/p>/, '$1')} <!-- this is a workaround for in-proper formatting of paragraphs within list items in firefox -->

View File

@@ -3,6 +3,7 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js'
@@ -12,7 +13,6 @@
tags = $bindable([]),
user_list = [],
} = $props();
let error = $state(null);
let newTag = $state('');
let router = useTinyRouter();
@@ -35,10 +35,10 @@
newTag = await resp.text();
tags.push(newTag);
tags = tags.sort();
error = null;
yikes();
newTag = '';
} else {
error = await resp.text();
error(resp);
}
return false;
}
@@ -58,7 +58,7 @@
tag = await resp.text();
tags = tags.filter(item => item !== tag);
} else {
error = await resp.text();
error(resp);
}
return false;
}
@@ -71,7 +71,7 @@
tags = await resp.json();
tags = tags.sort();
} else {
error = await resp.text();
error(resp);
}
}
@@ -86,10 +86,6 @@
$effect(() => loadTags(id));
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<div class="taglist">
{#each tags as tag,idx}
<span class="tag">

View File

@@ -2,12 +2,12 @@
import {onMount} from 'svelte';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import Reference from './Reference.svelte';
let { tag } = $props();
let error = $state(null);
let uses = $state(null);
async function loadUses(){
@@ -15,9 +15,9 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
uses = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -26,9 +26,6 @@
</script>
<fieldset>
<legend>{t('tag_uses',{tag:tag})}</legend>
{#if error}
<span class="error">{error}</span>
{/if}
{#if uses}
{#each Object.entries(uses) as [module,ids]}
<h2>{t(module.endsWith('s') ? module : `${module}s`)}</h2>

View File

@@ -3,6 +3,7 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
@@ -11,7 +12,6 @@
import Tags from '../tags/TagList.svelte';
let { project_id = null, parent_task_id } = $props();
let error = $state(null);
let project = $state(null);
let extendedSettings = $state(false);
let parent_task = $state(null);
@@ -54,10 +54,10 @@
parent_task = await resp.json();
task.parent_task_id = +parent_task_id;
project_id = parent_task.project_id;
error = null;
yikes();
project = null; // TODO
} else {
error = await resp.text();
error(resp);
}
}
@@ -67,9 +67,9 @@
if (resp.ok){
project = await resp.json();
task.project_id = +project_id;
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -111,9 +111,9 @@
if (task.parent_task_id){
router.navigate(`/task/${task.parent_task_id}/view`);
} else router.navigate(`/task/${task.id}/view`);
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -130,9 +130,6 @@
<fieldset>
<legend>{t('add_object',{object:t('task')})}</legend>
{#if error}
<span class="error">{error}</span>
{/if}
<table {onkeydown}>
<tbody>
<tr>

View File

@@ -3,9 +3,9 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
let error = $state(null);
let projects = $state({});
let router = useTinyRouter();
let tasks = $state(null);
@@ -27,7 +27,7 @@
if (resp.ok){
tasks[tid] = await resp.json();
} else {
error = await resp.text();
error(resp);
}
}
@@ -53,7 +53,7 @@
if (resp.ok){
projects[pid] = await resp.json();
} else {
error = await resp.text();
error(resp);
}
}
@@ -77,7 +77,7 @@
load();
}
} else {
error = await resp.text();
error(resp);
}
}
@@ -102,9 +102,6 @@
<fieldset>
<legend>{loading ? t('loading_object',{object:t('task_list')}) : t('task_list')}</legend>
{#if error}
<soan class="error">{error}</soan>
{/if}
{#if tasks}
<table>
<thead>

View File

@@ -4,6 +4,7 @@
import { dragged } from './dragndrop.svelte';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import { timetrack } from '../../user.svelte';
import { now } from '../../time.svelte';
@@ -20,7 +21,6 @@
} = $props();
let children = $state(null);
let deleted = $state(false);
let error = $state(null);
const router = useTinyRouter();
let start = 0;
@@ -39,7 +39,7 @@
const track = await resp.json();
timetrack.running = track;
} else {
error = await resp.text();
error(resp);
}
}
@@ -53,7 +53,7 @@
if (resp.ok){
deleted = true;
} else {
error = await resp.text();
error(resp);
}
}
}
@@ -78,7 +78,7 @@
children[dragged.element.id]=dragged.element;
if (dragged.siblings[dragged.element.id]) delete dragged.siblings[dragged.element.id];
} else {
error = await resp.text();
error(resp);
}
return false;
}
@@ -97,9 +97,9 @@
});
if (resp.ok){
children = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -121,7 +121,7 @@
task = await resp.json();
return true;
} else {
error = await resp.text();
error(resp);
return false;
}
}
@@ -168,9 +168,6 @@
<button class="symbol" title={t('delete_object',{object:t('subtask')})} onclick={deleteTask} ></button>
<button class="symbol" title={t('add_object',{object:t('subtask')})} onclick={addSubtask}></button>
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
{#if error}
<span class="error">{error}</span>
{/if}
{#if children}
<TaskList {states} tasks={children} {estimated_time} {show_closed} />
{/if}

View File

@@ -3,12 +3,12 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let { task = null } = $props();
let candidates = $state({});
let error = $state(null);
let key = $state(null);
let requiredTasks = $state({});
let router = useTinyRouter();
@@ -19,7 +19,7 @@
let url = api(`task/${new_task_id}`);
let resp = await fetch(url,{ credentials : 'include' });
if (resp.ok){
error = null;
yikes();
let newTask = await resp.json();
if (newTask.project_id != task.project_id){
alert('project mismatch!');
@@ -30,7 +30,7 @@
await patch();
delete candidates[new_task_id];
} else {
error = await resp.text();
error(resp);
}
}
@@ -47,12 +47,12 @@
body : JSON.stringify(data)
});
if (res.ok) {
error = null;
yikes();
candidates = await res.json();
for (var taskId of Object.keys(requiredTasks)) delete candidates[taskId];
delete candidates[task.id];
} else {
error = await res.text();
error(resp);
}
}
@@ -65,10 +65,10 @@
body : JSON.stringify({ids:task.required_tasks_ids})
});
if (res.ok){
error = null;
yikes();
requiredTasks = await res.json();
} else {
error = await res.text();
error(resp);
}
}
@@ -91,9 +91,7 @@
method : 'PATCH',
body : JSON.stringify({required_tasks_ids:task.required_tasks_ids})
});
if (!resp.ok){
error = await resp.text();
}
if (!resp.ok) error(resp);
}
async function unlink(required_task){

View File

@@ -2,8 +2,9 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import { timetrack } from '../../user.svelte.js';
import { now } from '../../time.svelte';
@@ -19,7 +20,6 @@
let { id } = $props();
let children = $state(null);
let dummy = $derived(updateOn(id));
let error = $state(null);
let estimated_time = $state({sum:0});
let project = $state(null);
const router = useTinyRouter();
@@ -47,8 +47,9 @@
if (resp.ok) {
const track = await resp.json();
timetrack.running = track;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -87,9 +88,9 @@
});
if (resp.ok){
children = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(resp);
}
}
@@ -99,7 +100,7 @@
if (resp.ok){
task.parent = await resp.json();
} else {
error = await resp.text();
error(resp);
}
}
@@ -108,14 +109,14 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
task = await resp.json();
error = null;
yikes();
project = null;
children = null;
loadChildren();
if (task.project_id) loadProject();
if (task.parent_task_id) loadParent();
} else {
error = await resp.text();
error(resp);
}
}
@@ -124,9 +125,9 @@
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
project = await resp.json();
error = null;
yikes();
} else {
error = await resp.text();
error(await resp.text());
}
}
@@ -142,13 +143,13 @@
body : JSON.stringify(data)
});
if (resp.ok){
error = null;
yikes();
let old_task = task;
task = await resp.json();
if (task.parent_id == old_task.parent_id) task.parent = old_task.parent;
return true;
} else {
error = await resp.text();
error(resp);
return false;
}
}
@@ -177,9 +178,6 @@
<title>Umbrella {t('task')}: {task?.name}</title>
</svelte:head>
{#if error}
<span class="error">{error}</span>
{/if}
{#if task}
<div class={`task grid2 prio_${task.total_prio} p${Math.floor(task.total_prio/10)*10} p${task.total_prio % 10}`} >
{#if project}

View File

@@ -1,14 +1,16 @@
<script>
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { display } from '../../time.svelte.js';
import { api } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
import { timetrack } from '../../user.svelte.js';
import { display } from '../../time.svelte.js';
import { now } from '../../time.svelte';
import TimeEditor from '../../Components/TimeRecordEditor.svelte';
let docLinks = $state(null);
let error = $state(null);
let router = useTinyRouter();
let times = $state(null);
let tasks = {};
@@ -25,6 +27,21 @@
let timeMap = $derived.by(calcYearMap);
let selectionSum = $derived(sortedTimes.filter(time => selected[time.id]).map(time => time.duration).reduce((acc, a) => acc + a, 0));
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;
} else {
error(resp);
}
}
function calcYearMap(){
let result = {
months : {},
@@ -70,13 +87,13 @@
body : `${id1}+${id2}`
});
if (res.ok){
error = null;
yikes();
let json = await res.json();
delete times[id1];
delete times[id2];
times[json.id] = json;
} else {
error = await res.text();
error(res);
}
return false;
}
@@ -91,7 +108,7 @@
projects = json.projects;
docLinks = json.documents;
} else {
error = await resp.text();
error(resp);
}
}
@@ -115,9 +132,9 @@
});
if (res.ok){
delete times[time_id];
error = false;
yikes();
} else {
error = await res.text();
error(res);
}
}
@@ -161,10 +178,10 @@
let id = json.id;
for (let key of Object.keys(json)) times[id][key] = json[key];
detail = null;
error = null;
yikes();
return true;
} else {
error = await res.text();
error(res);
return false;
}
}
@@ -177,9 +194,6 @@
</svelte:head>
<h1>{t('timetracking')}</h1>
{#if error}
<span class="error">{error}</span>
{/if}
{#if times}
{#if selectionSum}
<div class="timetracks sum">
@@ -230,7 +244,7 @@
<td class="subject" onclick={e => {detail = time.id}}>
{time.subject}
</td>
<td class="tasks" onclick={e => {detail = time.id}}>
<td class="tasks">
<ul>
{#each time.task_ids as tid}
{#if tasks[tid]}
@@ -239,6 +253,7 @@
<a href="/project/{tasks[tid].project_id}/view" {onclick}>{projects[tasks[tid].project_id].name}</a> /
{/if}
<a href="/task/{tid}/view" {onclick}>{tasks[tid].name}</a>
<button class="symbol" title={t('timetracking')} onclick={e => addTime(tid)}></button>
</li>
{/if}
{/each}

View File

@@ -5,7 +5,6 @@
let { editPassword = $bindable() } = $props();
let caption = $state(t('update'));
let error = $state("");
let newPass = $state("");
let mismatch = $derived(newPass != repeat);
let newEmpty = $derived(!/\S/.test(newPass));
@@ -67,7 +66,4 @@
</label>
<button onclick={submit} disabled={sent||oldEmpty||newEmpty||mismatch}>{caption}</button>
<button onclick={abort} disabled={sent}>{t('abort')}</button>
{#if error}
<span class="error">{error}</span>
{/if}
</fieldset>

View File

@@ -2,9 +2,10 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { onMount } from 'svelte';
import { api } from '../../urls.svelte.js'
import { checkUser } from '../../user.svelte.js';
import { t } from '../../translations.svelte.js';
import { api } from '../../urls.svelte'
import { error, yikes } from '../../warn.svelte';
import { checkUser } from '../../user.svelte';
import { t } from '../../translations.svelte';
let { user_id } = $props();
let caption = $state(t('save_object',{object:t('user')}));
@@ -60,8 +61,10 @@
if (resp.ok){
caption = t('saved');
checkUser();
yikes();
router.navigate('/user');
} else {
error(resp);
caption = t('failed');
sent = false;
}

View File

@@ -3,10 +3,10 @@
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { error } from '../../warn.svelte';
import { t } from '../../translations.svelte.js';
let caption = t('send_mail');
let error = null;
let mail = "";
const router = useTinyRouter();
@@ -35,7 +35,7 @@
if (resp.ok){
router.navigate('/user')
} else {
error = await resp.text();
error(resp);
}
}
}
@@ -66,8 +66,5 @@
{t('enter_email')}
</label>
<button type="submit">{caption}</button>
{#if error}
<div class="error">{error}</div>
{/if}
</fieldset>
</form>

View File

@@ -1,12 +1,12 @@
<script>
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import Markdown from '../../Components/MarkdownEditor.svelte';
let content = $state({source:null,rendered:null});
let error = $state(null);
let router = useTinyRouter();
let timer = null;
let title = $state(null);
@@ -21,9 +21,10 @@
body:content.source
});
if (res.ok){
yikes();
router.navigate(`/wiki/${title}/view`);
} else {
error = await res.text();
error(res);
}
}
@@ -40,10 +41,11 @@
var res = await fetch(url,{credentials:'include'});
const body = await res.text();
if (res.ok){
error = body == 'true' ? null : t('title_not_available',{title:title})
if (body == 'true') {
yikes();
} else error(t('title_not_available',{title:title}));
} else {
error = body;
if (!error) error = t('failed_to_check_availability');
error(body ? body : t('failed_to_check_availability'));
}
}
@@ -51,9 +53,6 @@
</script>
<h1>{t('create_new_object',{object:t('page')})}</h1>
{#if error}
<span class="error">{error}</span>
{/if}
<form {onsubmit}>
<label>
{t('Name')}

View File

@@ -2,12 +2,11 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let { key } = $props();
let error = $state(null);
let page = $state(null);
let router = useTinyRouter();
@@ -15,10 +14,10 @@
if (res.ok){
page = null;
page = await res.json();
error = null;
yikes();
return true;
} else {
error = await res.text();
error(res);
return false;
}
}
@@ -38,9 +37,6 @@
$effect(loadPage);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
{#if page}
<div class="wiki page">
<h2>{page.title}</h2>

View File

@@ -1,10 +1,10 @@
<script>
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { t } from '../../translations.svelte';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
let error = $state(null);
let lastLetter = null;
let pages = $state(null);
let router = useTinyRouter();
@@ -21,17 +21,15 @@
const res = await fetch(url,{credentials:'include'});
if (res.ok){
pages = await res.json();
yikes();
} else {
error = await res.text();
error(res);
}
}
onMount(loadPageList);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
<h1>{t('wiki')}</h1>
<button onclick={() => router.navigate('/wiki/add')}><span class="symbol"></span> {t('create_new_object',{object:t('page')})}</button>

View File

@@ -2,6 +2,7 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import { user } from '../../user.svelte';
@@ -12,7 +13,6 @@
import TagList from '../tags/TagList.svelte';
let detail = $state(false);
let error = $state(null);
let { key, version } = $props();
let page = $state(null);
let router = useTinyRouter();
@@ -45,8 +45,9 @@
if (res.ok){
let json = await res.json();
router.navigate(`/wiki/${page.id}/view`);
yikes();
} else {
error = await res.text();
error(res);
}
}
@@ -74,10 +75,10 @@
page = null;
page = await res.json();
page.versions.sort((a,b)=>b-a);
error = null;
yikes();
return true;
} else {
error = await res.text();
error(res);
return false;
}
}
@@ -130,9 +131,6 @@
$effect(loadPage);
</script>
{#if error}
<span class="error">{error}</span>
{/if}
{#if page}
<div class="wiki page">
<div class="versions">

View File

@@ -0,0 +1,19 @@
export const messages = $state({error:null,warning:null});
async function process(msg){
if (msg instanceof Response) return await msg.text();
return msg;
}
export async function error(msg){
messages.error = await process(msg);
}
export async function warn(msg){
messages.warning = await process(msg);
}
export function yikes(){
messages.error = null;
messages.warn = null;
}

View File

@@ -195,7 +195,7 @@
"save": "save",
"saved": "saved",
"save_object": "save {object}",
"search": "seach",
"search": "search",
"select_company" : "select on of you companies:",
"select_customer": "select customer",
"select_state": "select state",