working on project creation
This commit is contained in:
@@ -71,6 +71,7 @@ public class Application {
|
||||
var webHandler = new WebHandler();
|
||||
|
||||
documentApi .bindPath("/api/document") .on(server);
|
||||
companyModule .bindPath("/api/company") .on(server);
|
||||
itemApi .bindPath("/api/items") .on(server);
|
||||
markdownApi .bindPath("/api/markdown") .on(server);
|
||||
messageApi .bindPath("/api/messages") .on(server);
|
||||
|
||||
@@ -3,19 +3,28 @@ package de.srsoftware.umbrella.company;
|
||||
|
||||
import static de.srsoftware.umbrella.company.Constants.CONFIG_DATABASE;
|
||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
||||
import static de.srsoftware.umbrella.core.Paths.LIST;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.srsoftware.configuration.Configuration;
|
||||
import de.srsoftware.tools.Path;
|
||||
import de.srsoftware.tools.SessionToken;
|
||||
import de.srsoftware.umbrella.company.api.CompanyDb;
|
||||
import de.srsoftware.umbrella.core.BaseHandler;
|
||||
import de.srsoftware.umbrella.core.api.CompanyService;
|
||||
import de.srsoftware.umbrella.core.api.UserService;
|
||||
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import de.srsoftware.umbrella.core.model.Company;
|
||||
import de.srsoftware.umbrella.core.model.Token;
|
||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CompanyModule implements CompanyService {
|
||||
public class CompanyModule extends BaseHandler implements CompanyService {
|
||||
|
||||
private final UserService users;
|
||||
private final CompanyDb companyDb;
|
||||
@@ -26,11 +35,36 @@ public class CompanyModule implements CompanyService {
|
||||
users = userService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doGet(Path path, HttpExchange ex) throws IOException {
|
||||
addCors(ex);
|
||||
try {
|
||||
Optional<Token> token = SessionToken.from(ex).map(Token::of);
|
||||
var user = users.loadUser(token);
|
||||
if (user.isEmpty()) return unauthorized(ex);
|
||||
var head = path.pop();
|
||||
return switch (head){
|
||||
case LIST -> getCompanyList(user.get(),ex);
|
||||
case null,
|
||||
default -> super.doGet(path,ex);
|
||||
};
|
||||
} catch (UmbrellaException e) {
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Company get(long companyId) throws UmbrellaException {
|
||||
return companyDb.load(companyId);
|
||||
}
|
||||
|
||||
private boolean getCompanyList(UmbrellaUser user, HttpExchange ex) throws IOException, UmbrellaException {
|
||||
var list = listCompaniesOf(user).stream().map(Company::toMap);
|
||||
return sendContent(ex,list);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<UmbrellaUser> getMembers(long companyId) throws UmbrellaException {
|
||||
var members = new HashSet<UmbrellaUser>();
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
public record Project(long id, String name, String description, Status status, long companyId, boolean showClosed) implements Mappable {
|
||||
public record Project(long id, String name, String description, Status status, Long companyId, boolean showClosed) implements Mappable {
|
||||
public enum Status{
|
||||
Open(10),
|
||||
Started(20),
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import Messages from "./routes/message/Messages.svelte";
|
||||
import Menu from "./Components/Menu.svelte";
|
||||
import ProjectList from "./routes/project/List.svelte";
|
||||
import ProjectAdd from "./routes/project/Create.svelte";
|
||||
import ResetPw from "./routes/user/ResetPw.svelte";
|
||||
import Search from "./routes/search/Search.svelte";
|
||||
import SendDoc from "./routes/document/Send.svelte";
|
||||
@@ -46,6 +47,7 @@
|
||||
<Route path="/document/:id/view" component={ViewDoc} />
|
||||
<Route path="/message/settings" component={Messages} />
|
||||
<Route path="/project" component={ProjectList} />
|
||||
<Route path="/project/add" component={ProjectAdd} />
|
||||
<Route path="/search" component={Search} />
|
||||
<Route path="/user" component={User} />
|
||||
<Route path="/user/create" component={EditUser} />
|
||||
|
||||
37
frontend/src/Components/CompanySelector.svelte
Normal file
37
frontend/src/Components/CompanySelector.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script>
|
||||
import {onMount} from 'svelte';
|
||||
import {t} from '../translations.svelte.js';
|
||||
let { caption, onselect = (company) => console.log('selected '+company.name) } = $props();
|
||||
let message = t('loading');
|
||||
let companies = $state(null);
|
||||
let value = 0;
|
||||
|
||||
async function loadCompanies(){
|
||||
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/company/list`;
|
||||
var resp = await fetch(url,{ credentials: 'include'});
|
||||
if (resp.ok){
|
||||
companies = await resp.json();
|
||||
} else {
|
||||
message = await resp.text();
|
||||
}
|
||||
}
|
||||
|
||||
function select(){
|
||||
onselect(companies[value]);
|
||||
}
|
||||
|
||||
onMount(loadCompanies)
|
||||
|
||||
</script>
|
||||
|
||||
{#if companies}
|
||||
<select onchange={select} bind:value>
|
||||
<option value={0}>{caption}</option>
|
||||
{#each companies as company,idx}
|
||||
<option value={idx}>{company.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<span>{message}</span>
|
||||
{/if}
|
||||
|
||||
@@ -2,18 +2,75 @@
|
||||
import { useTinyRouter } from 'svelte-tiny-router';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../../translations.svelte.js';
|
||||
|
||||
import CompanySelector from '../../Components/CompanySelector.svelte';
|
||||
import Settings from './Settings.svelte';
|
||||
let showSettings = $state(false);
|
||||
let ready = $derived(!!project.name.trim())
|
||||
let error = $state(null);
|
||||
|
||||
let project = $state({
|
||||
name:'',
|
||||
description:'',
|
||||
settings:{
|
||||
show_closed:false
|
||||
}
|
||||
});
|
||||
|
||||
async function onsubmit(ev){
|
||||
ev.preventDefault();
|
||||
const url = `${location.protocol}//${location.host.replace('5173','8080')}/api/project`;
|
||||
var resp = await fetch(url,{
|
||||
credentials: 'include',
|
||||
method: 'POST',
|
||||
body: JSON.stringify(project)
|
||||
});
|
||||
if (resp.ok){
|
||||
router.navigate('/project');
|
||||
} else {
|
||||
error = await resp.text();
|
||||
}
|
||||
}
|
||||
|
||||
function onselect(company){
|
||||
project.company_id = company.id;
|
||||
console.log(project);
|
||||
}
|
||||
</script>
|
||||
|
||||
<fieldset>
|
||||
<legend>
|
||||
{t('create_new_project')}
|
||||
</legend>
|
||||
<style>
|
||||
label{ display: block }
|
||||
</style>
|
||||
|
||||
{#if error}
|
||||
<span class="error">{error}</span>
|
||||
{/if}
|
||||
<form {onsubmit}>
|
||||
<fieldset>
|
||||
<legend>{t('basic_data')}</legend>
|
||||
<span class="warn">Company Selector</span>
|
||||
<label>
|
||||
<input type="text" />
|
||||
{t('Name')}
|
||||
</label>
|
||||
<legend>
|
||||
{t('create_new_project')}
|
||||
</legend>
|
||||
<fieldset>
|
||||
<legend>{t('basic_data')}</legend>
|
||||
<label>
|
||||
<CompanySelector caption={t('no_company')} {onselect} />
|
||||
{t('company_optional')}
|
||||
</label>
|
||||
<label>
|
||||
<input type="text" bind:value={project.name}/>
|
||||
{t('Name')}
|
||||
</label>
|
||||
<label>
|
||||
<textarea bind:value={project.description}></textarea>
|
||||
{t('description')}
|
||||
</label>
|
||||
{#if !showSettings}
|
||||
<button onclick={() => showSettings = true}>{t('extended_settings')}</button>
|
||||
{/if}
|
||||
</fieldset>
|
||||
{#if showSettings}
|
||||
<Settings bind:settings={project.settings}/>
|
||||
{/if}
|
||||
<button type="submit" disabled={!ready}>{t('create')}</button>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -2,11 +2,13 @@
|
||||
import { useTinyRouter } from 'svelte-tiny-router';
|
||||
import { onMount } from 'svelte';
|
||||
import { t } from '../../translations.svelte.js';
|
||||
|
||||
const router = useTinyRouter();
|
||||
</script>
|
||||
|
||||
<fieldset>
|
||||
<legend>
|
||||
{t('projects')}
|
||||
<button>{t('create_new')}</button>
|
||||
<button onclick={() => router.navigate('/project/add')}>{t('create_new')}</button>
|
||||
</legend>
|
||||
</fieldset>
|
||||
13
frontend/src/routes/project/Settings.svelte
Normal file
13
frontend/src/routes/project/Settings.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script>
|
||||
import {t} from '../../translations.svelte.js';
|
||||
|
||||
let { settings = $bindable() } = $props();
|
||||
|
||||
</script>
|
||||
<fieldset>
|
||||
<legend>{t('settings')}</legend>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={settings.show_closed} />
|
||||
{t('display_closed_tasks')}
|
||||
</label>
|
||||
</fieldset>
|
||||
@@ -7,4 +7,6 @@ import java.util.Collection;
|
||||
|
||||
public interface ProjectDb {
|
||||
Collection<Project> list(long companyId, boolean includeClosed) throws UmbrellaException;
|
||||
|
||||
Project save(Project prj);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ package de.srsoftware.umbrella.project;
|
||||
import static de.srsoftware.umbrella.core.ConnectionProvider.connect;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.Paths.LIST;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.forbidden;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException;
|
||||
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
|
||||
import static de.srsoftware.umbrella.project.Constants.CONFIG_DATABASE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Comparator.comparing;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
@@ -21,6 +21,8 @@ import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
|
||||
import de.srsoftware.umbrella.core.model.Project;
|
||||
import de.srsoftware.umbrella.core.model.Token;
|
||||
import de.srsoftware.umbrella.core.model.UmbrellaUser;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -54,6 +56,7 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
||||
var head = path.pop();
|
||||
return switch (head) {
|
||||
case LIST -> listItems(ex,user.get());
|
||||
case null -> postProject(ex,user.get());
|
||||
default -> super.doGet(path,ex);
|
||||
};
|
||||
} catch (UmbrellaException e){
|
||||
@@ -61,10 +64,6 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Project> listProjects(long companyId, boolean includeClosed) throws UmbrellaException {
|
||||
return projectDb.list(companyId, includeClosed).stream().sorted(comparing(Project::name)).toList();
|
||||
}
|
||||
|
||||
private boolean listItems(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||
var json = json(ex);
|
||||
if (!(json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number cid)) throw missingFieldException(COMPANY_ID);
|
||||
@@ -77,4 +76,26 @@ public class ProjectModule extends BaseHandler implements ProjectService {
|
||||
.map(HashMap::new);
|
||||
return sendContent(ex,items);
|
||||
}
|
||||
|
||||
public Collection<Project> listProjects(long companyId, boolean includeClosed) throws UmbrellaException {
|
||||
return projectDb.list(companyId, includeClosed).stream().sorted(comparing(Project::name)).toList();
|
||||
}
|
||||
|
||||
private boolean postProject(HttpExchange ex, UmbrellaUser user) throws IOException, UmbrellaException {
|
||||
var json = json(ex);
|
||||
if (!(json.has(NAME) && json.get(NAME) instanceof String name)) throw missingFieldException(NAME);
|
||||
var description = json.has(DESCRIPTION) && json.get(DESCRIPTION) instanceof String d ? d : null;
|
||||
Long companyId = null;
|
||||
if (json.has(COMPANY_ID) && json.get(COMPANY_ID) instanceof Number number){
|
||||
if (!companies.membership(number.longValue(), user.id())) throw forbidden("You are not a member of company {0}!",number);
|
||||
companyId = number.longValue();
|
||||
}
|
||||
var showClosed = false;
|
||||
if (json.has(SETTINGS) && json.get(SETTINGS) instanceof JSONObject settingsJson){
|
||||
showClosed = settingsJson.has(SHOW_CLOSED) && settingsJson.get(SHOW_CLOSED) == TRUE;
|
||||
}
|
||||
var prj = new Project(0,name,description,Project.Status.Open,companyId,showClosed);
|
||||
projectDb.save(prj);
|
||||
return notFound(ex);
|
||||
}
|
||||
}
|
||||
@@ -138,4 +138,12 @@ CREATE TABLE IF NOT EXISTS {0} ( {1} VARCHAR(255) PRIMARY KEY, {2} VARCHAR(255)
|
||||
throw new UmbrellaException(HTTP_SERVER_ERROR,"Failed to load items from database");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project save(Project prj) {
|
||||
try {
|
||||
insertInto(TABLE_PROJECTS);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
"add_position": "hinzufügen",
|
||||
"advertisement" : "Umbrella ist ein Produkt von {0}.",
|
||||
"amount": "Menge",
|
||||
|
||||
"bank_account": "Bankverbindung",
|
||||
"base_url": "Basis-URL",
|
||||
"basic_data": "Basis-Daten",
|
||||
"bookmark": "Lesezeichen",
|
||||
"by": "von",
|
||||
|
||||
"client_id": "Client-ID",
|
||||
"client_secret": "Client-Geheimnis",
|
||||
"code": "Code",
|
||||
@@ -17,10 +20,13 @@
|
||||
"connected_services": "verbundene Login-Services",
|
||||
"confirm_deletion": "Soll '{pos}' wirklich gelöscht werden?",
|
||||
"company": "Firma",
|
||||
"company_optional": "Firma (optional)",
|
||||
"contact": "Kontakte",
|
||||
"contained_tax": "enthaltene Steuer",
|
||||
"content": "Inhalt",
|
||||
"create": "anlegen",
|
||||
"create_new_document": "neues Dokument",
|
||||
"create_new_project": "neues Projekt anlegen",
|
||||
"create_new_user": "Neuen Benutzer anlegen",
|
||||
"CREATE_USERS": "Nutzer anlegen",
|
||||
"create_pdf": "PDF erzeugen",
|
||||
@@ -35,10 +41,12 @@
|
||||
"DELETE_USERS": "Nutzer löschen",
|
||||
"delivery_date": "Lieferdatum",
|
||||
"description": "Beschreibung",
|
||||
"display_closed_tasks": "abgeschlossene Aufgaben anzeigen",
|
||||
"document": "Dokumente",
|
||||
"documents": "Dokumente",
|
||||
"do_login" : "anmelden",
|
||||
"do_send" : "versenden",
|
||||
|
||||
"edit": "Bearbeiten",
|
||||
"editing": "Nutzer {0} bearbeiten",
|
||||
"edit_password": "Passwort ändern",
|
||||
@@ -47,6 +55,7 @@
|
||||
"email_or_username": "Email oder Nutzername",
|
||||
"estimated_time": "geschätzte Zeit",
|
||||
"estimated_times": "geschätzte Zeiten",
|
||||
"extended_settings": "erweiterte Einstellungen",
|
||||
|
||||
"failed": "fehlgeschlagen",
|
||||
"files": "Dateien",
|
||||
@@ -93,6 +102,7 @@
|
||||
"net_sum": "Netto-Summe",
|
||||
"new_password": "neues Passwort",
|
||||
"new_document_from": "{2} / neues {0}s-Dokument von {1}",
|
||||
"no_company": "keine Firma",
|
||||
"notes": "Notizen",
|
||||
"number": "Nummer",
|
||||
|
||||
|
||||
Reference in New Issue
Block a user