Browse Source

preparing storing of new contact

module/contact
Stephan Richter 4 weeks ago
parent
commit
e0c05506c3
  1. 12
      contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java
  2. 22
      frontend/src/routes/contact/Card.svelte
  3. 21
      frontend/src/routes/contact/Index.svelte
  4. 43
      frontend/src/routes/contact/Number.svelte
  5. 18
      frontend/src/routes/contact/URL.svelte
  6. 11
      frontend/src/vcard.js
  7. 3
      translations/src/main/resources/de.json

12
contact/src/main/java/de/srsoftware/umbrella/contact/SqliteDb.java

@ -3,6 +3,7 @@ package de.srsoftware.umbrella.contact; @@ -3,6 +3,7 @@ package de.srsoftware.umbrella.contact;
import static de.srsoftware.tools.jdbc.Condition.equal;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.tools.jdbc.Query.insertInto;
import static de.srsoftware.tools.jdbc.Query.select;
import static de.srsoftware.umbrella.contact.Constants.*;
import static de.srsoftware.umbrella.core.Constants.*;
@ -92,7 +93,16 @@ public class SqliteDb extends BaseDb implements ContactDb{ @@ -92,7 +93,16 @@ public class SqliteDb extends BaseDb implements ContactDb{
@Override
public Contact save(Contact contact) {
if (contact.id() == 0){ // new contact
throw UmbrellaException.unprocessable("Storing of new contacts not implemented");
try {
var rs = insertInto(TABLE_CONTACTS,DATA).values(contact.vcard()).execute(db).getGeneratedKeys();
Long id = null;
if (rs.next()) id = rs.getLong(1);
rs.close();
if (id != null) return new Contact(id,contact.vcard());
throw databaseException("Failed to store new vcard");
} catch (SQLException e) {
throw databaseException("Failed to store new vcard",e);
}
} else try { // update
Query.update(TABLE_CONTACTS).set(DATA).where(ID,equal(contact.id())).prepare(db).apply(contact.vcard()).execute();
} catch (SQLException e) {

22
frontend/src/routes/contact/Card.svelte

@ -9,7 +9,9 @@ @@ -9,7 +9,9 @@
import ExtraField from './ExtraField.svelte';
import FN from './FN.svelte';
import Name from './Name.svelte';
import Number from './Number.svelte';
import Org from './Org.svelte';
import URL from './URL.svelte';
let { contact } = $props();
@ -18,7 +20,9 @@ @@ -18,7 +20,9 @@
let emails = $derived(contact.vcard.match(/^EMAIL.*:.+$/gm));
let extra_fields = $derived(contact.vcard.match(/^X-.*:.+/gm));
let fns = $derived(contact.vcard.match(/^FN.*:.+$/gm));
let numbers = $derived(contact.vcard.match(/^TEL.*:.+$/gm));
let orgs = $derived(contact.vcard.match(/^ORG.*:.+$/gm));
let urls = $derived(contact.vcard.match(/^URL.*:.+$/gm));
async function patch(from,to){
if (from == to) return;
@ -44,7 +48,11 @@ @@ -44,7 +48,11 @@
<fieldset class="vcard">
<legend>
{#if contact.id}
{t('contact_number',{number:contact.id})}
{:else}
{t('new_contact')}
{/if}
<button class="symbol" onclick={toggleCode}></button>
</legend>
<table>
@ -81,6 +89,13 @@ @@ -81,6 +89,13 @@
{/each}
</td>
</tr>
<tr>
<td>
{#each numbers as code}
<Number {code} {patch} />
{/each}
</td>
</tr>
<tr>
<td>
{#each emails as code}
@ -95,6 +110,13 @@ @@ -95,6 +110,13 @@
{/each}
</td>
</tr>
<tr>
<td>
{#each urls as code}
<URL {code} {patch} />
{/each}
</td>
</tr>
</tbody>
</table>
</fieldset>

21
frontend/src/routes/contact/Index.svelte

@ -15,6 +15,27 @@ @@ -15,6 +15,27 @@
yikes();
var data = await res.json();
contacts = Object.values(data).sort(byName);
contacts.unshift({
id: 0,
vcard: `BEGIN:VCARD
VERSION:3.0
PRODID:Umbrella Contact manager by SRSoftware
FN:${t('formatted_name')}
N:${t('family_name')};${t('given_name')};;;
ORG:${t('organization')}
EMAIL;TYPE=HOME:${t('email')}
EMAIL;TYPE=WORK:${t('email')}
ADR;TYPE=HOME:;;${t('street')};${t('locality')};${t('region')};${t('post_code')};${t('country')}
ADR;TYPE=WORK:;;${t('street')};${t('locality')};${t('region')};${t('post_code')};${t('country')}
TEL;TYPE=CELL;:${t('phone_cell')}
TEL;TYPE=HOME;:${t('phone_home')}
TEL;TYPE=WORK;:${t('phone_work')}
X-TAX-NUMBER:${t('tax_id')}
X-BANK-ACCOUNT:${t('bank_account')}\\nIBAN:XXXX\\nBIC:XXXX
X-COURT:${t('local_court')}
URL:https://example.com
END:VCARD`
});
} else {
error(res);
}

43
frontend/src/routes/contact/Number.svelte

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import { number } from '../../vcard.js';
let { code, patch = (from, to) => true } = $props();
let cell = $derived(code.toLowerCase().includes('type=cell'));
let home = $derived(code.toLowerCase().includes('type=home'));
let work = $derived(code.toLowerCase().includes('type=work'));
let value = $derived(number(code));
function onSet(newVal){
const newCode = code.replace(value,newVal);
return patch(code,newCode);
}
function toggleCell(){
toggleType('CELL');
}
function toggleHome(){
toggleType('HOME');
}
function toggleType(key){
key = key.toUpperCase();
if (code.toUpperCase().includes(';TYPE='+key)) {
const regex = new RegExp(';TYPE='+key, "ig");
patch(code,code.replace(regex,''));
} else patch(code,code.replace('TEL','TEL;TYPE='+key));
}
function toggleWork(){
toggleType('WORK');
}
</script>
{#if value}
<span class="symbol {cell?'':'inactive'}" onclick={toggleCell} ></span>
<span class="symbol {home?'':'inactive'}" onclick={toggleHome}></span>
<span class="symbol {work?'':'inactive'}" onclick={toggleWork} ></span>
<LineEditor type="span" editable={true} {value} {onSet} /><br/>
{/if}

18
frontend/src/routes/contact/URL.svelte

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
<script>
import LineEditor from '../../Components/LineEditor.svelte';
import { url } from '../../vcard.js';
import { t } from '../../translations.svelte';
let { code, patch = (from, to) => true } = $props();
let value = $derived(url(code));
function onSet(newVal){
const newCode = code.replace(value,newVal);
return patch(code,newCode);
}
</script>
{#if value}
<LineEditor type="span" editable={true} {value} {onSet} title={t('url')}/>
{/if}

11
frontend/src/vcard.js

@ -37,11 +37,22 @@ export function fn(vcard){ @@ -37,11 +37,22 @@ export function fn(vcard){
return match ? match[1].trim() : '';
}
export function number(vcard){
const match = vcard.match(/^TEL.*:(.+)$/m);
return match ? match[1].trim() : '';
}
export function org(vcard){
const match = vcard.match(/^ORG:(.+)$/m);
return match ? match[1].trim() : '';
}
export function url(vcard){
const match = vcard.match(/^URL:(.+)$/m);
return match ? match[1].trim() : '';
}
export function name(vcard){
const match = vcard.match(/^N:(.+)$/m);
let name = {

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

@ -91,6 +91,7 @@ @@ -91,6 +91,7 @@
"failed": "fehlgeschlagen",
"failed_login_attempts" : "Account nach {attempts} fehlgeschlagenen Logins gesperrt bis {release_time}",
"family_name": "Familenname",
"file": "Datei",
"files": "Dateien",
"filter": "Filter",
@ -100,6 +101,7 @@ @@ -100,6 +101,7 @@
"formatted_name": "Anzeigename",
"fulltext": "Volltextsuche",
"given_name": "Vorname",
"go": "los!",
"go_to_url_to_reset_password": "Um ein neues Passwort zu erhalten, öffnen Sie bitte den folgenden Link: {url}",
"gross_sum": "Brutto-Summe",
@ -167,6 +169,7 @@ @@ -167,6 +169,7 @@
"name": "Name",
"net_price": "Nettopreis",
"net_sum": "Netto-Summe",
"new_contact": "neuer Kontakt",
"new_password": "neues Passwort",
"new_document_from": "{number} / neues {type}s-Dokument von {sender}",
"no_company": "keine Firma",

Loading…
Cancel
Save