Skip to content

Commit

Permalink
[OPIK-527] [FR]: Add support for Project descriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
andriidudar committed Dec 5, 2024
1 parent c9b5cdf commit 3d05ce3
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 96 deletions.
20 changes: 5 additions & 15 deletions apps/opik-frontend/src/api/projects/useProjectCreateMutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,19 @@ import { useToast } from "@/components/ui/use-toast";

type UseProjectCreateMutationParams = {
project: Partial<Project>;
workspaceName: string;
};

const useProjectCreateMutation = () => {
const queryClient = useQueryClient();
const { toast } = useToast();

return useMutation({
mutationFn: async ({
project,
workspaceName,
}: UseProjectCreateMutationParams) => {
mutationFn: async ({ project }: UseProjectCreateMutationParams) => {
const { data } = await api.post(PROJECTS_REST_ENDPOINT, {
...project,
workspace_name: workspaceName,
});
return data;
},
onMutate: async (params: UseProjectCreateMutationParams) => {
return {
queryKey: ["projects", { workspaceName: params.workspaceName }],
};
},
onError: (error: AxiosError) => {
const message = get(
error,
Expand All @@ -43,10 +33,10 @@ const useProjectCreateMutation = () => {
variant: "destructive",
});
},
onSettled: (data, error, variables, context) => {
if (context) {
return queryClient.invalidateQueries({ queryKey: context.queryKey });
}
onSettled: () => {
return queryClient.invalidateQueries({
queryKey: ["projects"],
});
},
});
};
Expand Down
46 changes: 46 additions & 0 deletions apps/opik-frontend/src/api/projects/useProjectUpdateMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { AxiosError } from "axios";
import get from "lodash/get";

import api, { PROJECTS_REST_ENDPOINT } from "@/api/api";
import { Project } from "@/types/projects";
import { useToast } from "@/components/ui/use-toast";

type UseProjectUpdateMutationParams = {
project: Partial<Project>;
};

const useProjectUpdateMutation = () => {
const queryClient = useQueryClient();
const { toast } = useToast();

return useMutation({
mutationFn: async ({ project }: UseProjectUpdateMutationParams) => {
const { data } = await api.patch(
PROJECTS_REST_ENDPOINT + project.id,
project,
);
return data;
},
onError: (error: AxiosError) => {
const message = get(
error,
["response", "data", "message"],
error.message,
);

toast({
title: "Error",
description: message,
variant: "destructive",
});
},
onSettled: () => {
return queryClient.invalidateQueries({
queryKey: ["projects"],
});
},
});
};

export default useProjectUpdateMutation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useCallback, useState } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import useProjectCreateMutation from "@/api/projects/useProjectCreateMutation";
import { Project } from "@/types/projects";
import { Textarea } from "@/components/ui/textarea";
import useProjectUpdateMutation from "@/api/projects/useProjectUpdateMutation";

type AddEditProjectDialogProps = {
project?: Project;
open: boolean;
setOpen: (open: boolean) => void;
};

const AddEditProjectDialog: React.FC<AddEditProjectDialogProps> = ({
project,
open,
setOpen,
}) => {
const { mutate: createMutate } = useProjectCreateMutation();
const { mutate: updateMutate } = useProjectUpdateMutation();
const [name, setName] = useState(project ? project.name : "");
const [description, setDescription] = useState(
project ? project.description : "",
);

const isEdit = Boolean(project);
const isValid = Boolean(name.length);
const title = isEdit ? "Edit project" : "Create a new project";
const buttonText = isEdit ? "Update project" : "Create project";

const submitHandler = useCallback(() => {
if (isEdit) {
updateMutate({
project: {
id: project!.id,
description,
},
});
} else {
createMutate({
project: {
name,
...(description && { description }),
},
});
}
}, [createMutate, description, isEdit, name, project, updateMutate]);

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="max-w-lg sm:max-w-[560px]">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-2 pb-4">
<Label htmlFor="projectName">Name</Label>
<Input
id="projectName"
placeholder="Project name"
disabled={isEdit}
value={name}
onChange={(event) => setName(event.target.value)}
/>
</div>
<div className="flex flex-col gap-2 pb-4">
<Label htmlFor="projectDescription">Description</Label>
<Textarea
id="projectDescription"
placeholder="Project description"
value={description}
onChange={(event) => setDescription(event.target.value)}
maxLength={255}
/>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<DialogClose asChild>
<Button type="submit" disabled={!isValid} onClick={submitHandler}>
{buttonText}
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
};

export default AddEditProjectDialog;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { MoreHorizontal, Trash } from "lucide-react";
import { MoreHorizontal, Pencil, Trash } from "lucide-react";
import React, { useCallback, useRef, useState } from "react";
import { Project } from "@/types/projects";
import { CellContext } from "@tanstack/react-table";
import AddEditProjectDialog from "@/components/pages/ProjectsPage/AddEditProjectDialog";
import ConfirmDialog from "@/components/shared/ConfirmDialog/ConfirmDialog";
import useProjectDeleteMutation from "@/api/projects/useProjectDeleteMutation";
import CellWrapper from "@/components/shared/DataTableCells/CellWrapper";
Expand All @@ -18,26 +19,31 @@ export const ProjectRowActionsCell: React.FC<CellContext<Project, unknown>> = (
) => {
const resetKeyRef = useRef(0);
const project = context.row.original;
const [open, setOpen] = useState<boolean>(false);
const [open, setOpen] = useState<boolean | number>(false);

const projectDeleteMutation = useProjectDeleteMutation();
const { mutate } = useProjectDeleteMutation();

const deleteProjectHandler = useCallback(() => {
projectDeleteMutation.mutate({
mutate({
projectId: project.id,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [project.id]);
}, [project.id, mutate]);

return (
<CellWrapper
metadata={context.column.columnDef.meta}
tableMetadata={context.table.options.meta}
className="justify-end p-0"
>
<AddEditProjectDialog
key={`add-${resetKeyRef.current}`}
project={project}
open={open === 2}
setOpen={setOpen}
/>
<ConfirmDialog
key={`delete-${resetKeyRef.current}`}
open={open}
open={open === 1}
setOpen={setOpen}
onConfirm={deleteProjectHandler}
title={`Delete ${project.name}`}
Expand All @@ -54,7 +60,16 @@ export const ProjectRowActionsCell: React.FC<CellContext<Project, unknown>> = (
<DropdownMenuContent align="end" className="w-52">
<DropdownMenuItem
onClick={() => {
setOpen(true);
setOpen(2);
resetKeyRef.current = resetKeyRef.current + 1;
}}
>
<Pencil className="mr-2 size-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setOpen(1);
resetKeyRef.current = resetKeyRef.current + 1;
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ResourceCell from "@/components/shared/DataTableCells/ResourceCell";
import useProjectsList from "@/api/projects/useProjectsList";
import { Project } from "@/types/projects";
import Loader from "@/components/shared/Loader/Loader";
import AddProjectDialog from "@/components/pages/ProjectsPage/AddProjectDialog";
import AddEditProjectDialog from "@/components/pages/ProjectsPage/AddEditProjectDialog";
import ProjectsActionsPanel from "@/components/pages/ProjectsPage/ProjectsActionsPanel";
import { ProjectRowActionsCell } from "@/components/pages/ProjectsPage/ProjectRowActionsCell";
import { Button } from "@/components/ui/button";
Expand Down Expand Up @@ -71,6 +71,11 @@ export const DEFAULT_COLUMNS: ColumnData<Project>[] = [
label: "Created by",
type: COLUMN_TYPE.string,
},
{
id: "description",
label: "Description",
type: COLUMN_TYPE.string,
},
];

export const DEFAULT_COLUMN_PINNING: ColumnPinningState = {
Expand All @@ -81,6 +86,7 @@ export const DEFAULT_COLUMN_PINNING: ColumnPinningState = {
export const DEFAULT_SELECTED_COLUMNS: string[] = [
"last_updated_at",
"created_at",
"description",
];

export const DEFAULT_SORTING_COLUMNS: ColumnSort[] = [
Expand Down Expand Up @@ -264,7 +270,7 @@ const ProjectsPage: React.FunctionComponent = () => {
total={total}
></DataTablePagination>
</div>
<AddProjectDialog
<AddEditProjectDialog
key={resetDialogKeyRef.current}
open={openDialog}
setOpen={setOpenDialog}
Expand Down

0 comments on commit 3d05ce3

Please sign in to comment.