From 8f3a4cb9db1b1001b6cc6375120318e3a3a74dea Mon Sep 17 00:00:00 2001 From: Fenris Wolf Date: Thu, 26 Sep 2024 13:38:25 +0200 Subject: [PATCH] [add] page:calendar_add [add] page:event_add --- source/data/localization/deu.loc.json | 43 +++ source/data/localization/eng.loc.json | 43 +++ source/logic/backend.ts | 9 +- source/logic/main.ts | 440 ++++++++++++++++++++++++- source/logic/types.ts | 2 +- source/style/main.css | 16 + source/templates/calendar_add.html.tpl | 5 + source/templates/event_add.html.tpl | 5 + tools/makefile | 8 +- 9 files changed, 552 insertions(+), 19 deletions(-) create mode 100644 source/data/localization/deu.loc.json create mode 100644 source/data/localization/eng.loc.json create mode 100644 source/templates/calendar_add.html.tpl create mode 100644 source/templates/event_add.html.tpl diff --git a/source/data/localization/deu.loc.json b/source/data/localization/deu.loc.json new file mode 100644 index 0000000..a16a029 --- /dev/null +++ b/source/data/localization/deu.loc.json @@ -0,0 +1,43 @@ +{ + "meta": { + "identifier": "deu" + }, + "tree": { + "common.timezone_shift": "Zeitzonen-Verschiebung", + "common.date": "Datum", + "common.time": "Uhrzeit", + "access_level.none": "nichts", + "access_level.view": "nur lesen", + "access_level.edit": "lesen und bearbeiten", + "access_level.admin": "voll", + "event.event": "Termin", + "event.name": "Name", + "event.begin": "Anfang", + "event.end": "Ende", + "event.location": "Ort", + "event.description": "Beschreibung", + "resource.kind": "Art", + "resource.kinds.local.title": "lokal", + "resource.kinds.local.event_ids": "Termin-IDs", + "resource.kinds.caldav.title": "CalDAV", + "resource.kinds.caldav.url": "URL", + "resource.kinds.caldav.read_only": "nur lesen", + "calendar.calendar": "Kalender", + "calendar.name": "Name", + "calendar.resource": "Resource", + "calendar.access.access": "Zugriff", + "calendar.access.default_level": "Standard-Zugriff", + "calendar.access.attributed": "Zuweisungen", + "page.login.title": "Anmelden", + "page.login.internal.name": "Name", + "page.login.internal.password": "Kennwort", + "page.login.internal.do": "Anmelden", + "page.login.oidc.via": "via {{title}}", + "page.logout.title": "Abmelden", + "page.events.title": "Übersicht", + "page.calendar_add.title": "Kalendar anlegen", + "page.calendar_add.actions.do": "anlegen", + "page.event_add.title": "Termin anlegen", + "page.event_add.actions.do": "anlegen" + } +} diff --git a/source/data/localization/eng.loc.json b/source/data/localization/eng.loc.json new file mode 100644 index 0000000..29b10aa --- /dev/null +++ b/source/data/localization/eng.loc.json @@ -0,0 +1,43 @@ +{ + "meta": { + "identifier": "eng" + }, + "tree": { + "common.timezone_shift": "Timezone shift", + "common.date": "date", + "common.time": "time", + "access_level.none": "none", + "access_level.view": "read only", + "access_level.edit": "read and write", + "access_level.admin": "full", + "event.event": "event", + "event.name": "name", + "event.begin": "begin", + "event.end": "end", + "event.location": "location", + "event.description": "description", + "resource.kind": "kind", + "resource.kinds.local.title": "local", + "resource.kinds.local.event_ids": "event ids", + "resource.kinds.caldav.title": "CalDAV", + "resource.kinds.caldav.url": "URL", + "resource.kinds.caldav.read_only": "read-only", + "calendar.calendar": "Kalendar", + "calendar.name": "name", + "calendar.resource": "resource", + "calendar.access.access": "access", + "calendar.access.default_level": "default level", + "calendar.access.attributed": "attributed", + "page.login.title": "Login", + "page.login.internal.name": "name", + "page.login.internal.password": "password", + "page.login.internal.do": "login", + "page.login.oidc.via": "via {{title}}", + "page.logout.title": "Logout", + "page.events.title": "Overview", + "page.calendar_add.title": "Add calendar", + "page.calendar_add.actions.do": "anlegen", + "page.event_add.title": "Add event", + "page.event_add.actions.do": "anlegen" + } +} diff --git a/source/logic/backend.ts b/source/logic/backend.ts index 125eced..d275432 100644 --- a/source/logic/backend.ts +++ b/source/logic/backend.ts @@ -235,10 +235,9 @@ namespace _zeitbild.frontend_web.backend ) : Promise< Array< { - key : _zeitbild.frontend_web.type.calendar_id; - preview : { - name : string; - } + id : int; + name : string; + access_level : string; } > > @@ -254,7 +253,7 @@ namespace _zeitbild.frontend_web.backend /** */ export async function calendar_add( - calendar_object : _zeitbild.frontend_web.type.type_calendar_object + calendar_object : _zeitbild.frontend_web.type.calendar_object ) : Promise< _zeitbild.frontend_web.type.calendar_id > diff --git a/source/logic/main.ts b/source/logic/main.ts index 055421b..f293354 100644 --- a/source/logic/main.ts +++ b/source/logic/main.ts @@ -1,4 +1,78 @@ +/** + */ +class class_input_datetime implements lib_plankton.zoo_input.interface_input<_zeitbild.frontend_web.helpers.type_datetime> +{ + + /** + */ + private core : lib_plankton.zoo_input.class_input_group<_zeitbild.frontend_web.helpers.type_datetime>; + + + /** + */ + public constructor( + prefix : string + ) + { + this.core = new lib_plankton.zoo_input.class_input_group<_zeitbild.frontend_web.helpers.type_datetime>( + [ + { + "name": "timezone_shift", + "input": new lib_plankton.zoo_input.class_input_number( + ), + "label": (prefix + lib_plankton.translate.get("common.timezone_shift")), + }, + { + "name": "date", + "input": new lib_plankton.zoo_input.class_input_date( + ), + "label": (prefix + lib_plankton.translate.get("common.date")), + }, + { + "name": "time", + "input": new lib_plankton.zoo_input.class_input_soft( + new lib_plankton.zoo_input.class_input_time( + ) + ), + "label": (prefix + lib_plankton.translate.get("common.time")), + }, + ] + ); + } + + + /** + */ + public async setup( + parent : HTMLElement + ) : Promise + { + return this.core.setup(parent); + } + + + /** + */ + public async read( + ) : Promise<_zeitbild.frontend_web.helpers.type_datetime> + { + return this.core.read(); + } + + + /** + */ + public async write( + value : _zeitbild.frontend_web.helpers.type_datetime + ) : Promise + { + return this.core.write(value); + } + +} + + /** */ namespace _zeitbild.frontend_web @@ -12,8 +86,20 @@ namespace _zeitbild.frontend_web // conf await _zeitbild.frontend_web.conf.init("conf.json"); + // init await _zeitbild.frontend_web.backend.init(); + await lib_plankton.translate.initialize( + { + "verbosity": 1, + "packages": [ + JSON.parse(await lib_plankton.file.read("data/localization/deu.loc.json")), + JSON.parse(await lib_plankton.file.read("data/localization/eng.loc.json")), + ], + "order": ["deu", "eng"], + "autopromote": false, + } + ); lib_plankton.zoo_page.init( document.querySelector("main"), { @@ -26,7 +112,6 @@ namespace _zeitbild.frontend_web target_element.innerHTML = await _zeitbild.frontend_web.helpers.template_coin( "login", { - "form": "", } ); const form : lib_plankton.zoo_form.class_form< @@ -45,18 +130,18 @@ namespace _zeitbild.frontend_web { "name": "name", "input": new lib_plankton.zoo_input.class_input_text(), - "label": "Name", // TODO: translate + "label": lib_plankton.translate.get("page.login.internal.name"), }, { "name": "password", "input": new lib_plankton.zoo_input.class_input_password(), - "label": "Kennwort", // TODO: translate + "label": lib_plankton.translate.get("page.login.internal.password"), }, ] ), [ { - "label": "Anmelden", // TODO: translate + "label": lib_plankton.translate.get("page.login.internal.do"), "target": "submit", "procedure": async (get_value, get_representation) => { const value : any = await get_value(); @@ -98,9 +183,9 @@ namespace _zeitbild.frontend_web case "oidc": { let element_a : HTMLElement = document.createElement("a");; element_a.textContent = lib_plankton.string.coin( - "via {{label}}", // TODO: translate + lib_plankton.translate.get("page.login.oidc.via"), { - "label": preparation.data.label, + "title": preparation.data.label, } ); element_a.setAttribute("href", preparation.data.url); @@ -133,6 +218,329 @@ namespace _zeitbild.frontend_web } ); }, + "calendar_add": async (parameters, target_element) => { + target_element.innerHTML = await _zeitbild.frontend_web.helpers.template_coin( + "calendar_add", + { + "label": lib_plankton.translate.get("page.calendar_add.title") + } + ); + const form : lib_plankton.zoo_form.class_form< + _zeitbild.frontend_web.type.calendar_object, + { + name : string; + access_default_level : string; + resource_kind : string; + } + > = new lib_plankton.zoo_form.class_form< + _zeitbild.frontend_web.type.calendar_object, + { + name : string; + access_default_level : string; + resource_kind : string; + } + >( + (calendar_object) => ({ + "name": calendar_object.name, + "access_default_level": (() => { + switch (calendar_object.access.default_level) { + case _zeitbild.frontend_web.type.enum_access_level.none: return "none"; + case _zeitbild.frontend_web.type.enum_access_level.view: return "view"; + case _zeitbild.frontend_web.type.enum_access_level.edit: return "edit"; + case _zeitbild.frontend_web.type.enum_access_level.admin: return "admin"; + } + }) (), + "resource_kind": calendar_object.resource.kind, + }), + (raw) => ({ + "name": raw.name, + "access": { + "default_level": (() => { + switch (raw.access_default_level) { + case "none": return _zeitbild.frontend_web.type.enum_access_level.none; + case "view": return _zeitbild.frontend_web.type.enum_access_level.view; + case "edit": return _zeitbild.frontend_web.type.enum_access_level.edit; + case "admin": return _zeitbild.frontend_web.type.enum_access_level.admin; + } + }) (), + "attributed": lib_plankton.map.hashmap.implementation_map( + lib_plankton.map.hashmap.make( + x => x.toFixed(0) + ) + ), + }, + "resource": (() => { + switch (raw.resource_kind) { + case "local": { + return { + "kind": "local", + "data": { + "events": [], + } + }; + break; + } + case "caldav": { + return { + "kind": "caldav", + "data": { + "url": "", // TODO + "read_only": true, // TODO + } + }; + break; + } + default: { + throw (new Error("invalid resource kind: " + raw.resource_kind)); + break; + } + } + }) (), + }), + new lib_plankton.zoo_input.class_input_group( + [ + { + "name": "name", + "input": new lib_plankton.zoo_input.class_input_text(), + "label": lib_plankton.translate.get("calendar.name") + }, + { + "name": "access_default_level", + "input": new lib_plankton.zoo_input.class_input_selection( + [ + { + "value": "none", + "label": lib_plankton.translate.get("access_level.none"), + }, + { + "value": "view", + "label": lib_plankton.translate.get("access_level.view") + }, + { + "value": "edit", + "label": lib_plankton.translate.get("access_level.edit") + }, + { + "value": "admin", + "label": lib_plankton.translate.get("access_level.admin") + }, + ] + ), + "label": lib_plankton.string.coin( + "{{default_level}}", + { + "head": lib_plankton.translate.get("calendar.access.access"), + "default_level": lib_plankton.translate.get("calendar.access.default_level"), + } + ) + }, + { + "name": "resource_kind", + "input": new lib_plankton.zoo_input.class_input_selection( + [ + { + "value": "local", + "label": lib_plankton.translate.get("resource.kinds.local.title") + }, + { + "value": "caldav", + "label": lib_plankton.translate.get("resource.kinds.caldav.title") + }, + ] + ), + "label": lib_plankton.translate.get("resource.kind") + }, + ] + ), + [ + { + "label": lib_plankton.translate.get("page.calendar_add.actions.do"), + "target": "submit", + "procedure": async (get_value, get_representation) => { + const value : any = await get_value(); + try { + await _zeitbild.frontend_web.backend.calendar_add( + value + ); + lib_plankton.zoo_page.set( + { + "name": "events", + "parameters": {} + } + ); + } + catch (error) { + // do nothing + /* + lib_plankton.zoo_page.set( + { + "name": "event_add", + "parameters": { + } + } + ); + */ + } + } + }, + ] + ); + await form.setup(document.querySelector("#calendar_add_form")); + }, + "event_add": async (parameters, target_element) => { + const calendar_id : int = parseInt(parameters["calendar_id"]); + target_element.innerHTML = await _zeitbild.frontend_web.helpers.template_coin( + "event_add", + { + "label": lib_plankton.translate.get("page.event_add.title") + } + ); + const form : lib_plankton.zoo_form.class_form< + { + calendar_id : _zeitbild.frontend_web.type.calendar_id; + event_object : _zeitbild.frontend_web.type.event_object; + }, + { + calendar_id : string; + name : string; + begin : _zeitbild.frontend_web.helpers.type_datetime; + end : (null | _zeitbild.frontend_web.helpers.type_datetime); + location : (null | string); + description : (null | string); + } + > = new lib_plankton.zoo_form.class_form< + { + calendar_id : _zeitbild.frontend_web.type.calendar_id; + event_object : _zeitbild.frontend_web.type.event_object; + }, + { + calendar_id : string; + name : string; + begin : _zeitbild.frontend_web.helpers.type_datetime; + end : (null | _zeitbild.frontend_web.helpers.type_datetime); + location : (null | string); + description : (null | string); + } + >( + (value) => ({ + "calendar_id": value.calendar_id.toFixed(0), + "name": value.event_object.name, + "begin": value.event_object.begin, + "end": value.event_object.end, + "location": value.event_object.location, + "description": value.event_object.description, + }), + (representation) => ({ + "calendar_id": parseInt(representation.calendar_id), + "event_object": { + "name": representation.name, + "begin": representation.begin, + "end": representation.end, + "location": representation.location, + "description": representation.description, + } + }), + new lib_plankton.zoo_input.class_input_group( + [ + { + "name": "calendar_id", + "input": new lib_plankton.zoo_input.class_input_selection( + (await _zeitbild.frontend_web.backend.calendar_list()) + .filter( + (entry) => (["edit","admin"].includes(entry.access_level)) + ) + .map( + (entry) => ({ + "value": entry.id.toFixed(0), + "label": entry.name, + }) + ) + ), + "label": lib_plankton.translate.get("calendar.calendar") + }, + { + "name": "name", + "input": new lib_plankton.zoo_input.class_input_text( + ), + "label": lib_plankton.translate.get("event.name") + }, + { + "name": "begin", + "input": new class_input_datetime( + "" + ), + "label": lib_plankton.translate.get("event.begin") + }, + { + "name": "end", + "input": new lib_plankton.zoo_input.class_input_soft<_zeitbild.frontend_web.helpers.type_datetime>( + new class_input_datetime( + "" + ) + ), + "label": lib_plankton.translate.get("event.end") + }, + { + "name": "location", + "input": new lib_plankton.zoo_input.class_input_soft( + new lib_plankton.zoo_input.class_input_text( + ) + ), + "label": lib_plankton.translate.get("event.location") + }, + { + "name": "description", + "input": new lib_plankton.zoo_input.class_input_soft( + new lib_plankton.zoo_input.class_input_text( + ) + ), + "label": lib_plankton.translate.get("event.description") + }, + ] + ), + [ + { + "label": lib_plankton.translate.get("page.event_add.actions.do"), + "target": "submit", + "procedure": async (get_value, get_representation) => { + const value : any = await get_value(); + try { + await _zeitbild.frontend_web.backend.calendar_event_add( + value.calendar_id, + value.event_object + ); + lib_plankton.zoo_page.set( + { + "name": "events", + "parameters": {} + } + ); + } + catch (error) { + // do nothing + /* + lib_plankton.zoo_page.set( + { + "name": "event_add", + "parameters": { + } + } + ); + */ + } + } + }, + ] + ); + /* + await form.write( + { + "calendar_id": parameters["calendar_id"], + } + ); + */ + await form.setup(document.querySelector("#event_add_form")); + }, "events": async (parameters, target_element) => { const content = await _zeitbild.frontend_web.view.calendar_view_table_html( { @@ -161,16 +569,24 @@ namespace _zeitbild.frontend_web ); lib_plankton.zoo_page.add_nav_entry( {"name": "login", "parameters": {}}, - {"label": "Login"} - ); // TODO: translate + {"label": lib_plankton.translate.get("page.login.title")} + ); lib_plankton.zoo_page.add_nav_entry( {"name": "events", "parameters": {}}, - {"label": "Events"} - ); // TODO: translate + {"label": lib_plankton.translate.get("page.events.title")} + ); + lib_plankton.zoo_page.add_nav_entry( + {"name": "calendar_add", "parameters": {}}, + {"label": lib_plankton.translate.get("page.calendar_add.title")} + ); + lib_plankton.zoo_page.add_nav_entry( + {"name": "event_add", "parameters": {}}, + {"label": lib_plankton.translate.get("page.event_add.title")} + ); lib_plankton.zoo_page.add_nav_entry( {"name": "logout", "parameters": {}}, - {"label": "Logout"} - ); // TODO: translate + {"label": lib_plankton.translate.get("page.logout.title")} + ); // exec lib_plankton.zoo_page.start(); diff --git a/source/logic/types.ts b/source/logic/types.ts index 23e277e..fcebbc4 100644 --- a/source/logic/types.ts +++ b/source/logic/types.ts @@ -88,7 +88,7 @@ namespace _zeitbild.frontend_web.type /** */ - export type type_calendar_object = { + export type calendar_object = { name : string; access : { default_level : enum_access_level; diff --git a/source/style/main.css b/source/style/main.css index 3f1579b..63287bb 100644 --- a/source/style/main.css +++ b/source/style/main.css @@ -125,3 +125,19 @@ a:hover { font-weight: bold; font-size: 0.8em; } + +.plankton_input_soft_container > * { + display: inline-block; +} + +.plankton_input_soft_setter { + margin-right: 8px; +} + +.plankton_input_soft_inactive { + display: none !important; +} + +.plankton_input_group { + margin-left: 48px; +} diff --git a/source/templates/calendar_add.html.tpl b/source/templates/calendar_add.html.tpl new file mode 100644 index 0000000..3b573e7 --- /dev/null +++ b/source/templates/calendar_add.html.tpl @@ -0,0 +1,5 @@ +
+

{{label}}

+
+
+
diff --git a/source/templates/event_add.html.tpl b/source/templates/event_add.html.tpl new file mode 100644 index 0000000..3cfad06 --- /dev/null +++ b/source/templates/event_add.html.tpl @@ -0,0 +1,5 @@ +
+

{{label}}

+
+
+
diff --git a/tools/makefile b/tools/makefile index cc29957..579ee80 100644 --- a/tools/makefile +++ b/tools/makefile @@ -17,7 +17,7 @@ cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc ## rules .PHONY: default -default: index templates style logic +default: index templates style logic data .PHONY: index index: ${dir_build}/index.html @@ -63,3 +63,9 @@ ${dir_build}/logic.js: \ @ ${cmd_log} "logic | link …" @ ${cmd_mkdir} $(dir $@) @ ${cmd_cat} $^ > $@ + +.PHONY: data +data: + @ ${cmd_log} "data …" + @ ${cmd_mkdir} ${dir_build}/data + @ ${cmd_cp} -r -u -v ${dir_source}/data/* ${dir_build}/data/