fixed assignments view once again
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import { color2Tailwind, colors } from "@/components/Colorselector";
|
||||
import { apiCall } from "@/lib";
|
||||
import { AddLarge, Edit } from "@carbon/icons-react";
|
||||
import { colors } from "@/components/Colorselector";
|
||||
import { apiCall, getAvailabilities } from "@/lib";
|
||||
import { AddLarge, Edit, TrashCan } from "@carbon/icons-react";
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Checkbox,
|
||||
Chip,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@@ -18,26 +18,20 @@ import { useState } from "react";
|
||||
import AddAvailability from "./AddAvailability";
|
||||
import { Availability } from "./AvailabilityEditor";
|
||||
import EditAvailability from "./EditAvailability";
|
||||
import DeleteConfirmation from "@/components/DeleteConfirmation";
|
||||
import AvailabilityChip from "@/components/AvailabilityChip";
|
||||
import zustand from "@/Zustand";
|
||||
|
||||
export default function Availabilities() {
|
||||
const [showAddAvailability, setShowAddAvailability] = useState(false);
|
||||
const [editAvailability, setEditAvailability] = useState<Availability>();
|
||||
const [deleteAvailability, setDeleteAvailability] = useState<Availability>();
|
||||
|
||||
const availabilities = useAsyncList<Availability>({
|
||||
async load() {
|
||||
const result = await apiCall("GET", "availabilities");
|
||||
|
||||
if (result.ok) {
|
||||
const json = await result.json();
|
||||
|
||||
return {
|
||||
items: json.availabilities,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
items: [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
items: await getAvailabilities(),
|
||||
};
|
||||
},
|
||||
async sort({ items, sortDescriptor }) {
|
||||
return {
|
||||
@@ -46,7 +40,7 @@ export default function Availabilities() {
|
||||
|
||||
switch (sortDescriptor.column) {
|
||||
case "text":
|
||||
cmp = a.text.localeCompare(b.text);
|
||||
cmp = a.name.localeCompare(b.name);
|
||||
break;
|
||||
case "enabled":
|
||||
if (a.enabled && !b.enabled) {
|
||||
@@ -76,6 +70,26 @@ export default function Availabilities() {
|
||||
},
|
||||
});
|
||||
|
||||
function reload() {
|
||||
// clear the availabilites in the zustand
|
||||
zustand.getState().patch({ availabilities: undefined });
|
||||
|
||||
// refresh the availabilites
|
||||
availabilities.reload();
|
||||
}
|
||||
|
||||
async function sendDeleteAvailability(id: number | undefined) {
|
||||
if (id !== undefined) {
|
||||
const result = await apiCall("DELETE", "availabilities", { id });
|
||||
|
||||
if (result.ok) {
|
||||
reload();
|
||||
|
||||
setDeleteAvailability(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const topContent = (
|
||||
<>
|
||||
<Button
|
||||
@@ -101,10 +115,7 @@ export default function Availabilities() {
|
||||
>
|
||||
<TableHeader>
|
||||
<TableColumn allowsSorting key="userName">
|
||||
Text
|
||||
</TableColumn>
|
||||
<TableColumn allowsSorting key="color" align="center">
|
||||
Color
|
||||
Name
|
||||
</TableColumn>
|
||||
<TableColumn allowsSorting key="admin" align="center">
|
||||
Enabled
|
||||
@@ -115,35 +126,36 @@ export default function Availabilities() {
|
||||
</TableHeader>
|
||||
<TableBody items={availabilities.items}>
|
||||
{(availability) => (
|
||||
<TableRow key={availability.text}>
|
||||
<TableCell
|
||||
className={`text-${color2Tailwind(availability.color)}`}
|
||||
>
|
||||
{availability.text}
|
||||
</TableCell>
|
||||
<TableRow key={availability.name}>
|
||||
<TableCell>
|
||||
<Chip
|
||||
classNames={{
|
||||
base: `bg-${color2Tailwind(availability.color)}`,
|
||||
}}
|
||||
>
|
||||
{availability.color}
|
||||
</Chip>
|
||||
<AvailabilityChip availability={availability} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Checkbox isSelected={availability.enabled} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={() => setEditAvailability(availability)}
|
||||
>
|
||||
<Tooltip content="Edit availability">
|
||||
<Edit />
|
||||
</Tooltip>
|
||||
</Button>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={() => setEditAvailability(availability)}
|
||||
>
|
||||
<Tooltip content="Edit availability">
|
||||
<Edit />
|
||||
</Tooltip>
|
||||
</Button>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={() => setDeleteAvailability(availability)}
|
||||
color="danger"
|
||||
className="text-danger"
|
||||
>
|
||||
<TrashCan />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
@@ -153,7 +165,7 @@ export default function Availabilities() {
|
||||
<AddAvailability
|
||||
isOpen={showAddAvailability}
|
||||
onOpenChange={setShowAddAvailability}
|
||||
onSuccess={availabilities.reload}
|
||||
onSuccess={reload}
|
||||
/>
|
||||
|
||||
<EditAvailability
|
||||
@@ -162,8 +174,25 @@ export default function Availabilities() {
|
||||
onOpenChange={(isOpen) =>
|
||||
!isOpen ? setEditAvailability(undefined) : null
|
||||
}
|
||||
onSuccess={availabilities.reload}
|
||||
onSuccess={reload}
|
||||
/>
|
||||
|
||||
<DeleteConfirmation
|
||||
isOpen={!!deleteAvailability}
|
||||
onOpenChange={(isOpen) =>
|
||||
!isOpen ? setDeleteAvailability(undefined) : null
|
||||
}
|
||||
header="Delete Availability"
|
||||
onDelete={() => sendDeleteAvailability(deleteAvailability?.id)}
|
||||
>
|
||||
{!!deleteAvailability ? (
|
||||
<>
|
||||
The availability{" "}
|
||||
<AvailabilityChip availability={deleteAvailability} /> will be
|
||||
deleted.
|
||||
</>
|
||||
) : null}
|
||||
</DeleteConfirmation>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import {
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "@heroui/react";
|
||||
import React, { FormEvent, useState } from "react";
|
||||
import React, { FormEvent, useEffect, useState } from "react";
|
||||
|
||||
export interface Availability {
|
||||
text: string;
|
||||
name: string;
|
||||
color: string;
|
||||
id: number | undefined;
|
||||
enabled: boolean;
|
||||
@@ -26,13 +26,22 @@ export default function AvailabilityEditor(props: {
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
onSubmit?: (e: Availability) => void;
|
||||
}) {
|
||||
const [text, setText] = useState(props.value?.text ?? "");
|
||||
const [name, setName] = useState(props.value?.name ?? "");
|
||||
const [color, setColor] = useState(props.value?.color ?? "Red");
|
||||
const [enabled, setEnabled] = useState(props.value?.enabled ?? true);
|
||||
|
||||
// clear the inputs on closing
|
||||
useEffect(() => {
|
||||
if (!props.isOpen) {
|
||||
setName("");
|
||||
setColor("");
|
||||
setEnabled(true);
|
||||
}
|
||||
}, [props.isOpen]);
|
||||
|
||||
function submit(e: FormEvent<HTMLFormElement>) {
|
||||
const formData = Object.fromEntries(new FormData(e.currentTarget)) as {
|
||||
text: string;
|
||||
name: string;
|
||||
color: string;
|
||||
enabled: string;
|
||||
};
|
||||
@@ -64,10 +73,10 @@ export default function AvailabilityEditor(props: {
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Input
|
||||
value={text}
|
||||
onValueChange={setText}
|
||||
name="text"
|
||||
label="Text"
|
||||
value={name}
|
||||
onValueChange={setName}
|
||||
name="name"
|
||||
label="Name"
|
||||
isRequired
|
||||
variant="bordered"
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { apiCall } from "@/lib";
|
||||
import AvailabilityEditor, { Availability } from "./AvailabilityEditor";
|
||||
import { Button } from "@heroui/react";
|
||||
import { Renew } from "@carbon/icons-react";
|
||||
import AvailabilityChip from "@/components/AvailabilityChip";
|
||||
|
||||
export default function EditAvailability(props: {
|
||||
value: Availability | undefined;
|
||||
@@ -24,9 +25,9 @@ export default function EditAvailability(props: {
|
||||
header={
|
||||
<>
|
||||
Edit Availability{" "}
|
||||
<span className="font-numbers font-normal italic">
|
||||
"{props.value?.text}"
|
||||
</span>
|
||||
{!!props.value ? (
|
||||
<AvailabilityChip availability={props.value} className="ms-4" />
|
||||
) : null}
|
||||
</>
|
||||
}
|
||||
footer={
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { apiCall } from "@/lib";
|
||||
import TaskEditor, { Task } from "./TaskEditor";
|
||||
import { apiCall, Task } from "@/lib";
|
||||
import TaskEditor from "./TaskEditor";
|
||||
import { Button } from "@heroui/react";
|
||||
import { AddLarge } from "@carbon/icons-react";
|
||||
|
||||
|
||||
@@ -20,12 +20,12 @@ export default function EditTask(props: {
|
||||
|
||||
return (
|
||||
<TaskEditor
|
||||
key={props.value?.id}
|
||||
key={props.value?.name}
|
||||
header={
|
||||
<>
|
||||
Edit Task{" "}
|
||||
<span className="font-numbers font-normal italic">
|
||||
"{props.value?.text}"
|
||||
"{props.value?.name}"
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "@heroui/react";
|
||||
import React, { FormEvent, useState } from "react";
|
||||
import React, { FormEvent, useEffect, useState } from "react";
|
||||
|
||||
export default function TaskEditor(props: {
|
||||
header: React.ReactNode;
|
||||
@@ -19,13 +19,20 @@ export default function TaskEditor(props: {
|
||||
onOpenChange?: (isOpen: boolean) => void;
|
||||
onSubmit?: (e: Task) => void;
|
||||
}) {
|
||||
const [text, setText] = useState(props.value?.text ?? "");
|
||||
const [name, setName] = useState(props.value?.name ?? "");
|
||||
const [enabled, setEnabled] = useState(props.value?.enabled ?? true);
|
||||
|
||||
// clear the inputs on closing
|
||||
useEffect(() => {
|
||||
if (!props.isOpen) {
|
||||
setName("");
|
||||
setEnabled(true);
|
||||
}
|
||||
}, [props.isOpen]);
|
||||
|
||||
function submit(e: FormEvent<HTMLFormElement>) {
|
||||
const formData = Object.fromEntries(new FormData(e.currentTarget)) as {
|
||||
text: string;
|
||||
color: string;
|
||||
name: string;
|
||||
enabled: string;
|
||||
};
|
||||
|
||||
@@ -56,10 +63,10 @@ export default function TaskEditor(props: {
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<Input
|
||||
value={text}
|
||||
onValueChange={setText}
|
||||
name="text"
|
||||
label="Text"
|
||||
value={name}
|
||||
onValueChange={setName}
|
||||
name="name"
|
||||
label="Name"
|
||||
isRequired
|
||||
variant="bordered"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { apiCall, Task } from "@/lib";
|
||||
import { AddLarge, Edit } from "@carbon/icons-react";
|
||||
import { AddLarge, Edit, TrashCan } from "@carbon/icons-react";
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Checkbox,
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -15,20 +16,23 @@ import { useAsyncList } from "@react-stately/data";
|
||||
import { useState } from "react";
|
||||
import AddTask from "./AddTask";
|
||||
import EditTask from "./EditTask";
|
||||
import DeleteConfirmation from "@/components/DeleteConfirmation";
|
||||
import zustand from "@/Zustand";
|
||||
|
||||
export default function Tasks() {
|
||||
const [showAddTask, setShowAddTask] = useState(false);
|
||||
const [editTask, setEditTask] = useState<Task>();
|
||||
const [deleteTask, setDeleteTask] = useState<Task>();
|
||||
|
||||
const tasks = useAsyncList<Task>({
|
||||
async load() {
|
||||
const result = await apiCall("GET", "tasks");
|
||||
|
||||
if (result.ok) {
|
||||
const json = await result.json();
|
||||
const json = (await result.json()) as Task[];
|
||||
|
||||
return {
|
||||
items: json.tasks,
|
||||
items: json,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
@@ -43,7 +47,7 @@ export default function Tasks() {
|
||||
|
||||
switch (sortDescriptor.column) {
|
||||
case "text":
|
||||
cmp = a.text.localeCompare(b.text);
|
||||
cmp = a.name.localeCompare(b.name);
|
||||
break;
|
||||
case "enabled":
|
||||
if (a.enabled && !b.enabled) {
|
||||
@@ -64,6 +68,25 @@ export default function Tasks() {
|
||||
},
|
||||
});
|
||||
|
||||
function reload() {
|
||||
// clear the zustand
|
||||
zustand.getState().patch({ tasks: undefined });
|
||||
|
||||
// reload the tasks
|
||||
tasks.reload();
|
||||
}
|
||||
|
||||
async function sendDeleteTask(id: number | undefined) {
|
||||
if (id !== undefined) {
|
||||
const result = await apiCall("DELETE", "tasks", { id });
|
||||
|
||||
if (result.ok) {
|
||||
tasks.reload();
|
||||
setDeleteTask(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const topContent = (
|
||||
<>
|
||||
<Button
|
||||
@@ -89,7 +112,7 @@ export default function Tasks() {
|
||||
>
|
||||
<TableHeader>
|
||||
<TableColumn allowsSorting key="userName">
|
||||
Text
|
||||
Name
|
||||
</TableColumn>
|
||||
<TableColumn allowsSorting key="admin" align="center">
|
||||
Enabled
|
||||
@@ -101,21 +124,33 @@ export default function Tasks() {
|
||||
<TableBody items={tasks.items}>
|
||||
{(task) => (
|
||||
<TableRow key={task.id}>
|
||||
<TableCell>{task.text}</TableCell>
|
||||
<TableCell>{task.name}</TableCell>
|
||||
<TableCell>
|
||||
<Checkbox isSelected={task.enabled} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={() => setEditTask(task)}
|
||||
>
|
||||
<Tooltip content="Edit task">
|
||||
<Edit />
|
||||
</Tooltip>
|
||||
</Button>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={() => setEditTask(task)}
|
||||
>
|
||||
<Tooltip content="Edit task">
|
||||
<Edit />
|
||||
</Tooltip>
|
||||
</Button>
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
size="sm"
|
||||
onPress={() => setDeleteTask(task)}
|
||||
color="danger"
|
||||
className="text-danger"
|
||||
>
|
||||
<TrashCan />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
@@ -125,15 +160,32 @@ export default function Tasks() {
|
||||
<AddTask
|
||||
isOpen={showAddTask}
|
||||
onOpenChange={setShowAddTask}
|
||||
onSuccess={tasks.reload}
|
||||
onSuccess={reload}
|
||||
/>
|
||||
|
||||
<EditTask
|
||||
value={editTask}
|
||||
isOpen={!!editTask}
|
||||
onOpenChange={(isOpen) => (!isOpen ? setEditTask(undefined) : null)}
|
||||
onSuccess={tasks.reload}
|
||||
onSuccess={reload}
|
||||
/>
|
||||
|
||||
<DeleteConfirmation
|
||||
isOpen={!!deleteTask}
|
||||
onOpenChange={(isOpen) => (!isOpen ? setDeleteTask(undefined) : null)}
|
||||
header="Delete Task"
|
||||
onDelete={() => sendDeleteTask(deleteTask?.id)}
|
||||
>
|
||||
{!!deleteTask ? (
|
||||
<>
|
||||
The task{" "}
|
||||
<span className="font-numbers text-accent-1">
|
||||
{deleteTask.name}
|
||||
</span>{" "}
|
||||
will be deleted.
|
||||
</>
|
||||
) : null}
|
||||
</DeleteConfirmation>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user