Skip to content

Commit

Permalink
add and delete dependencies (#1)
Browse files Browse the repository at this point in the history
* add and delete dependencies

* move delete to edit view

* improve link behavior
  • Loading branch information
chribjel authored Sep 16, 2024
1 parent bcaf59e commit a859bc1
Show file tree
Hide file tree
Showing 8 changed files with 364 additions and 124 deletions.
4 changes: 3 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
},
"files": {
"ignoreUnknown": false,
"ignore": []
"ignore": [
"src/routeTree.gen.ts"
]
},
"formatter": {
"enabled": true,
Expand Down
21 changes: 7 additions & 14 deletions src/components/breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { useFunction } from "@/hooks/use-function";
import { Route } from "@/routes";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
Skeleton,
} from "@kvib/react";
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from "@kvib/react";
import { Link as TSRLink } from "@tanstack/react-router";

type BreadcrumbsProps = {
Expand All @@ -32,14 +27,12 @@ function CustomBreadcrumbItem({ functionId }: BreadcrumbItemProps) {
const { path } = Route.useSearch();

return (
<Skeleton isLoaded={!!func.data} fitContent w={24}>
<BreadcrumbItem>
<BreadcrumbLink isCurrentPage={path === func.data?.path}>
<TSRLink to={Route.to} search={{ path: func.data?.path }}>
{func.data?.name}
</TSRLink>
<BreadcrumbItem>
<TSRLink to={Route.to} search={{ path: func.data?.path }}>
<BreadcrumbLink as="span" isCurrentPage={path === func.data?.path}>
{func.data?.name}
</BreadcrumbLink>
</BreadcrumbItem>
</Skeleton>
</TSRLink>
</BreadcrumbItem>
);
}
173 changes: 127 additions & 46 deletions src/components/function-edit-view.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { useQueryClient } from "@tanstack/react-query";
import { useFunction } from "@/hooks/use-function";
import type { BackendFunction } from "@/services/backend";
import { Button } from "@kvib/react";
import { getIdsFromPath } from "@/lib/utils";
import { Route } from "@/routes";
import { type BackendFunction, getFunctions } from "@/services/backend";
import {
Button,
FormControl,
FormLabel,
Icon,
Input,
SearchAsync,
Textarea,
} from "@kvib/react";
import { useCallback, useState } from "react";

type FunctionEditViewProps = {
functionId: number;
Expand All @@ -12,58 +22,129 @@ export function FunctionEditView({
functionId,
onEditComplete,
}: FunctionEditViewProps) {
const queryClient = useQueryClient();
const { func } = useFunction(functionId);
const { path } = Route.useSearch();
const { func, removeChild, dependencies, addDependency, removeDependency } =
useFunction(functionId, {
includeDependencies: true,
});
const [newDependencies, setDependencies] = useState<
{ label: string; value: number }[]
>(
dependencies.data?.map((dependency) => ({
label: dependency.name,
value: dependency.id,
})) ?? [],
);

const navigate = Route.useNavigate();

const selectedFunctionIds = getIdsFromPath(path);

const handleDeletedFunction = useCallback(
(deletedFunction: BackendFunction) => {
if (selectedFunctionIds.includes(deletedFunction.id)) {
const deletedFunctionParentPath = deletedFunction.path
.split(".")
.slice(0, -1)
.join(".");
navigate({
search: {
path: deletedFunctionParentPath ?? "1",
},
});
return;
}
},
[selectedFunctionIds, navigate],
);

return (
<form
className="flex flex-col gap-2"
onSubmit={async (e) => {
onSubmit={(e) => {
e.preventDefault();
if (!func.data) return;
const form = e.target as HTMLFormElement;
const nameElement = form.elements.namedItem(
"name",
) as HTMLInputElement | null;
const descriptionElement = form.elements.namedItem(
"description",
) as HTMLTextAreaElement | null;

const nameValue =
nameElement?.value === "" ? undefined : nameElement?.value;
const descriptionValue =
descriptionElement?.value === ""
? undefined
: descriptionElement?.value;
const dependenciesToCreate = newDependencies.filter(
(dependency) =>
!dependencies.data?.map((dep) => dep.id).includes(dependency.value),
);
const dependenciesToDelete =
dependencies.data?.filter(
(dependency) =>
!newDependencies.map((dep) => dep.value).includes(dependency.id),
) ?? [];

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const putObject: BackendFunction = {
...func.data,
...(nameValue && { name: nameValue }),
...(descriptionValue && { description: descriptionValue }),
};

/* await putFunction(putObject).then(() => {
queryClient.invalidateQueries({
queryKey: ['functions', functionId],
});
onEditComplete?.();
}) */
for (const dependency of dependenciesToDelete) {
removeDependency.mutate({
functionId,
dependencyFunctionId: dependency.id,
});
}
for (const dependency of dependenciesToCreate) {
addDependency.mutate({
functionId: functionId,
dependencyFunctionId: dependency.value,
});
}
onEditComplete?.();
}}
>
<input
type="text"
name="name"
placeholder="Navn"
required
defaultValue={func.data?.name}
/>
<textarea
name="description"
placeholder="Beskrivelse"
defaultValue={func.data?.description ?? ""}
/>
<Button type="submit">Lagre</Button>
<FormControl className="flex flex-col gap-2">
<FormLabel htmlFor="name">Navn</FormLabel>
<Input
disabled
type="text"
name="name"
placeholder="Navn"
required
defaultValue={func.data?.name}
/>

<FormLabel htmlFor="description">Beskrivelse</FormLabel>
<Textarea
disabled
name="description"
placeholder="Beskrivelse"
defaultValue={func.data?.description ?? ""}
/>

<FormLabel htmlFor="async-search">Legg til avhengigheter</FormLabel>
<SearchAsync
value={newDependencies}
isMulti
debounceTime={100}
defaultOptions
dropdownIndicator={<Icon icon="expand_more" weight={400} />}
loadOptions={(inputValue, callback) => {
getFunctions(inputValue).then((functions) => {
const depOpts = functions.map((functionData) => ({
label: functionData.name,
value: functionData.id,
}));
// @ts-expect-error
callback(depOpts);
});
}}
onChange={(newValue) => {
// @ts-expect-error
setDependencies(newValue ?? []);
}}
placeholder="Søk"
/>

<Button type="submit">Lagre</Button>
<Button
colorScheme="red"
disabled={!func.data}
onClick={() => {
if (!func.data) return;
removeChild.mutate(func.data.id);
handleDeletedFunction(func.data);
}}
>
Slett
</Button>
</FormControl>
</form>
);
}
9 changes: 8 additions & 1 deletion src/components/function-folder.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useFunction } from "@/hooks/use-function";
import { cn, getIdsFromPath } from "@/lib/utils";
import { Route } from "@/routes";
import { Button, Input, Skeleton } from "@kvib/react";
import { Link as TSRLink } from "@tanstack/react-router";
Expand All @@ -8,12 +9,15 @@ type FunctionFolderProps = {
};

export function FunctionFolder({ functionId }: FunctionFolderProps) {
const { path } = Route.useSearch();
const { func, children, addChild } = useFunction(functionId, {
includeChildren: true,
});

const navigate = Route.useNavigate();

const selectedFunctionIds = getIdsFromPath(path);

return (
<div className="p-2 flex flex-col gap-2">
<h2 className="text-xl font-bold">
Expand Down Expand Up @@ -52,7 +56,10 @@ export function FunctionFolder({ functionId }: FunctionFolderProps) {
className="bg-transparent w-full"
>
<TSRLink
className="flex w-full p-2 text-start data-[status=active]:bg-green-200"
className={cn(
"flex w-full p-2 text-start data-[status=active]:bg-green-200",
selectedFunctionIds.includes(child.id) ? "bg-green-200" : "",
)}
to={Route.to}
search={{ path: child.path }}
>
Expand Down
34 changes: 18 additions & 16 deletions src/components/function-info-view.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useFunction } from "@/hooks/use-function";
import { Route } from "@/routes";
import { Link as TSRLink } from "@tanstack/react-router";
import { Skeleton, Link as KvibLink } from "@kvib/react";
import { Skeleton, Link as KvibLink, List, ListItem } from "@kvib/react";

type FunctionInfoViewProps = {
functionId: number;
Expand All @@ -18,26 +18,28 @@ export function FunctionInfoView({ functionId }: FunctionInfoViewProps) {
<div className="p-2">
<p>Funksjonsnavn: {func.data?.name}</p>
<p>Beskrivelse: {func.data?.description}</p>
<p className="font-bold">Bruker</p>
{dependencies.data?.map((dependency) => (
<div key={dependency.id}>
<KvibLink>

<p className="font-bold">Avhengig av</p>
<List>
{dependencies.data?.map((dependency) => (
<ListItem key={dependency.id}>
<TSRLink to={Route.to} search={{ path: dependency.path }}>
{dependency.name}
<KvibLink as="span">{dependency.name}</KvibLink>
</TSRLink>
</KvibLink>
</div>
))}
</ListItem>
))}
</List>

<p className="font-bold">Brukes av</p>
{dependents.data?.map((dependent) => (
<div key={dependent.id}>
<KvibLink>
<List>
{dependents.data?.map((dependent) => (
<ListItem key={dependent.id}>
<TSRLink to={Route.to} search={{ path: dependent.path }}>
{dependent.name}
<KvibLink as="span">{dependent.name}</KvibLink>
</TSRLink>
</KvibLink>
</div>
))}
</ListItem>
))}
</List>
</div>
</Skeleton>
);
Expand Down
Loading

0 comments on commit a859bc1

Please sign in to comment.