add item status
This commit is contained in:
Vendored
+4
@@ -7,6 +7,10 @@
|
|||||||
--thread-view-width-with-tools: 20rem;
|
--thread-view-width-with-tools: 20rem;
|
||||||
--footer-top-line: calc(var(--control-height) + var(--safe-area-bottom));
|
--footer-top-line: calc(var(--control-height) + var(--safe-area-bottom));
|
||||||
}
|
}
|
||||||
|
.object-entry-wide {
|
||||||
|
width: 280px;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
#message-tab {
|
#message-tab {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+183
-32
@@ -495,6 +495,7 @@
|
|||||||
viewAll: "All",
|
viewAll: "All",
|
||||||
viewNotes: "Notes",
|
viewNotes: "Notes",
|
||||||
viewKanban: "Kanban",
|
viewKanban: "Kanban",
|
||||||
|
viewStatus: "Status",
|
||||||
noObjects: "No objects",
|
noObjects: "No objects",
|
||||||
objectTitle: "Object title",
|
objectTitle: "Object title",
|
||||||
objectTitlePlaceholder: "My object",
|
objectTitlePlaceholder: "My object",
|
||||||
@@ -502,9 +503,11 @@
|
|||||||
note: "Note",
|
note: "Note",
|
||||||
noteContentPlaceholder: "Take a note...",
|
noteContentPlaceholder: "Take a note...",
|
||||||
category: "Category",
|
category: "Category",
|
||||||
|
categoryPlaceholder: "Define a category",
|
||||||
priority: "Priority",
|
priority: "Priority",
|
||||||
priorityPlaceholder: "2",
|
priorityPlaceholder: "2",
|
||||||
categoryPlaceholder: "Define a category",
|
status: "Status",
|
||||||
|
statusPlaceholder: "Pending",
|
||||||
date: "Date",
|
date: "Date",
|
||||||
time: "Time",
|
time: "Time",
|
||||||
resendObjects: "Resend all objects",
|
resendObjects: "Resend all objects",
|
||||||
@@ -582,6 +585,7 @@
|
|||||||
viewAll: "Todos",
|
viewAll: "Todos",
|
||||||
viewNotes: "Notas",
|
viewNotes: "Notas",
|
||||||
viewKanban: "Kanban",
|
viewKanban: "Kanban",
|
||||||
|
viewStatus: "Estado",
|
||||||
noObjects: "Sin objetos",
|
noObjects: "Sin objetos",
|
||||||
objectTitle: "T\xEDtulo",
|
objectTitle: "T\xEDtulo",
|
||||||
objectTitlePlaceholder: "Mi objeto",
|
objectTitlePlaceholder: "Mi objeto",
|
||||||
@@ -589,9 +593,11 @@
|
|||||||
note: "Nota",
|
note: "Nota",
|
||||||
noteContentPlaceholder: "Toma nota...",
|
noteContentPlaceholder: "Toma nota...",
|
||||||
category: "Categor\xEDa",
|
category: "Categor\xEDa",
|
||||||
|
categoryPlaceholder: "Define una categor\xEDa",
|
||||||
priority: "Prioridad",
|
priority: "Prioridad",
|
||||||
priorityPlaceholder: "2",
|
priorityPlaceholder: "2",
|
||||||
categoryPlaceholder: "Define una categor\xEDa",
|
status: "Estado",
|
||||||
|
statusPlaceholder: "Pendiente",
|
||||||
date: "Fecha",
|
date: "Fecha",
|
||||||
time: "Hora",
|
time: "Hora",
|
||||||
resendObjects: "Reenviar todos objetos",
|
resendObjects: "Reenviar todos objetos",
|
||||||
@@ -667,6 +673,7 @@
|
|||||||
viewAll: "Alle",
|
viewAll: "Alle",
|
||||||
viewNotes: "Notizen",
|
viewNotes: "Notizen",
|
||||||
viewKanban: "Kanban",
|
viewKanban: "Kanban",
|
||||||
|
viewStatus: "Status",
|
||||||
noObjects: "Keine Objekte",
|
noObjects: "Keine Objekte",
|
||||||
objectTitle: "Titel",
|
objectTitle: "Titel",
|
||||||
objectTitlePlaceholder: "Mein Objekt",
|
objectTitlePlaceholder: "Mein Objekt",
|
||||||
@@ -674,9 +681,11 @@
|
|||||||
note: "Notiz",
|
note: "Notiz",
|
||||||
noteContentPlaceholder: "Notiz eingeben...",
|
noteContentPlaceholder: "Notiz eingeben...",
|
||||||
category: "Kategorie",
|
category: "Kategorie",
|
||||||
|
categoryPlaceholder: "Kategorie eingeben...",
|
||||||
priority: "Priorit\xE4t",
|
priority: "Priorit\xE4t",
|
||||||
priorityPlaceholder: "2",
|
priorityPlaceholder: "2",
|
||||||
categoryPlaceholder: "Kategorie eingeben...",
|
status: "Status",
|
||||||
|
statusPlaceholder: "Ausstehend",
|
||||||
date: "Datum",
|
date: "Datum",
|
||||||
time: "Uhrzeit",
|
time: "Uhrzeit",
|
||||||
resendObjects: "Objekte erneut senden",
|
resendObjects: "Objekte erneut senden",
|
||||||
@@ -1398,6 +1407,7 @@
|
|||||||
objectHistory: "history",
|
objectHistory: "history",
|
||||||
noteContent: "sticky_note_2",
|
noteContent: "sticky_note_2",
|
||||||
categoryName: "category",
|
categoryName: "category",
|
||||||
|
status: "trending_up",
|
||||||
priority: "priority_high",
|
priority: "priority_high",
|
||||||
date: "calendar_month",
|
date: "calendar_month",
|
||||||
time: "schedule"
|
time: "schedule"
|
||||||
@@ -1416,7 +1426,7 @@
|
|||||||
const fields = {
|
const fields = {
|
||||||
[icons.noteContent]: "---",
|
[icons.noteContent]: "---",
|
||||||
[icons.priority]: "---",
|
[icons.priority]: "---",
|
||||||
[icons.categoryName]: "---",
|
[icons.status]: "---",
|
||||||
[icons.date]: "---",
|
[icons.date]: "---",
|
||||||
[icons.time]: "---"
|
[icons.time]: "---"
|
||||||
};
|
};
|
||||||
@@ -1425,6 +1435,7 @@
|
|||||||
if (!value) return;
|
if (!value) return;
|
||||||
const icon = icons[key];
|
const icon = icons[key];
|
||||||
if (!icon) return;
|
if (!icon) return;
|
||||||
|
if (!fields[icon]) return;
|
||||||
fields[icon] = value;
|
fields[icon] = value;
|
||||||
});
|
});
|
||||||
const fieldElements = Object.entries(fields).map((field) => {
|
const fieldElements = Object.entries(fields).map((field) => {
|
||||||
@@ -1439,7 +1450,7 @@
|
|||||||
draggable: "true",
|
draggable: "true",
|
||||||
"on:dragstart": dragStart
|
"on:dragstart": dragStart
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, chat.getObjectTitle(messageObject)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("span", { class: "flex-column gap height-100 flex secondary ellipsis " }, ...fieldElements))
|
/* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", { class: "ellipsis" }, chat.getObjectTitle(messageObject)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("span", { class: "flex-column gap height-100 flex secondary ellipsis " }, ...fieldElements))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1482,6 +1493,24 @@
|
|||||||
return /* @__PURE__ */ createElement("div", { class: "width-100 height-100", "children:set": content });
|
return /* @__PURE__ */ createElement("div", { class: "width-100 height-100", "children:set": content });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/Views/renameView.tsx
|
||||||
|
function RenameView(editingName, initialName, rename) {
|
||||||
|
const cannotRename = createProxyState(
|
||||||
|
[editingName],
|
||||||
|
() => editingName.value == initialName || editingName.value == ""
|
||||||
|
);
|
||||||
|
return /* @__PURE__ */ createElement("div", { class: "flex-row align-center justify-apart object-entry-wide" }, /* @__PURE__ */ createElement("input", { "bind:value": editingName, "on:enter": rename }), /* @__PURE__ */ createElement(
|
||||||
|
"button",
|
||||||
|
{
|
||||||
|
class: "primary",
|
||||||
|
"aria-label": translation.rename,
|
||||||
|
"on:click": rename,
|
||||||
|
"toggle:disabled": cannotRename
|
||||||
|
},
|
||||||
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "edit")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// src/Views/Objects/kanbanView.tsx
|
// src/Views/Objects/kanbanView.tsx
|
||||||
function KanbanView(chat, selectedObject, isShowingObjectModal) {
|
function KanbanView(chat, selectedObject, isShowingObjectModal) {
|
||||||
const boards = new MapState();
|
const boards = new MapState();
|
||||||
@@ -1492,7 +1521,7 @@
|
|||||||
if (!latest.categoryName) return;
|
if (!latest.categoryName) return;
|
||||||
const boardTitle = latest.categoryName;
|
const boardTitle = latest.categoryName;
|
||||||
if (!boards.value.has(boardTitle))
|
if (!boards.value.has(boardTitle))
|
||||||
boards.set(boardTitle, { title: boardTitle, items: [] });
|
boards.set(boardTitle, { category: boardTitle, items: [] });
|
||||||
boards.value.get(boardTitle)?.items.push({
|
boards.value.get(boardTitle)?.items.push({
|
||||||
priority: latest.priority ?? 0,
|
priority: latest.priority ?? 0,
|
||||||
messageObject
|
messageObject
|
||||||
@@ -1501,24 +1530,19 @@
|
|||||||
});
|
});
|
||||||
const content = createProxyState(
|
const content = createProxyState(
|
||||||
[chat.objects],
|
[chat.objects],
|
||||||
() => boards.value.size == 0 ? PlaceholderView() : /* @__PURE__ */ createElement("div", { class: "flex-row large-gap width-100 height-100 scroll-v scroll-h padding" }, ...[...boards.value.values()].sort((a, b) => a.title.localeCompare(b.title)).map(
|
() => boards.value.size == 0 ? PlaceholderView() : /* @__PURE__ */ createElement("div", { class: "flex-row large-gap width-100 height-100 scroll-v scroll-h padding" }, ...[...boards.value.values()].sort((a, b) => a.category.localeCompare(b.category)).map(
|
||||||
(board) => KanbanBoardView(chat, board, selectedObject, isShowingObjectModal)
|
(board) => KanbanBoardView(chat, board, selectedObject, isShowingObjectModal)
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
return /* @__PURE__ */ createElement("div", { class: "width-100 height-100 scroll-no", "children:set": content });
|
return /* @__PURE__ */ createElement("div", { class: "width-100 height-100 scroll-no", "children:set": content });
|
||||||
}
|
}
|
||||||
function KanbanBoardView(chat, kanbanBoard, selectedObject, isShowingObjectModal) {
|
function KanbanBoardView(chat, kanbanBoard, selectedObject, isShowingObjectModal) {
|
||||||
const editingBoardTitle = new State(kanbanBoard.title);
|
const editingCategory = new State(kanbanBoard.category);
|
||||||
const isEditing = new State(false);
|
|
||||||
const cannotRename = createProxyState(
|
|
||||||
[editingBoardTitle],
|
|
||||||
() => editingBoardTitle.value == kanbanBoard.title || editingBoardTitle.value == ""
|
|
||||||
);
|
|
||||||
function renameBoard() {
|
function renameBoard() {
|
||||||
kanbanBoard.items.forEach((kanbanBoardItem) => {
|
kanbanBoard.items.forEach((kanbanBoardItem) => {
|
||||||
const { messageObject } = kanbanBoardItem;
|
const { messageObject } = kanbanBoardItem;
|
||||||
const latest = chat.getMostRecentContent(messageObject);
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
latest.categoryName = editingBoardTitle.value;
|
latest.categoryName = editingCategory.value;
|
||||||
chat.addObjectAndSend(messageObject);
|
chat.addObjectAndSend(messageObject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1532,28 +1556,17 @@
|
|||||||
const messageObject = chat.objects.value.get(id);
|
const messageObject = chat.objects.value.get(id);
|
||||||
if (!messageObject) return;
|
if (!messageObject) return;
|
||||||
const latest = chat.getMostRecentContent(messageObject);
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
latest.categoryName = kanbanBoard.title;
|
latest.categoryName = kanbanBoard.category;
|
||||||
chat.addObjectAndSend(messageObject);
|
chat.addObjectAndSend(messageObject);
|
||||||
}
|
}
|
||||||
const titleInput = /* @__PURE__ */ createElement("input", { "bind:value": editingBoardTitle, "on:enter": renameBoard });
|
|
||||||
return /* @__PURE__ */ createElement(
|
return /* @__PURE__ */ createElement(
|
||||||
"div",
|
"div",
|
||||||
{
|
{
|
||||||
class: "flex-column flex-no",
|
class: "flex-column flex-no object-entry-wide",
|
||||||
style: "min-width: 280px",
|
|
||||||
"on:dragover": dragOver,
|
"on:dragover": dragOver,
|
||||||
"on:drop": drop
|
"on:drop": drop
|
||||||
},
|
},
|
||||||
/* @__PURE__ */ createElement("div", { class: "flex-row align-center justify-apart" }, titleInput, /* @__PURE__ */ createElement(
|
RenameView(editingCategory, kanbanBoard.category, renameBoard),
|
||||||
"button",
|
|
||||||
{
|
|
||||||
class: "primary",
|
|
||||||
"aria-label": translation.rename,
|
|
||||||
"on:click": renameBoard,
|
|
||||||
"toggle:disabled": cannotRename
|
|
||||||
},
|
|
||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "edit")
|
|
||||||
)),
|
|
||||||
/* @__PURE__ */ createElement("hr", null),
|
/* @__PURE__ */ createElement("hr", null),
|
||||||
/* @__PURE__ */ createElement("div", { class: "flex-column gap padding-bottom" }, ...kanbanBoard.items.sort((a, b) => b.priority - a.priority).map(
|
/* @__PURE__ */ createElement("div", { class: "flex-column gap padding-bottom" }, ...kanbanBoard.items.sort((a, b) => b.priority - a.priority).map(
|
||||||
(kanbanBoardItem) => ObjectEntryView(
|
(kanbanBoardItem) => ObjectEntryView(
|
||||||
@@ -1608,6 +1621,10 @@
|
|||||||
[selectedMessageObject],
|
[selectedMessageObject],
|
||||||
() => selectedMessageObject.value.categoryName ?? ""
|
() => selectedMessageObject.value.categoryName ?? ""
|
||||||
);
|
);
|
||||||
|
const editingStatus = createProxyState(
|
||||||
|
[selectedMessageObject],
|
||||||
|
() => selectedMessageObject.value.status ?? ""
|
||||||
|
);
|
||||||
const editingDate = createProxyState(
|
const editingDate = createProxyState(
|
||||||
[selectedMessageObject],
|
[selectedMessageObject],
|
||||||
() => selectedMessageObject.value.date ?? ""
|
() => selectedMessageObject.value.date ?? ""
|
||||||
@@ -1624,6 +1641,7 @@
|
|||||||
[
|
[
|
||||||
editingNoteContent,
|
editingNoteContent,
|
||||||
editingCategory,
|
editingCategory,
|
||||||
|
editingStatus,
|
||||||
editingDate,
|
editingDate,
|
||||||
editingTime,
|
editingTime,
|
||||||
editingPriority
|
editingPriority
|
||||||
@@ -1651,6 +1669,7 @@
|
|||||||
noteContent: editingNoteContent.value,
|
noteContent: editingNoteContent.value,
|
||||||
priority: editingPriority.value,
|
priority: editingPriority.value,
|
||||||
categoryName: editingCategory.value,
|
categoryName: editingCategory.value,
|
||||||
|
status: editingStatus.value,
|
||||||
date: editingDate.value,
|
date: editingDate.value,
|
||||||
time: editingTime.value
|
time: editingTime.value
|
||||||
});
|
});
|
||||||
@@ -1675,12 +1694,12 @@
|
|||||||
).toLocaleString()))), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.noteContent), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.note), /* @__PURE__ */ createElement(
|
).toLocaleString()))), /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_drop_down"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.noteContent), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.note), /* @__PURE__ */ createElement(
|
||||||
"textarea",
|
"textarea",
|
||||||
{
|
{
|
||||||
rows: "5",
|
rows: "10",
|
||||||
class: "height-auto",
|
class: "height-auto",
|
||||||
"bind:value": editingNoteContent,
|
"bind:value": editingNoteContent,
|
||||||
placeholder: translation.noteContentPlaceholder
|
placeholder: translation.noteContentPlaceholder
|
||||||
}
|
}
|
||||||
))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.categoryName), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.category), /* @__PURE__ */ createElement(
|
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.categoryName), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.category), /* @__PURE__ */ createElement(
|
||||||
"input",
|
"input",
|
||||||
{
|
{
|
||||||
"bind:value": editingCategory,
|
"bind:value": editingCategory,
|
||||||
@@ -1701,14 +1720,138 @@
|
|||||||
"bind:value": editingPriority,
|
"bind:value": editingPriority,
|
||||||
placeholder: translation.priorityPlaceholder
|
placeholder: translation.priorityPlaceholder
|
||||||
}
|
}
|
||||||
|
))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.status), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.status), /* @__PURE__ */ createElement(
|
||||||
|
"input",
|
||||||
|
{
|
||||||
|
"bind:value": editingStatus,
|
||||||
|
placeholder: translation.statusPlaceholder
|
||||||
|
}
|
||||||
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.date), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.date), /* @__PURE__ */ createElement("input", { type: "date", "bind:value": editingDate }))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.time), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.time), /* @__PURE__ */ createElement("input", { type: "time", "bind:value": editingTime }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("button", { class: "danger width-input", "on:click": deleteAndClose }, translation.deleteObject, /* @__PURE__ */ createElement("span", { class: "icon" }, "delete")))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("button", { class: "flex-1 width-100 danger", "on:click": closeModal }, translation.discard), /* @__PURE__ */ createElement("button", { class: "flex-1 width-100 primary", "on:click": saveAndClose }, translation.save, /* @__PURE__ */ createElement("span", { class: "icon" }, "save")))));
|
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.date), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.date), /* @__PURE__ */ createElement("input", { type: "date", "bind:value": editingDate }))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, icons.time), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translation.time), /* @__PURE__ */ createElement("input", { type: "time", "bind:value": editingTime }))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("button", { class: "danger width-input", "on:click": deleteAndClose }, translation.deleteObject, /* @__PURE__ */ createElement("span", { class: "icon" }, "delete")))), /* @__PURE__ */ createElement("div", { class: "flex-row" }, /* @__PURE__ */ createElement("button", { class: "flex-1 width-100 danger", "on:click": closeModal }, translation.discard), /* @__PURE__ */ createElement("button", { class: "flex-1 width-100 primary", "on:click": saveAndClose }, translation.save, /* @__PURE__ */ createElement("span", { class: "icon" }, "save")))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// src/Views/Objects/statusView.tsx
|
||||||
|
function StatusView(chat, selectedObject, isShowingObjectModal) {
|
||||||
|
const rows = new MapState();
|
||||||
|
const statuses = /* @__PURE__ */ new Map();
|
||||||
|
chat.objects.subscribe(() => {
|
||||||
|
rows.clear();
|
||||||
|
statuses.clear();
|
||||||
|
chat.objects.value.forEach((messageObject) => {
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
if (!latest.categoryName || !latest.status) return;
|
||||||
|
const category = latest.categoryName;
|
||||||
|
const status = latest.status;
|
||||||
|
if (!statuses.has(status)) {
|
||||||
|
statuses.set(status, { title: status, items: [] });
|
||||||
|
}
|
||||||
|
statuses.get(status).items.push(messageObject);
|
||||||
|
if (!rows.value.has(category)) {
|
||||||
|
rows.set(category, { category, statusColumns: /* @__PURE__ */ new Map() });
|
||||||
|
}
|
||||||
|
const row = rows.value.get(category);
|
||||||
|
if (!row.statusColumns.has(status)) {
|
||||||
|
row.statusColumns.set(status, { title: status, items: [] });
|
||||||
|
}
|
||||||
|
const column = row.statusColumns.get(status);
|
||||||
|
column.items.push({
|
||||||
|
priority: latest.priority ?? 0,
|
||||||
|
status,
|
||||||
|
messageObject
|
||||||
|
});
|
||||||
|
});
|
||||||
|
statuses.forEach((statusItemList) => {
|
||||||
|
rows.value.forEach((row) => {
|
||||||
|
if (row.statusColumns.has(statusItemList.title)) return;
|
||||||
|
row.statusColumns.set(statusItemList.title, {
|
||||||
|
title: statusItemList.title,
|
||||||
|
items: []
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const content = createProxyState(
|
||||||
|
[chat.objects],
|
||||||
|
() => rows.value.size == 0 ? PlaceholderView() : /* @__PURE__ */ createElement("div", { class: "flex-column large-gap width-100 height-100 scroll-v scroll-h padding" }, /* @__PURE__ */ createElement("div", { class: "flex-row large-gap" }, /* @__PURE__ */ createElement("div", { class: "flex-column object-entry-wide" }), ...[...statuses.values()].sort((a, b) => a.title.localeCompare(b.title)).map((status) => StatusHeaderView(chat, status))), ...[...rows.value.values()].sort((a, b) => a.category.localeCompare(b.category)).map(
|
||||||
|
(board) => KanbanRowView(chat, board, selectedObject, isShowingObjectModal)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
return /* @__PURE__ */ createElement("div", { class: "width-100 height-100 scroll-no", "children:set": content });
|
||||||
|
}
|
||||||
|
function StatusHeaderView(chat, status) {
|
||||||
|
const editingStatus = new State(status.title);
|
||||||
|
function rename() {
|
||||||
|
status.items.forEach((messageObject) => {
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
if (!latest) return;
|
||||||
|
latest.status = editingStatus.value;
|
||||||
|
chat.addObject(messageObject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return /* @__PURE__ */ createElement("div", { class: "object-entry-wide" }, RenameView(editingStatus, status.title, rename), /* @__PURE__ */ createElement("hr", null));
|
||||||
|
}
|
||||||
|
function KanbanRowView(chat, kanbanRow, selectedObject, isShowingObjectModal) {
|
||||||
|
const editingCategory = new State(kanbanRow.category);
|
||||||
|
const cellItemEntries = [...kanbanRow.statusColumns.entries()];
|
||||||
|
function renameCategory() {
|
||||||
|
kanbanRow.statusColumns.forEach((statusColumns) => {
|
||||||
|
statusColumns.items.forEach((statusCellItem) => {
|
||||||
|
const { messageObject } = statusCellItem;
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
latest.categoryName = editingCategory.value;
|
||||||
|
chat.addObjectAndSend(messageObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return /* @__PURE__ */ createElement("div", { class: "flex-row flex-no large-gap" }, /* @__PURE__ */ createElement("div", { class: "flex-row align-start" }, RenameView(editingCategory, kanbanRow.category, renameCategory)), ...cellItemEntries.sort((a, b) => a[0].localeCompare(b[0])).map(
|
||||||
|
(entry) => StatusColumnView(
|
||||||
|
chat,
|
||||||
|
entry[1].items,
|
||||||
|
kanbanRow.category,
|
||||||
|
entry[1].title,
|
||||||
|
selectedObject,
|
||||||
|
isShowingObjectModal
|
||||||
|
)
|
||||||
|
), /* @__PURE__ */ createElement("hr", null));
|
||||||
|
}
|
||||||
|
function StatusColumnView(chat, items, category, status, selectedObject, isShowingObjectModal) {
|
||||||
|
function dragOver(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
function drop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var id = event.dataTransfer?.getData("text");
|
||||||
|
if (!id) return;
|
||||||
|
const messageObject = chat.objects.value.get(id);
|
||||||
|
if (!messageObject) return;
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
latest.status = status;
|
||||||
|
latest.categoryName = category;
|
||||||
|
chat.addObjectAndSend(messageObject);
|
||||||
|
}
|
||||||
|
return /* @__PURE__ */ createElement(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "flex-column gap object-entry-wide",
|
||||||
|
"on:dragover": dragOver,
|
||||||
|
"on:drop": drop
|
||||||
|
},
|
||||||
|
...items.sort((a, b) => b.priority - a.priority).map(
|
||||||
|
(kanbanBoardItem) => ObjectEntryView(
|
||||||
|
chat,
|
||||||
|
kanbanBoardItem.messageObject,
|
||||||
|
selectedObject,
|
||||||
|
isShowingObjectModal
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// src/Views/Objects/objectPane.tsx
|
// src/Views/Objects/objectPane.tsx
|
||||||
var viewTypes = {
|
var viewTypes = {
|
||||||
all: [translation.viewAll, "grid_view"],
|
all: [translation.viewAll, "grid_view"],
|
||||||
notes: [translation.viewNotes, icons.noteContent],
|
notes: [translation.viewNotes, icons.noteContent],
|
||||||
kanban: [translation.viewKanban, "view_kanban"]
|
kanban: [translation.viewKanban, "view_kanban"],
|
||||||
|
status: [translation.viewStatus, icons.status]
|
||||||
};
|
};
|
||||||
function ObjectPane(chat) {
|
function ObjectPane(chat) {
|
||||||
const isShowingObjectModal = new State(false);
|
const isShowingObjectModal = new State(false);
|
||||||
@@ -1731,6 +1874,8 @@
|
|||||||
return NoteObjectsView;
|
return NoteObjectsView;
|
||||||
case "kanban":
|
case "kanban":
|
||||||
return KanbanView;
|
return KanbanView;
|
||||||
|
case "status":
|
||||||
|
return StatusView;
|
||||||
default:
|
default:
|
||||||
return AllObjectsView;
|
return AllObjectsView;
|
||||||
}
|
}
|
||||||
@@ -1753,7 +1898,13 @@
|
|||||||
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
/* @__PURE__ */ createElement("span", { class: "icon" }, "add")
|
||||||
), /* @__PURE__ */ createElement("div", { class: "padding-sm flex flex-row gap justify-center scroll-h width-100" }, ...Object.keys(viewTypes).map(
|
), /* @__PURE__ */ createElement("div", { class: "padding-sm flex flex-row gap justify-center scroll-h width-100" }, ...Object.keys(viewTypes).map(
|
||||||
(key) => ViewTypeToggle(key, chat.viewType)
|
(key) => ViewTypeToggle(key, chat.viewType)
|
||||||
)), /* @__PURE__ */ createElement("button", { class: "height-100", disabled: true }, /* @__PURE__ */ createElement("span", { class: "icon" }, "visibility"))), /* @__PURE__ */ createElement("div", { class: "width-100 height-100 flex scroll-no", "children:set": mainView }), /* @__PURE__ */ createElement("div", { "children:set": objectModal }));
|
)), /* @__PURE__ */ createElement("button", { class: "height-100", disabled: true }, /* @__PURE__ */ createElement("span", { class: "icon" }, "visibility"))), /* @__PURE__ */ createElement(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
class: "width-100 height-100 flex scroll-no",
|
||||||
|
"children:set": mainView
|
||||||
|
}
|
||||||
|
), /* @__PURE__ */ createElement("div", { "children:set": objectModal }));
|
||||||
}
|
}
|
||||||
function ViewTypeToggle(key, selection) {
|
function ViewTypeToggle(key, selection) {
|
||||||
const [label, icon] = viewTypes[key];
|
const [label, icon] = viewTypes[key];
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export interface MessageObjectContent {
|
|||||||
noteContent?: string;
|
noteContent?: string;
|
||||||
priority?: number;
|
priority?: number;
|
||||||
categoryName?: string;
|
categoryName?: string;
|
||||||
|
status?: string;
|
||||||
date?: string;
|
date?: string;
|
||||||
time?: string;
|
time?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import * as React from "bloatless-react";
|
|||||||
import { Chat, MessageObject } from "../../Model/chatModel";
|
import { Chat, MessageObject } from "../../Model/chatModel";
|
||||||
|
|
||||||
import { ObjectEntryView } from "./objectEntryView";
|
import { ObjectEntryView } from "./objectEntryView";
|
||||||
import { ObjectGridView } from "./objectGridView";
|
|
||||||
import { PlaceholderView } from "./placeholderView";
|
import { PlaceholderView } from "./placeholderView";
|
||||||
|
import { RenameView } from "../renameView";
|
||||||
import { translation } from "../../translations";
|
import { translation } from "../../translations";
|
||||||
|
|
||||||
interface KanbanBoard {
|
interface KanbanBoard {
|
||||||
title: string;
|
category: string;
|
||||||
items: KanbanBoardItem[];
|
items: KanbanBoardItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export function KanbanView(
|
|||||||
const boardTitle = latest.categoryName;
|
const boardTitle = latest.categoryName;
|
||||||
|
|
||||||
if (!boards.value.has(boardTitle))
|
if (!boards.value.has(boardTitle))
|
||||||
boards.set(boardTitle, { title: boardTitle, items: [] });
|
boards.set(boardTitle, { category: boardTitle, items: [] });
|
||||||
|
|
||||||
boards.value.get(boardTitle)?.items.push({
|
boards.value.get(boardTitle)?.items.push({
|
||||||
priority: latest.priority ?? 0,
|
priority: latest.priority ?? 0,
|
||||||
@@ -48,7 +48,7 @@ export function KanbanView(
|
|||||||
) : (
|
) : (
|
||||||
<div class="flex-row large-gap width-100 height-100 scroll-v scroll-h padding">
|
<div class="flex-row large-gap width-100 height-100 scroll-v scroll-h padding">
|
||||||
{...[...boards.value.values()]
|
{...[...boards.value.values()]
|
||||||
.sort((a, b) => a.title.localeCompare(b.title))
|
.sort((a, b) => a.category.localeCompare(b.category))
|
||||||
.map((board) =>
|
.map((board) =>
|
||||||
KanbanBoardView(chat, board, selectedObject, isShowingObjectModal)
|
KanbanBoardView(chat, board, selectedObject, isShowingObjectModal)
|
||||||
)}
|
)}
|
||||||
@@ -67,20 +67,13 @@ function KanbanBoardView(
|
|||||||
selectedObject: React.State<MessageObject | undefined>,
|
selectedObject: React.State<MessageObject | undefined>,
|
||||||
isShowingObjectModal: React.State<boolean>
|
isShowingObjectModal: React.State<boolean>
|
||||||
) {
|
) {
|
||||||
const editingBoardTitle = new React.State(kanbanBoard.title);
|
const editingCategory = new React.State(kanbanBoard.category);
|
||||||
const isEditing = new React.State(false);
|
|
||||||
const cannotRename = React.createProxyState(
|
|
||||||
[editingBoardTitle],
|
|
||||||
() =>
|
|
||||||
editingBoardTitle.value == kanbanBoard.title ||
|
|
||||||
editingBoardTitle.value == ""
|
|
||||||
);
|
|
||||||
|
|
||||||
function renameBoard() {
|
function renameBoard() {
|
||||||
kanbanBoard.items.forEach((kanbanBoardItem) => {
|
kanbanBoard.items.forEach((kanbanBoardItem) => {
|
||||||
const { messageObject } = kanbanBoardItem;
|
const { messageObject } = kanbanBoardItem;
|
||||||
const latest = chat.getMostRecentContent(messageObject);
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
latest.categoryName = editingBoardTitle.value;
|
latest.categoryName = editingCategory.value;
|
||||||
chat.addObjectAndSend(messageObject);
|
chat.addObjectAndSend(messageObject);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -98,32 +91,17 @@ function KanbanBoardView(
|
|||||||
if (!messageObject) return;
|
if (!messageObject) return;
|
||||||
|
|
||||||
const latest = chat.getMostRecentContent(messageObject);
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
latest.categoryName = kanbanBoard.title;
|
latest.categoryName = kanbanBoard.category;
|
||||||
chat.addObjectAndSend(messageObject);
|
chat.addObjectAndSend(messageObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
const titleInput = (
|
|
||||||
<input bind:value={editingBoardTitle} on:enter={renameBoard}></input>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="flex-column flex-no"
|
class="flex-column flex-no object-entry-wide"
|
||||||
style="min-width: 280px"
|
|
||||||
on:dragover={dragOver}
|
on:dragover={dragOver}
|
||||||
on:drop={drop}
|
on:drop={drop}
|
||||||
>
|
>
|
||||||
<div class="flex-row align-center justify-apart">
|
{RenameView(editingCategory, kanbanBoard.category, renameBoard)}
|
||||||
{titleInput}
|
|
||||||
<button
|
|
||||||
class="primary"
|
|
||||||
aria-label={translation.rename}
|
|
||||||
on:click={renameBoard}
|
|
||||||
toggle:disabled={cannotRename}
|
|
||||||
>
|
|
||||||
<span class="icon">edit</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
<div class="flex-column gap padding-bottom">
|
<div class="flex-column gap padding-bottom">
|
||||||
{...kanbanBoard.items
|
{...kanbanBoard.items
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ export function ObjectDetailModal(
|
|||||||
[selectedMessageObject],
|
[selectedMessageObject],
|
||||||
() => selectedMessageObject.value.categoryName ?? ""
|
() => selectedMessageObject.value.categoryName ?? ""
|
||||||
);
|
);
|
||||||
|
const editingStatus = React.createProxyState(
|
||||||
|
[selectedMessageObject],
|
||||||
|
() => selectedMessageObject.value.status ?? ""
|
||||||
|
);
|
||||||
const editingDate = React.createProxyState(
|
const editingDate = React.createProxyState(
|
||||||
[selectedMessageObject],
|
[selectedMessageObject],
|
||||||
() => selectedMessageObject.value.date ?? ""
|
() => selectedMessageObject.value.date ?? ""
|
||||||
@@ -50,6 +54,7 @@ export function ObjectDetailModal(
|
|||||||
[
|
[
|
||||||
editingNoteContent,
|
editingNoteContent,
|
||||||
editingCategory,
|
editingCategory,
|
||||||
|
editingStatus,
|
||||||
editingDate,
|
editingDate,
|
||||||
editingTime,
|
editingTime,
|
||||||
editingPriority,
|
editingPriority,
|
||||||
@@ -84,6 +89,7 @@ export function ObjectDetailModal(
|
|||||||
noteContent: editingNoteContent.value,
|
noteContent: editingNoteContent.value,
|
||||||
priority: editingPriority.value,
|
priority: editingPriority.value,
|
||||||
categoryName: editingCategory.value,
|
categoryName: editingCategory.value,
|
||||||
|
status: editingStatus.value,
|
||||||
date: editingDate.value,
|
date: editingDate.value,
|
||||||
time: editingTime.value,
|
time: editingTime.value,
|
||||||
});
|
});
|
||||||
@@ -152,7 +158,7 @@ export function ObjectDetailModal(
|
|||||||
<div>
|
<div>
|
||||||
<span>{translation.note}</span>
|
<span>{translation.note}</span>
|
||||||
<textarea
|
<textarea
|
||||||
rows="5"
|
rows="10"
|
||||||
class="height-auto"
|
class="height-auto"
|
||||||
bind:value={editingNoteContent}
|
bind:value={editingNoteContent}
|
||||||
placeholder={translation.noteContentPlaceholder}
|
placeholder={translation.noteContentPlaceholder}
|
||||||
@@ -160,6 +166,8 @@ export function ObjectDetailModal(
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<hr></hr>
|
||||||
|
|
||||||
<label class="tile">
|
<label class="tile">
|
||||||
<span class="icon">{icons.categoryName}</span>
|
<span class="icon">{icons.categoryName}</span>
|
||||||
<div>
|
<div>
|
||||||
@@ -189,6 +197,17 @@ export function ObjectDetailModal(
|
|||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="tile">
|
||||||
|
<span class="icon">{icons.status}</span>
|
||||||
|
<div>
|
||||||
|
<span>{translation.status}</span>
|
||||||
|
<input
|
||||||
|
bind:value={editingStatus}
|
||||||
|
placeholder={translation.statusPlaceholder}
|
||||||
|
></input>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
|
|
||||||
<label class="tile">
|
<label class="tile">
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export function ObjectEntryView(
|
|||||||
const fields = {
|
const fields = {
|
||||||
[icons.noteContent]: "---",
|
[icons.noteContent]: "---",
|
||||||
[icons.priority]: "---",
|
[icons.priority]: "---",
|
||||||
[icons.categoryName]: "---",
|
[icons.status]: "---",
|
||||||
[icons.date]: "---",
|
[icons.date]: "---",
|
||||||
[icons.time]: "---",
|
[icons.time]: "---",
|
||||||
};
|
};
|
||||||
@@ -39,6 +39,7 @@ export function ObjectEntryView(
|
|||||||
const icon = icons[key];
|
const icon = icons[key];
|
||||||
if (!icon) return;
|
if (!icon) return;
|
||||||
|
|
||||||
|
if (!fields[icon]) return;
|
||||||
fields[icon] = value;
|
fields[icon] = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ export function ObjectEntryView(
|
|||||||
on:dragstart={dragStart}
|
on:dragstart={dragStart}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<b>{chat.getObjectTitle(messageObject)}</b>
|
<b class="ellipsis">{chat.getObjectTitle(messageObject)}</b>
|
||||||
<hr></hr>
|
<hr></hr>
|
||||||
<span class="flex-column gap height-100 flex secondary ellipsis ">
|
<span class="flex-column gap height-100 flex secondary ellipsis ">
|
||||||
{...fieldElements}
|
{...fieldElements}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { AllObjectsView } from "./allObjectsView";
|
|||||||
import { KanbanView } from "./kanbanView";
|
import { KanbanView } from "./kanbanView";
|
||||||
import { NoteObjectsView } from "./noteObjectsView";
|
import { NoteObjectsView } from "./noteObjectsView";
|
||||||
import { ObjectDetailModal } from "./objectDetailModal";
|
import { ObjectDetailModal } from "./objectDetailModal";
|
||||||
|
import { StatusView } from "./statusView";
|
||||||
import { icons } from "../../icons";
|
import { icons } from "../../icons";
|
||||||
import { translation } from "../../translations";
|
import { translation } from "../../translations";
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ export const viewTypes = {
|
|||||||
all: [translation.viewAll, "grid_view"],
|
all: [translation.viewAll, "grid_view"],
|
||||||
notes: [translation.viewNotes, icons.noteContent],
|
notes: [translation.viewNotes, icons.noteContent],
|
||||||
kanban: [translation.viewKanban, "view_kanban"],
|
kanban: [translation.viewKanban, "view_kanban"],
|
||||||
|
status: [translation.viewStatus, icons.status],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ObjectPane(chat: Chat) {
|
export function ObjectPane(chat: Chat) {
|
||||||
@@ -40,6 +42,8 @@ export function ObjectPane(chat: Chat) {
|
|||||||
return NoteObjectsView;
|
return NoteObjectsView;
|
||||||
case "kanban":
|
case "kanban":
|
||||||
return KanbanView;
|
return KanbanView;
|
||||||
|
case "status":
|
||||||
|
return StatusView;
|
||||||
default:
|
default:
|
||||||
return AllObjectsView;
|
return AllObjectsView;
|
||||||
}
|
}
|
||||||
@@ -78,7 +82,10 @@ export function ObjectPane(chat: Chat) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="width-100 height-100 flex scroll-no" children:set={mainView}></div>
|
<div
|
||||||
|
class="width-100 height-100 flex scroll-no"
|
||||||
|
children:set={mainView}
|
||||||
|
></div>
|
||||||
|
|
||||||
<div children:set={objectModal}></div>
|
<div children:set={objectModal}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
|
import { Chat, MessageObject } from "../../Model/chatModel";
|
||||||
|
|
||||||
|
import { ObjectEntryView } from "./objectEntryView";
|
||||||
|
import { PlaceholderView } from "./placeholderView";
|
||||||
|
import { RenameView } from "../renameView";
|
||||||
|
import { translation } from "../../translations";
|
||||||
|
|
||||||
|
interface KanbanRow {
|
||||||
|
category: string;
|
||||||
|
statusColumns: Map<string, StatusColumn>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StatusColumn {
|
||||||
|
title: string;
|
||||||
|
items: StatusCellItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StatusCellItem {
|
||||||
|
priority: number;
|
||||||
|
status: string;
|
||||||
|
messageObject: MessageObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StatusItemList {
|
||||||
|
title: string;
|
||||||
|
items: MessageObject[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StatusView(
|
||||||
|
chat: Chat,
|
||||||
|
selectedObject: React.State<MessageObject | undefined>,
|
||||||
|
isShowingObjectModal: React.State<boolean>
|
||||||
|
) {
|
||||||
|
const rows = new React.MapState<KanbanRow>();
|
||||||
|
const statuses = new Map<string, StatusItemList>();
|
||||||
|
|
||||||
|
chat.objects.subscribe(() => {
|
||||||
|
rows.clear();
|
||||||
|
statuses.clear();
|
||||||
|
|
||||||
|
// add items
|
||||||
|
chat.objects.value.forEach((messageObject) => {
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
if (!latest.categoryName || !latest.status) return;
|
||||||
|
|
||||||
|
const category = latest.categoryName;
|
||||||
|
const status = latest.status;
|
||||||
|
|
||||||
|
if (!statuses.has(status)) {
|
||||||
|
statuses.set(status, { title: status, items: [] });
|
||||||
|
}
|
||||||
|
statuses.get(status)!.items.push(messageObject);
|
||||||
|
|
||||||
|
if (!rows.value.has(category)) {
|
||||||
|
rows.set(category, { category: category, statusColumns: new Map() });
|
||||||
|
}
|
||||||
|
const row = rows.value.get(category)!;
|
||||||
|
|
||||||
|
if (!row.statusColumns.has(status)) {
|
||||||
|
row.statusColumns.set(status, { title: status, items: [] });
|
||||||
|
}
|
||||||
|
const column = row.statusColumns.get(status)!;
|
||||||
|
|
||||||
|
column.items.push({
|
||||||
|
priority: latest.priority ?? 0,
|
||||||
|
status,
|
||||||
|
messageObject,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// add status placeholders
|
||||||
|
statuses.forEach((statusItemList) => {
|
||||||
|
rows.value.forEach((row) => {
|
||||||
|
if (row.statusColumns.has(statusItemList.title)) return;
|
||||||
|
row.statusColumns.set(statusItemList.title, {
|
||||||
|
title: statusItemList.title,
|
||||||
|
items: [],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = React.createProxyState([chat.objects], () =>
|
||||||
|
rows.value.size == 0 ? (
|
||||||
|
PlaceholderView()
|
||||||
|
) : (
|
||||||
|
<div class="flex-column large-gap width-100 height-100 scroll-v scroll-h padding">
|
||||||
|
<div class="flex-row large-gap">
|
||||||
|
<div class="flex-column object-entry-wide"></div>
|
||||||
|
{...[...statuses.values()]
|
||||||
|
.sort((a, b) => a.title.localeCompare(b.title))
|
||||||
|
.map((status) => StatusHeaderView(chat, status))}
|
||||||
|
</div>
|
||||||
|
{...[...rows.value.values()]
|
||||||
|
.sort((a, b) => a.category.localeCompare(b.category))
|
||||||
|
.map((board) =>
|
||||||
|
KanbanRowView(chat, board, selectedObject, isShowingObjectModal)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="width-100 height-100 scroll-no" children:set={content}></div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusHeaderView(chat: Chat, status: StatusItemList) {
|
||||||
|
const editingStatus = new React.State(status.title);
|
||||||
|
|
||||||
|
function rename() {
|
||||||
|
status.items.forEach((messageObject) => {
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
if (!latest) return;
|
||||||
|
|
||||||
|
latest.status = editingStatus.value;
|
||||||
|
chat.addObject(messageObject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="object-entry-wide">
|
||||||
|
{RenameView(editingStatus, status.title, rename)}
|
||||||
|
<hr></hr>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function KanbanRowView(
|
||||||
|
chat: Chat,
|
||||||
|
kanbanRow: KanbanRow,
|
||||||
|
selectedObject: React.State<MessageObject | undefined>,
|
||||||
|
isShowingObjectModal: React.State<boolean>
|
||||||
|
) {
|
||||||
|
const editingCategory = new React.State(kanbanRow.category);
|
||||||
|
|
||||||
|
const cellItemEntries = [...kanbanRow.statusColumns.entries()];
|
||||||
|
|
||||||
|
function renameCategory() {
|
||||||
|
kanbanRow.statusColumns.forEach((statusColumns) => {
|
||||||
|
statusColumns.items.forEach((statusCellItem) => {
|
||||||
|
const { messageObject } = statusCellItem;
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
latest.categoryName = editingCategory.value;
|
||||||
|
chat.addObjectAndSend(messageObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="flex-row flex-no large-gap">
|
||||||
|
<div class="flex-row align-start">
|
||||||
|
{RenameView(editingCategory, kanbanRow.category, renameCategory)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{...cellItemEntries
|
||||||
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||||
|
.map((entry) =>
|
||||||
|
StatusColumnView(
|
||||||
|
chat,
|
||||||
|
entry[1].items,
|
||||||
|
kanbanRow.category,
|
||||||
|
entry[1].title,
|
||||||
|
selectedObject,
|
||||||
|
isShowingObjectModal
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
|
||||||
|
<hr></hr>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusColumnView(
|
||||||
|
chat: Chat,
|
||||||
|
items: StatusCellItem[],
|
||||||
|
category: string,
|
||||||
|
status: string,
|
||||||
|
selectedObject: React.State<MessageObject | undefined>,
|
||||||
|
isShowingObjectModal: React.State<boolean>
|
||||||
|
) {
|
||||||
|
function dragOver(event: DragEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function drop(event: DragEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
var id = event.dataTransfer?.getData("text");
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const messageObject = chat.objects.value.get(id);
|
||||||
|
if (!messageObject) return;
|
||||||
|
|
||||||
|
const latest = chat.getMostRecentContent(messageObject);
|
||||||
|
latest.status = status;
|
||||||
|
latest.categoryName = category;
|
||||||
|
chat.addObjectAndSend(messageObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="flex-column gap object-entry-wide"
|
||||||
|
on:dragover={dragOver}
|
||||||
|
on:drop={drop}
|
||||||
|
>
|
||||||
|
{...items
|
||||||
|
.sort((a, b) => b.priority - a.priority)
|
||||||
|
.map((kanbanBoardItem) =>
|
||||||
|
ObjectEntryView(
|
||||||
|
chat,
|
||||||
|
kanbanBoardItem.messageObject,
|
||||||
|
selectedObject,
|
||||||
|
isShowingObjectModal
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import * as React from "bloatless-react";
|
||||||
|
|
||||||
|
import { translation } from "../translations";
|
||||||
|
|
||||||
|
export function RenameView(
|
||||||
|
editingName: React.State<string>,
|
||||||
|
initialName: string,
|
||||||
|
rename: () => void
|
||||||
|
) {
|
||||||
|
const cannotRename = React.createProxyState(
|
||||||
|
[editingName],
|
||||||
|
() => editingName.value == initialName || editingName.value == ""
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="flex-row align-center justify-apart object-entry-wide">
|
||||||
|
<input bind:value={editingName} on:enter={rename}></input>
|
||||||
|
<button
|
||||||
|
class="primary"
|
||||||
|
aria-label={translation.rename}
|
||||||
|
on:click={rename}
|
||||||
|
toggle:disabled={cannotRename}
|
||||||
|
>
|
||||||
|
<span class="icon">edit</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ export const icons = {
|
|||||||
|
|
||||||
noteContent: "sticky_note_2",
|
noteContent: "sticky_note_2",
|
||||||
categoryName: "category",
|
categoryName: "category",
|
||||||
|
status: "trending_up",
|
||||||
priority: "priority_high",
|
priority: "priority_high",
|
||||||
date: "calendar_month",
|
date: "calendar_month",
|
||||||
time: "schedule",
|
time: "schedule",
|
||||||
|
|||||||
+6
-1
@@ -2,13 +2,18 @@
|
|||||||
border-left: 10px solid var(--text-warning);
|
border-left: 10px solid var(--text-warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TOOLS */
|
/* OBJECTS */
|
||||||
:root {
|
:root {
|
||||||
--thread-view-height-with-tools: 8rem;
|
--thread-view-height-with-tools: 8rem;
|
||||||
--thread-view-width-with-tools: 20rem;
|
--thread-view-width-with-tools: 20rem;
|
||||||
--footer-top-line: calc(var(--control-height) + var(--safe-area-bottom));
|
--footer-top-line: calc(var(--control-height) + var(--safe-area-bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.object-entry-wide {
|
||||||
|
width: 280px;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
#message-tab {
|
#message-tab {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-3
@@ -90,6 +90,7 @@ const englishTranslations = {
|
|||||||
viewAll: "All",
|
viewAll: "All",
|
||||||
viewNotes: "Notes",
|
viewNotes: "Notes",
|
||||||
viewKanban: "Kanban",
|
viewKanban: "Kanban",
|
||||||
|
viewStatus: "Status",
|
||||||
|
|
||||||
noObjects: "No objects",
|
noObjects: "No objects",
|
||||||
|
|
||||||
@@ -100,9 +101,11 @@ const englishTranslations = {
|
|||||||
note: "Note",
|
note: "Note",
|
||||||
noteContentPlaceholder: "Take a note...",
|
noteContentPlaceholder: "Take a note...",
|
||||||
category: "Category",
|
category: "Category",
|
||||||
|
categoryPlaceholder: "Define a category",
|
||||||
priority: "Priority",
|
priority: "Priority",
|
||||||
priorityPlaceholder: "2",
|
priorityPlaceholder: "2",
|
||||||
categoryPlaceholder: "Define a category",
|
status: "Status",
|
||||||
|
statusPlaceholder: "Pending",
|
||||||
date: "Date",
|
date: "Date",
|
||||||
time: "Time",
|
time: "Time",
|
||||||
|
|
||||||
@@ -204,6 +207,7 @@ const allTranslations: { [key: string]: typeof englishTranslations } = {
|
|||||||
viewAll: "Todos",
|
viewAll: "Todos",
|
||||||
viewNotes: "Notas",
|
viewNotes: "Notas",
|
||||||
viewKanban: "Kanban",
|
viewKanban: "Kanban",
|
||||||
|
viewStatus: "Estado",
|
||||||
|
|
||||||
noObjects: "Sin objetos",
|
noObjects: "Sin objetos",
|
||||||
|
|
||||||
@@ -214,9 +218,11 @@ const allTranslations: { [key: string]: typeof englishTranslations } = {
|
|||||||
note: "Nota",
|
note: "Nota",
|
||||||
noteContentPlaceholder: "Toma nota...",
|
noteContentPlaceholder: "Toma nota...",
|
||||||
category: "Categoría",
|
category: "Categoría",
|
||||||
|
categoryPlaceholder: "Define una categoría",
|
||||||
priority: "Prioridad",
|
priority: "Prioridad",
|
||||||
priorityPlaceholder: "2",
|
priorityPlaceholder: "2",
|
||||||
categoryPlaceholder: "Define una categoría",
|
status: "Estado",
|
||||||
|
statusPlaceholder: "Pendiente",
|
||||||
date: "Fecha",
|
date: "Fecha",
|
||||||
time: "Hora",
|
time: "Hora",
|
||||||
|
|
||||||
@@ -315,6 +321,7 @@ const allTranslations: { [key: string]: typeof englishTranslations } = {
|
|||||||
viewAll: "Alle",
|
viewAll: "Alle",
|
||||||
viewNotes: "Notizen",
|
viewNotes: "Notizen",
|
||||||
viewKanban: "Kanban",
|
viewKanban: "Kanban",
|
||||||
|
viewStatus: "Status",
|
||||||
|
|
||||||
noObjects: "Keine Objekte",
|
noObjects: "Keine Objekte",
|
||||||
|
|
||||||
@@ -325,9 +332,11 @@ const allTranslations: { [key: string]: typeof englishTranslations } = {
|
|||||||
note: "Notiz",
|
note: "Notiz",
|
||||||
noteContentPlaceholder: "Notiz eingeben...",
|
noteContentPlaceholder: "Notiz eingeben...",
|
||||||
category: "Kategorie",
|
category: "Kategorie",
|
||||||
|
categoryPlaceholder: "Kategorie eingeben...",
|
||||||
priority: "Priorität",
|
priority: "Priorität",
|
||||||
priorityPlaceholder: "2",
|
priorityPlaceholder: "2",
|
||||||
categoryPlaceholder: "Kategorie eingeben...",
|
status: "Status",
|
||||||
|
statusPlaceholder: "Ausstehend",
|
||||||
date: "Datum",
|
date: "Datum",
|
||||||
time: "Uhrzeit",
|
time: "Uhrzeit",
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user