frontend-dali/source/widgets/weekview/logic.ts

875 lines
22 KiB
TypeScript

namespace _zeitbild.frontend_web.widgets.weekview
{
/**
*/
export class class_widget_weekview extends _zeitbild.class_widget
{
/**
*/
private container : (null | Element);
/**
*/
private action_select_day : (
(
date : lib_plankton.pit.type_date
)
=>
void
);
/**
*/
private action_select_event : (
(
calendar_id : _zeitbild.frontend_web.type.calendar_id,
access_level : _zeitbild.frontend_web.type.enum_access_level,
event_id : _zeitbild.frontend_web.type.local_resource_event_id
)
=>
void
);
/**
*/
public constructor(
options : {
action_select_day ?: (
(
date : lib_plankton.pit.type_date
)
=>
void
);
action_select_event ?: (
(
calendar_id : _zeitbild.frontend_web.type.calendar_id,
access_level : _zeitbild.frontend_web.type.enum_access_level,
event_id : _zeitbild.frontend_web.type.local_resource_event_id
)
=>
void
);
} = {}
)
{
options = Object.assign(
{
"action_select_day": (date) => {},
"action_select_event": (calendar_id, access_level, event_id) => {},
},
options
);
super();
this.container = null;
this.action_select_day = options.action_select_day;
this.action_select_event = options.action_select_event;
}
/**
*/
private static event_generate_tooltip(
calendar_name : string,
event_object : _zeitbild.frontend_web.type.event_object
) : string
{
return (
lib_plankton.string.coin(
"[{{calendar_name}}] {{event_name}}\n",
{
"calendar_name": calendar_name,
"event_name": event_object.name,
}
)
+
"--\n"
+
(
(event_object.begin.time !== null)
?
lib_plankton.string.coin(
"{{label}}: {{value}}\n",
{
"label": lib_plankton.translate.get("event.begin"),
"value": lib_plankton.string.coin(
"{{hour}}:{{minute}}",
{
"hour": event_object.begin.time.hour.toFixed(0).padStart(2, "0"),
"minute": event_object.begin.time.minute.toFixed(0).padStart(2, "0"),
}
), // TODO: outsource
}
)
:
""
)
+
(
(event_object.end !== null)
?
lib_plankton.string.coin(
"{{label}}: {{value}}\n",
{
"label": lib_plankton.translate.get("event.end"),
"value": (
[
(
(
(event_object.end.date.year !== event_object.begin.date.year)
||
(event_object.end.date.month !== event_object.begin.date.month)
||
(event_object.end.date.day !== event_object.begin.date.day)
)
?
lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": event_object.end.date.year.toFixed(0).padStart(4, "0"),
"month": event_object.end.date.month.toFixed(0).padStart(2, "0"),
"day": event_object.end.date.day.toFixed(0).padStart(2, "0"),
}
)
:
null
),
(
(event_object.end.time !== null)
?
lib_plankton.string.coin(
"{{hour}}:{{minute}}",
{
"hour": event_object.end.time.hour.toFixed(0).padStart(2, "0"),
"minute": event_object.end.time.minute.toFixed(0).padStart(2, "0"),
}
)
:
null
),
]
.filter(x => (x !== null))
.join(",")
),
}
)
:
""
)
+
(
(event_object.location !== null)
?
(
lib_plankton.string.coin(
"{{label}}: {{value}}\n",
{
"label": lib_plankton.translate.get("event.location"),
"value": event_object.location,
}
)
)
:
""
)
/*
+
(
(event_object.description !== null)
?
(
"--\n"
+
lib_plankton.string.coin(
"{{description}}\n",
{
"description": event_object.description,
}
)
)
:
""
)
*/
);
}
/**
* @todo kein "while"
*/
private static async calendar_view_table_data(
calendar_ids : (
null
|
Array<_zeitbild.frontend_web.type.calendar_id>
),
from : {
year : int;
week : int;
},
to : {
year : int;
week : int;
},
timezone_shift : int
) : Promise<
{
sources : lib_plankton.map.type_map<
_zeitbild.frontend_web.type.calendar_id,
{
name : string;
access_level : _zeitbild.frontend_web.type.enum_access_level;
}
>;
rows : Array<
{
week : int;
data : Array<
{
pit : lib_plankton.pit.type_pit;
entries : Array<
{
calendar_id : _zeitbild.frontend_web.type.calendar_id;
event_id : (null | _zeitbild.frontend_web.type.local_resource_event_id);
event_object : _zeitbild.frontend_web.type.event_object;
}
>;
today : boolean;
}
>;
}
>
}
>
{
const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now();
const from_pit : lib_plankton.pit.type_pit = lib_plankton.pit.from_ywd(
{
"year": (from as {year : int; week : int}).year,
"week": (from as {year : int; week : int}).week,
"day": 1,
},
{
"timezone_shift": (timezone_shift as int),
}
);
const to_pit : lib_plankton.pit.type_pit = lib_plankton.pit.from_ywd(
{
"year": (to as {year : int; week : int}).year,
"week": (to as {year : int; week : int}).week,
"day": 1,
},
{
"timezone_shift": (timezone_shift as int),
}
);
// prepare
const entries : Array<
{
calendar_id : _zeitbild.frontend_web.type.calendar_id;
calendar_name : string;
access_level : _zeitbild.frontend_web.type.enum_access_level;
event_id : (null | _zeitbild.frontend_web.type.local_resource_event_id);
event_object : _zeitbild.frontend_web.type.event_object;
}
> = await _zeitbild.frontend_web.backend.events(
from_pit,
to_pit,
{
"calendar_ids": calendar_ids,
}
);
let result : {
sources : lib_plankton.map.type_map<
_zeitbild.frontend_web.type.calendar_id,
{
name : string;
access_level : _zeitbild.frontend_web.type.enum_access_level;
}
>;
rows : Array<
{
week : int;
data : Array<
{
pit : lib_plankton.pit.type_pit;
entries : Array<
{
calendar_id : _zeitbild.frontend_web.type.calendar_id;
event_id : (null | _zeitbild.frontend_web.type.local_resource_event_id);
event_object : _zeitbild.frontend_web.type.event_object;
}
>;
today : boolean;
}
>;
}
>;
} = {
"sources": lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(
x => x.toFixed(0),
{
"pairs": (
entries
.map(
(entry) => (
{
"key": entry.calendar_id,
"value": {
"name": entry.calendar_name,
"access_level": entry.access_level,
}
}
)
)
)
}
)
),
"rows": [],
};
let row : Array<
{
pit : lib_plankton.pit.type_pit;
entries : Array<
{
calendar_id : _zeitbild.frontend_web.type.calendar_id;
event_id : (null | _zeitbild.frontend_web.type.local_resource_event_id);
event_object : _zeitbild.frontend_web.type.event_object;
}
>;
today : boolean;
}
> = [];
let day : int = 0;
while (true) {
const pit_current : lib_plankton.pit.type_pit = lib_plankton.pit.shift_day(
from_pit,
day
);
if (
lib_plankton.pit.is_before(
pit_current,
to_pit
)
) {
day += 1;
row.push(
{
"pit": pit_current,
"entries": [],
"today": false, // TODO
}
);
if (day % 7 === 0) {
result.rows.push(
{
"week": (
(from as {year : int; week : int}).week
+
Math.floor(day / 7)
-
1 // TODO
),
"data": row
}
);
row = [];
}
else {
// do nothing
}
}
else {
break;
}
}
// fill
{
// events
(
entries
.forEach(
(entry) => {
const distance_seconds : int = (
lib_plankton.pit.from_datetime(entry.event_object.begin)
-
from_pit
);
const distance_days : int = (distance_seconds / (60 * 60 * 24));
const week : int = Math.floor(Math.floor(distance_days) / 7);
const day : int = (Math.floor(distance_days) % 7);
if ((week >= 0) && (week < result.rows.length)) {
result.rows[week].data[day].entries.push(entry);
}
else {
// do nothing
}
}
)
);
// today
{
const distance_seconds : int = (
now_pit
-
from_pit
);
const distance_days : int = (distance_seconds / (60 * 60 * 24));
const week : int = Math.floor(Math.floor(distance_days) / 7);
const day : int = (Math.floor(distance_days) % 7);
if ((week >= 0) && (week < result.rows.length)) {
result.rows[week].data[day].today = true;
}
else {
// do nothing
}
}
}
return Promise.resolve(result);
}
/**
*/
private async table_rows(
options : {
calendar_ids ?: (
null
|
Array<_zeitbild.frontend_web.type.calendar_id>
);
from ?: {
year : int;
week : int;
};
to ?: {
year : int;
week : int;
};
timezone_shift ?: int;
action_select ?: (
(
calendar_id : _zeitbild.frontend_web.type.calendar_id,
event_id : _zeitbild.frontend_web.type.local_resource_event_id
)
=>
void
)
} = {}
) : Promise<string>
{
const now_pit : lib_plankton.pit.type_pit = lib_plankton.pit.now();
options = Object.assign(
{
"calendar_ids": null,
"from": lib_plankton.call.convey(
now_pit,
[
(x : lib_plankton.pit.type_pit) => lib_plankton.pit.shift_week(x, -1),
lib_plankton.pit.to_ywd,
x => ({"year": x.year, "week": x.week}),
]
),
"to": lib_plankton.call.convey(
now_pit,
[
(x : lib_plankton.pit.type_pit) => lib_plankton.pit.shift_week(x, +4),
lib_plankton.pit.to_ywd,
x => ({"year": x.year, "week": x.week}),
]
),
"timezone_shift": 0,
},
options
);
const stuff : {
sources : lib_plankton.map.type_map<
_zeitbild.frontend_web.type.calendar_id,
{
name : string;
access_level : _zeitbild.frontend_web.type.enum_access_level;
}
>;
rows : Array<
{
week : int;
data : Array<
{
pit : lib_plankton.pit.type_pit;
entries : Array<
{
calendar_id : _zeitbild.frontend_web.type.calendar_id;
event_id : (null | _zeitbild.frontend_web.type.local_resource_event_id);
event_object : _zeitbild.frontend_web.type.event_object;
}
>;
today : boolean;
}
>;
}
>;
} = await class_widget_weekview.calendar_view_table_data(
options.calendar_ids,
options.from,
options.to,
options.timezone_shift
);
const sources : lib_plankton.map.type_map<
_zeitbild.frontend_web.type.calendar_id,
{
name : string;
access_level : _zeitbild.frontend_web.type.enum_access_level;
color : lib_plankton.color.type_color;
}
> = lib_plankton.map.hashmap.implementation_map(
lib_plankton.map.hashmap.make(
(x => x.toFixed(0)),
{
"pairs": (
lib_plankton.map.dump(
stuff.sources
)
.map(
(pair) => ({
"key": pair.key,
"value": {
"name": pair.value.name,
"access_level": pair.value.access_level,
"color": lib_plankton.color.give_generic(
(pair.key - 1),
{
"saturation": 0.375,
"value": 0.375,
}
),
}
})
)
)
}
)
);
return (
await _zeitbild.frontend_web.helpers.promise_row<string>(
stuff.rows
.map(
(row) => async () => _zeitbild.frontend_web.helpers.template_coin(
"widget-weekview",
"tableview-row",
{
"week": row.week.toFixed(0).padStart(2, "0"),
"cells": (
await _zeitbild.frontend_web.helpers.promise_row<string>(
row.data
.map(
(cell) => async () => _zeitbild.frontend_web.helpers.template_coin(
"widget-weekview",
"tableview-cell",
{
"extra_classes": (
[""]
.concat(cell.today ? ["calendar-cell-today"] : [])
.join(" ")
),
"title": lib_plankton.call.convey(
cell.pit,
[
lib_plankton.pit.to_datetime,
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": x.date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
),
]
),
"day": lib_plankton.call.convey(
cell.pit,
[
lib_plankton.pit.to_datetime,
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
"{{day}}",
{
"year": x.date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
),
]
),
"rel": lib_plankton.call.convey(
cell.pit,
[
lib_plankton.pit.to_datetime,
(x : lib_plankton.pit.type_datetime) => lib_plankton.string.coin(
"{{year}}-{{month}}-{{day}}",
{
"year": x.date.year.toFixed(0).padStart(4, "0"),
"month": x.date.month.toFixed(0).padStart(2, "0"),
"day": x.date.day.toFixed(0).padStart(2, "0"),
}
)
]
),
"entries": (
await _zeitbild.frontend_web.helpers.promise_row<string>(
cell.entries
.map(
(entry) => () => _zeitbild.frontend_web.helpers.template_coin(
"widget-weekview",
"tableview-cell-entry",
{
"color": lib_plankton.color.output_hex(
sources.get(
entry.calendar_id
).color
),
"title": class_widget_weekview.event_generate_tooltip(
sources.get(
entry.calendar_id
).name,
entry.event_object
),
"name": entry.event_object.name,
"rel": lib_plankton.string.coin(
"{{calendar_id}}/{{event_id}}/{{access_level}}",
{
"calendar_id": entry.calendar_id.toFixed(0),
"event_id": (
(entry.event_id === null)
?
"-"
:
entry.event_id.toFixed(0)
),
"access_level": (() => {
const access_level : _zeitbild.frontend_web.type.enum_access_level = sources.get(entry.calendar_id).access_level;
switch (access_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";
}
}) (),
}
),
"additional_classes": lib_plankton.string.coin(
" access_level-{{access_level}}",
{
"access_level": (() => {
const access_level : _zeitbild.frontend_web.type.enum_access_level = sources.get(entry.calendar_id).access_level;
switch (access_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";
}
}) (),
}
),
}
)
)
)
).join(""),
}
)
)
)
).join(""),
}
)
)
)
).join("");
}
/**
*/
private async update(
year : int,
week : int,
count : int,
options : {
update_controls ?: boolean;
} = {}
) : Promise<void>
{
options = Object.assign(
{
"update_controls": true,
},
options
);
const context : Element = this.container;
// controls
{
if (! options.update_controls) {
// do nothing
}
else {
(context.querySelector(".weekview-control-year > input") as HTMLInputElement).value = year.toFixed(0);
(context.querySelector(".weekview-control-week > input") as HTMLInputElement).value = week.toFixed(0);
(context.querySelector(".weekview-control-count > input") as HTMLInputElement).value = count.toFixed(0);
}
}
// table
{
context.querySelector(".weekview-table tbody").innerHTML = await this.table_rows(
{
"calendar_ids": null,
// TODO
"from": {
"year": year,
"week": week
},
// TODO
"to": {
"year": year,
"week": (week + count)
},
"timezone_shift": /*conf.timezone_shift*/0,
}
);
// cells
{
context.querySelectorAll(".calendar-cell-regular").forEach(
(element) => {
element.addEventListener(
"click",
(event) => {
if (! (element === event.target)) {
// do nothing
}
else {
const rel : string = element.getAttribute("rel");
const parts : Array<string> = rel.split("-");
const date : lib_plankton.pit.type_date = {
"year": parseInt(parts[0]),
"month": parseInt(parts[1]),
"day": parseInt(parts[2]),
};
this.action_select_day(date);
}
}
);
}
);
}
// events
{
context.querySelectorAll(".calendar-event_entry").forEach(
(element) => {
element.addEventListener(
"click",
() => {
const rel : string = element.getAttribute("rel");
const parts : Array<string> = rel.split("/");
const calendar_id : _zeitbild.frontend_web.type.calendar_id = parseInt(parts[0]);
const event_id : (null | _zeitbild.frontend_web.type.local_resource_event_id) = (
(parts[1] === "-")
?
null
:
parseInt(parts[1])
);
const access_level : _zeitbild.frontend_web.type.enum_access_level = (() => {
switch (parts[2]) {
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;
}
}) ();
this.action_select_event(
calendar_id,
access_level,
event_id
);
}
);
}
);
}
}
return Promise.resolve<void>(undefined);
}
/**
* [implementation]
*/
public async load(
target_element : Element
) : Promise<void>
{
target_element.innerHTML = await _zeitbild.frontend_web.helpers.template_coin(
"widget-weekview",
"main",
{
"label_control_year": lib_plankton.translate.get("widget.weekview.controls.year"),
"label_control_week": lib_plankton.translate.get("widget.weekview.controls.week"),
"label_control_count": lib_plankton.translate.get("widget.weekview.controls.count"),
"label_control_apply": lib_plankton.translate.get("widget.weekview.controls.apply"),
}
);
this.container = target_element.querySelector(".weekview");
// controls
{
target_element.querySelector(".weekview-control-apply").addEventListener(
"select",
(event) => {
event.preventDefault();
const year : int = parseInt((target_element.querySelector(".weekview-control-year > input") as HTMLInputElement).value);
const week : int = parseInt((target_element.querySelector(".weekview-control-week > input") as HTMLInputElement).value);
const count : int = parseInt((target_element.querySelector(".weekview-control-count > input") as HTMLInputElement).value);
this.update(
year,
week,
count,
{
"update_controls": false,
}
);
}
);
}
// table
{
const ywd_now : lib_plankton.pit.type_ywd = lib_plankton.pit.to_ywd(lib_plankton.pit.now());
let year : int = ywd_now.year;
let week : int = Math.max(0, (ywd_now.week - 1));
let count : int = 5;
await this.update(
year,
week,
count,
{
"update_controls": true,
}
);
}
return Promise.resolve<void>(undefined);
}
}
}