Files
CT/src/View/ChatPages/boardStatusGridPage.tsx
T

240 lines
6.6 KiB
TypeScript
Raw Normal View History

2024-07-25 04:12:30 +02:00
import * as React from "bloatless-react";
2024-07-25 18:42:06 +02:00
import {
PropertyValueList,
collectPropertyValuesToState,
createPropertyValueIndexState,
createSortedPropertyValueState,
} from "../Components/propertyValueList";
2024-07-25 04:12:30 +02:00
import {
TaskCategoryBulkChangeViewModel,
TaskStatusBulkChangeViewModel,
2024-07-25 15:31:27 +02:00
} from "../../ViewModel/Utility/taskPropertyBulkChangeViewModel";
2024-07-25 04:12:30 +02:00
import BoardViewModel from "../../ViewModel/Pages/boardViewModel";
import { FilteredList } from "../Components/filteredList";
import TaskViewModel from "../../ViewModel/Pages/taskViewModel";
import { TaskViewModelToEntry } from "../Components/taskEntry";
2024-07-25 16:24:09 +02:00
import { allowDrop } from "../utility";
2024-07-25 04:12:30 +02:00
import { translations } from "../translations";
export function BoardStatusGridPage(boardViewModel: BoardViewModel) {
const statuses: React.ListState<string> = new React.ListState();
2024-07-25 18:42:06 +02:00
const sortedStatuses: React.State<string[]> =
createSortedPropertyValueState(statuses);
2024-07-25 04:12:30 +02:00
boardViewModel.taskViewModels.subscribe(() => {
2024-07-25 18:42:06 +02:00
collectPropertyValuesToState(
2024-07-25 04:12:30 +02:00
"status",
(taskViewModel: TaskViewModel) => taskViewModel.task,
2024-07-25 18:42:06 +02:00
boardViewModel.taskViewModels,
statuses
2024-07-25 04:12:30 +02:00
);
});
2024-07-25 18:42:06 +02:00
const statusNameCellConverter: React.StateItemConverter<string> = (
statusName: string
) => {
const index = createPropertyValueIndexState(sortedStatuses, statusName);
return StatusNameCell(statusName, index, boardViewModel);
};
2024-07-25 04:12:30 +02:00
return (
<div class="status-page-content">
<div
class="status-name-row"
children:append={[statuses, statusNameCellConverter]}
></div>
{PropertyValueList(
"category",
(taskViewModel: TaskViewModel) => taskViewModel.task,
2024-07-25 15:31:27 +02:00
boardViewModel.filteredTaskViewModels,
2024-07-25 18:42:06 +02:00
(
categories: React.ListState<string>,
sortedCategories: React.State<string[]>
) => {
const categoryRowConverter: React.StateItemConverter<string> = (
categoryName: string
) => {
const index: React.State<number> = createPropertyValueIndexState(
sortedCategories,
categoryName
);
return CategoryRow(
categoryName,
index,
statuses,
sortedStatuses,
boardViewModel
);
};
2024-07-25 04:12:30 +02:00
return (
<div
class="status-grid-wrapper"
children:append={[categories, categoryRowConverter]}
></div>
);
}
)}
</div>
);
}
2024-07-25 18:42:06 +02:00
function StatusNameCell(
statusName: string,
index: React.State<number>,
boardViewModel: BoardViewModel
) {
2024-07-25 04:12:30 +02:00
const taskViewModelsWithMatchingStatus: React.ListState<TaskViewModel> =
new React.ListState();
boardViewModel.taskViewModels.handleAddition(
(taskViewModel: TaskViewModel) => {
const doesMatchStatus: boolean = taskViewModel.task.status == statusName;
if (doesMatchStatus == false) return;
taskViewModelsWithMatchingStatus.add(taskViewModel);
boardViewModel.taskViewModels.handleRemoval(taskViewModel, () => {
taskViewModelsWithMatchingStatus.remove(taskViewModel);
});
}
);
const viewModel: TaskStatusBulkChangeViewModel =
new TaskStatusBulkChangeViewModel(
taskViewModelsWithMatchingStatus,
statusName
);
2024-07-25 18:42:06 +02:00
const view = (
2024-07-25 04:12:30 +02:00
<div class="flex-row">
<div class="property-input-wrapper">
<input
placeholder={
translations.chatPage.task.renameCategoryInputPlaceholder
}
bind:value={viewModel.inputValue}
on:enter={viewModel.set}
></input>
<button
class="primary"
on:click={viewModel.set}
toggle:disabled={viewModel.cannotSet}
>
<span class="icon">check</span>
</button>
</div>
</div>
);
2024-07-25 18:42:06 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 04:12:30 +02:00
}
function CategoryRow(
categoryName: string,
2024-07-25 18:42:06 +02:00
index: React.State<number>,
2024-07-25 04:12:30 +02:00
allStatuses: React.ListState<string>,
2024-07-25 18:42:06 +02:00
sortedStatuses: React.State<string[]>,
2024-07-25 04:12:30 +02:00
boardViewModel: BoardViewModel
) {
return FilteredList(
{ category: categoryName },
(taskViewModel: TaskViewModel) => taskViewModel.task,
boardViewModel.taskViewModels,
(taskViewModels: React.ListState<TaskViewModel>) => {
const statusNameConverter: React.StateItemConverter<string> = (
statusName: string
) => {
2024-07-25 18:42:06 +02:00
const index = createPropertyValueIndexState(sortedStatuses, statusName);
2024-07-25 16:24:09 +02:00
return CategoryStatusColumn(
categoryName,
statusName,
2024-07-25 18:42:06 +02:00
index,
2024-07-25 16:24:09 +02:00
boardViewModel,
taskViewModels
);
2024-07-25 04:12:30 +02:00
};
const viewModel: TaskCategoryBulkChangeViewModel =
new TaskCategoryBulkChangeViewModel(taskViewModels, categoryName);
2024-07-25 18:42:06 +02:00
const view = (
2024-07-25 04:12:30 +02:00
<div class="flex-row flex-no large-gap">
<div class="property-input-wrapper">
<input
placeholder={
translations.chatPage.task.renameCategoryInputPlaceholder
}
bind:value={viewModel.inputValue}
on:enter={viewModel.set}
></input>
<button
class="primary"
on:click={viewModel.set}
toggle:disabled={viewModel.cannotSet}
>
<span class="icon">check</span>
</button>
</div>
<div
class="flex-row large-gap padding-right"
children:append={[allStatuses, statusNameConverter]}
></div>
</div>
);
2024-07-25 18:42:06 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 04:12:30 +02:00
}
);
}
function CategoryStatusColumn(
2024-07-25 16:24:09 +02:00
categoryName: string,
2024-07-25 04:12:30 +02:00
statusName: string,
2024-07-25 18:42:06 +02:00
index: React.State<number>,
2024-07-25 16:24:09 +02:00
boardViewModel: BoardViewModel,
2024-07-25 04:12:30 +02:00
taskViewModelsWithMatchingCategory: React.ListState<TaskViewModel>
) {
const taskViewModels: React.ListState<TaskViewModel> = new React.ListState();
taskViewModelsWithMatchingCategory.handleAddition((taskViewModel) => {
const doesMatchStatus: boolean = taskViewModel.status.value == statusName;
if (doesMatchStatus == false) return;
taskViewModels.add(taskViewModel);
taskViewModelsWithMatchingCategory.handleRemoval(taskViewModel, () => {
taskViewModels.remove(taskViewModel);
});
});
2024-07-25 16:24:09 +02:00
function drop() {
boardViewModel.handleDropWithinBoard(categoryName, statusName);
2024-07-25 16:24:09 +02:00
}
2024-07-25 18:42:06 +02:00
const view = (
2024-07-25 04:12:30 +02:00
<div
class="status-column gap"
2024-07-25 16:24:09 +02:00
on:dragover={allowDrop}
on:drop={drop}
2024-07-25 04:12:30 +02:00
children:append={[taskViewModels, TaskViewModelToEntry]}
></div>
);
2024-07-25 18:42:06 +02:00
index.subscribe((newIndex) => {
view.style.order = newIndex;
});
return view;
2024-07-25 04:12:30 +02:00
}