taskchef 8.5.0 → 8.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +2 -2
- package/LICENSE +202 -21
- package/README.md +111 -461
- package/assets/CODEX_ASSET_NOTICE.md +1 -1
- package/docs/advanced-guide.md +506 -0
- package/docs/images/readme-board.png +0 -0
- package/docs/images/readme-completed.png +0 -0
- package/docs/images/readme-list.png +0 -0
- package/docs/images/readme-needs-input.png +0 -0
- package/docs/images/readme-roles.png +0 -0
- package/docs/spec.md +4 -3
- package/docs/workflows.md +1 -1
- package/package.json +8 -2
- package/src/dashboard/dist/app.js +30 -30
- package/src/dashboard/dist/app.js.map +1 -1
- package/src/dashboard/dist/styles.css +1 -1
- package/src/dashboard/react/App.test.tsx +8 -0
- package/src/dashboard/react/App.tsx +3 -2
- package/src/dashboard/react/components/TaskDetail.tsx +1 -1
- package/src/dashboard/react/styles.css +1 -0
|
@@ -154,6 +154,8 @@ test("Settings uses the same capped content layout as List", () => {
|
|
|
154
154
|
const { container } = render(<DashboardApp connect={false} />);
|
|
155
155
|
const settingsPage = container.querySelector(".taskchef-settings-page");
|
|
156
156
|
expect(settingsPage).toHaveClass("taskchef-content");
|
|
157
|
+
expect(container.querySelector(".taskchef-header")).not.toHaveClass("taskchef-list-layout");
|
|
158
|
+
expect(container.querySelector(".taskchef-main")).not.toHaveClass("taskchef-list-layout");
|
|
157
159
|
expect(within(settingsPage as HTMLElement).getByRole("link", { name: "Back to tasks" })).toBeInTheDocument();
|
|
158
160
|
window.location.hash = "";
|
|
159
161
|
});
|
|
@@ -208,6 +210,8 @@ test("places the filtered task count below the toolbar and updates it from live
|
|
|
208
210
|
const summary = document.querySelector<HTMLElement>(".taskchef-results-summary");
|
|
209
211
|
const taskList = screen.getByRole("region", { name: "Tasks" });
|
|
210
212
|
|
|
213
|
+
expect(header.querySelector(".taskchef-header")).toHaveClass("taskchef-list-layout");
|
|
214
|
+
expect(document.querySelector(".taskchef-main")).toHaveClass("taskchef-list-layout");
|
|
211
215
|
expect(projectFilter).toHaveValue("Another project");
|
|
212
216
|
expect(screen.getByRole("combobox", { name: "Updated" })).toHaveValue("All time");
|
|
213
217
|
expect(toolbar?.firstElementChild?.firstElementChild).toContainElement(viewSwitch);
|
|
@@ -248,6 +252,8 @@ test("Board ignores the List status filter and restores it on return", () => {
|
|
|
248
252
|
render(<DashboardApp connect={false} initialFilters={{ status: "completed" }} initialTasks={tasks} />);
|
|
249
253
|
expect(screen.getByRole("radio", { name: /Completed/ })).toBeChecked();
|
|
250
254
|
fireEvent.click(screen.getByRole("radio", { name: "Board" }));
|
|
255
|
+
expect(document.querySelector(".taskchef-header")).not.toHaveClass("taskchef-list-layout");
|
|
256
|
+
expect(document.querySelector(".taskchef-main")).not.toHaveClass("taskchef-list-layout");
|
|
251
257
|
const toolbar = document.querySelector<HTMLElement>(".taskchef-toolbar");
|
|
252
258
|
expect(toolbar?.firstElementChild?.firstElementChild).toContainElement(screen.getByRole("radiogroup", { name: "View" }));
|
|
253
259
|
expect(screen.getByRole("radiogroup", { name: "View" }).compareDocumentPosition(screen.getByRole("combobox", { name: "Project" })) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
@@ -256,6 +262,8 @@ test("Board ignores the List status filter and restores it on return", () => {
|
|
|
256
262
|
expect(screen.getByRole("region", { name: "Task board" })).toHaveTextContent("Working task");
|
|
257
263
|
expect(screen.getByRole("region", { name: "Task board" })).toHaveTextContent("Completed task");
|
|
258
264
|
fireEvent.click(screen.getByRole("radio", { name: "List" }));
|
|
265
|
+
expect(document.querySelector(".taskchef-header")).toHaveClass("taskchef-list-layout");
|
|
266
|
+
expect(document.querySelector(".taskchef-main")).toHaveClass("taskchef-list-layout");
|
|
259
267
|
expect(screen.getByRole("radio", { name: /Completed/ })).toBeChecked();
|
|
260
268
|
expect(screen.getByText("Tasks: 1 of 2")).toBeInTheDocument();
|
|
261
269
|
});
|
|
@@ -239,6 +239,7 @@ export function DashboardApp({
|
|
|
239
239
|
const visible = useMemo(() => filterTasks(tasks, { project, status, date, now }), [tasks, project, status, date, now]);
|
|
240
240
|
const boardTasks = useMemo(() => filterTasks(tasks, { project, date, now }), [tasks, project, date, now]);
|
|
241
241
|
const board = preferredView === "board";
|
|
242
|
+
const listLayout = !board && !settings;
|
|
242
243
|
const counts = useMemo(() => statusFilterCounts(tasks, { project, date, now }), [tasks, project, date, now]);
|
|
243
244
|
const statusData = STATUS_FILTERS.map(({ label, value }: { label: string; value: string }) => ({
|
|
244
245
|
label: counts[value] > 0 ? `${label} ${counts[value]}` : label,
|
|
@@ -322,7 +323,7 @@ export function DashboardApp({
|
|
|
322
323
|
<RelativeTimeProvider now={now}>
|
|
323
324
|
<AppShell className="taskchef-shell" padding={0}>
|
|
324
325
|
<Box className="taskchef-header-shell" component="header">
|
|
325
|
-
<Container className={`taskchef-header${board ? " taskchef-header-board" : ""}`} size="100%">
|
|
326
|
+
<Container className={`taskchef-header${board ? " taskchef-header-board" : ""}${listLayout ? " taskchef-list-layout" : ""}`} size="100%">
|
|
326
327
|
<Group className="taskchef-header-layout" justify="space-between" wrap="nowrap">
|
|
327
328
|
<Group className="taskchef-header-copy" gap="xs" wrap="nowrap">
|
|
328
329
|
<BrandIcon />
|
|
@@ -344,7 +345,7 @@ export function DashboardApp({
|
|
|
344
345
|
</Box>
|
|
345
346
|
|
|
346
347
|
<AppShell.Main>
|
|
347
|
-
<Container className={`taskchef-main${board ? " taskchef-main-board" : ""}`} pb={80} size="100%">
|
|
348
|
+
<Container className={`taskchef-main${board ? " taskchef-main-board" : ""}${listLayout ? " taskchef-list-layout" : ""}`} pb={80} size="100%">
|
|
348
349
|
{projectIndex?.status === "unavailable" && (
|
|
349
350
|
<Alert color="yellow" icon={<IconAlertTriangle size={18} />} mb="md" title="Project index unavailable">
|
|
350
351
|
Task history is still visible. Verify the index and inspect backups before making project changes.
|
|
@@ -186,7 +186,7 @@ export function TaskDetail({
|
|
|
186
186
|
opened={opened}
|
|
187
187
|
overlayProps={{ backgroundOpacity: 0.55, blur: 2 }}
|
|
188
188
|
scrollAreaComponent={ScrollArea.Autosize}
|
|
189
|
-
size="min(
|
|
189
|
+
size="min(600px, calc(100vw - 32px))"
|
|
190
190
|
title={<span className="taskchef-visually-hidden">Task details</span>}
|
|
191
191
|
trapFocus
|
|
192
192
|
zIndex={300}
|
|
@@ -30,6 +30,7 @@ body {
|
|
|
30
30
|
.taskchef-version { margin-inline-start: 0.45rem; color: var(--taskchef-muted); font-size: 0.78em; font-weight: 600; letter-spacing: 0; text-transform: none; }
|
|
31
31
|
.taskchef-brand-icon, .taskchef-brand-icon img { display: block; flex: none; width: 28px; height: 28px; }
|
|
32
32
|
.taskchef-main { max-width: var(--taskchef-page-max-width); padding-top: 8px; padding-inline: var(--taskchef-page-gutter); }
|
|
33
|
+
.taskchef-list-layout { --taskchef-content-max-width: 600px; max-width: calc(var(--taskchef-content-max-width) + 2 * var(--taskchef-page-gutter)); }
|
|
33
34
|
.taskchef-content { width: 100%; max-width: var(--taskchef-content-max-width); }
|
|
34
35
|
.taskchef-toolbar { max-width: none; margin-bottom: 22px; padding: 8px 0 var(--mantine-spacing-md); border: 0; border-bottom: 1px solid var(--taskchef-border); background: transparent; box-shadow: none; }
|
|
35
36
|
.taskchef-toolbar-filters { width: min(100%, 488px); min-width: 0; }
|