Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: connect permission page to api #41

Merged
merged 4 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions components/ArkeSearch/ArkeSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2024 Arkemis S.r.l.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Autocomplete } from "@arkejs/ui";
manolo-battista marked this conversation as resolved.
Show resolved Hide resolved
import { TUnit } from "@arkejs/client";
import React, { useEffect, useState } from "react";
import useDebounce from "@/hooks/useDebounce";
import useClient from "@/arke/useClient";

export default function ArkeSearch({
value,
onChange,
}: {
value: TUnit;
onChange: (value: TUnit) => void;
}) {
const client = useClient();
const [inputValue, setInputValue] = useState("");
const [values, setValues] = useState<TUnit[]>([]);
const debouncedInputValue = useDebounce<string>(inputValue, 500);

useEffect(() => {
if (debouncedInputValue) {
client.arke
.getAll({
params: {
offset: 0,
limit: 5,
filter: `and(contains(id,${debouncedInputValue}))`,
order: `label;asc`,
},
})
.then((res) => {
setValues(res.data.content.items);
});
}
}, [debouncedInputValue]);

return (
<Autocomplete
onChange={onChange}
onInputChange={(event) => setInputValue(event.target.value)}
values={values}
value={value}
renderChips={false}
placeholder="Search an Arke or Group"
renderValue={(value) => (value as TUnit).label ?? (value as TUnit).id}
/>
);
}
131 changes: 131 additions & 0 deletions components/Permissions/AddPermissionDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2024 Arkemis S.r.l.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Button, Dialog, Switch } from "@arkejs/ui";
manolo-battista marked this conversation as resolved.
Show resolved Hide resolved
import { Field, Form, useForm } from "@arkejs/form";
import ArkeSearch from "@/components/ArkeSearch/ArkeSearch";
import toast from "react-hot-toast";
import useClient from "@/arke/useClient";
import { TUnit } from "@arkejs/client";

type Schema = {
parent?: TUnit;
get?: string;
post?: string;
put?: string;
delete?: string;
filter?: string;
};

const fields: Field[] = [
{ id: "parent" },
{ id: "get", type: "boolean", label: "GET", value: false },
{ id: "post", type: "boolean", label: "POST", value: false },
{ id: "put", type: "boolean", label: "PUT", value: false },
{ id: "delete", type: "boolean", label: "DELETE", value: false },
{ id: "filter", type: "string", label: "Filter" },
];

export default function AddPermissionDialog({
roleID,
open,
onClose,
...props
}: {
roleID: string | null;
open: boolean;
onClose: () => void;
onSubmit: (roleID: string) => void;
}) {
const client = useClient();
const {
formProps,
methods: { reset },
} = useForm({ fields });

const onSubmit = async (data: Schema) => {
if (!data?.parent?.id || !roleID) return;

try {
await client.unit.topology.addLink(
{ arkeId: "arke", id: roleID },
"permission",
{ arkeId: "arke", id: data.parent.id },
{
metadata: {
get: data.get,
post: data.post,
put: data.put,
delete: data.delete,
filter: data.filter || null,
},
}
);

// reset on close
reset();
props.onSubmit(roleID);
toast.success(`Permissions added to ${roleID}`);
} catch (err) {
toast.error("Something went wrong");
}
};

const handleClose = () => {
onClose();
reset();
};

return (
<Dialog
open={open}
onClose={handleClose}
title={`Add permission to ${roleID}`}
>
<Form
{...formProps}
onSubmit={onSubmit}
components={{
boolean: ({ field }) => (
<Switch color="primary" {...field} checked={!!field.value} />
),
}}
>
<div className="grid gap-4">
<Form.Field
id="parent"
render={({ field }) => <ArkeSearch {...field} />}
/>
<div className="grid grid-cols-4 gap-4">
<Form.Field id="get" />
<Form.Field id="post" />
<Form.Field id="put" />
<Form.Field id="delete" />
</div>
<Form.Field id="filter" />
</div>
<div className="mt-4 flex gap-4">
<Button className="btn-outlined w-full bg-neutral" onClick={onClose}>
Close
</Button>
<Button className="w-full" color="primary" type="submit">
Confirm
</Button>
</div>
</Form>
</Dialog>
);
}
68 changes: 45 additions & 23 deletions components/Permissions/PermissionInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,56 @@
* limitations under the License.
*/
import useClient from "@/arke/useClient";
import { Input } from "@arkejs/ui";
import { useRef, useState } from "react";
import useOnClickOutside from "@/hooks/useOnClickOutside";
import { Form, useForm } from "@arkejs/form";
import { Button } from "@arkejs/ui";
import { CheckIcon } from "@heroicons/react/20/solid";
import toast from "react-hot-toast";
import { TUnit } from "@arkejs/client";

interface PermissionInputProps {
role: string;
type PermissionInputProps = {
roleID: string;
value: string;
}
export function PermissionInput(props: PermissionInputProps) {
unitID: string;
} & TUnit<true>;

export function PermissionInput({
roleID,
unitID,
value,
link,
}: PermissionInputProps) {
const client = useClient();
const [value, setValue] = useState(props.value);
const inputRef = useRef<HTMLDivElement>(null);
const {
formProps,
methods: { handleSubmit },
} = useForm({
fields: [{ id: "filter", type: "string", value: value }],
});

function onUpdateData() {
console.log(value);
}
const onSubmit = async (data: Record<string, unknown>) => {
try {
await client.unit.topology.editLink(
{ arkeId: "arke", id: roleID },
"permission",
{ arkeId: "arke", id: unitID },
{ metadata: { ...link?.metadata, filter: data.filter } }
);
toast.success(`Filter updated`);
} catch (err) {
toast.error("Something went wrong");
}
};

useOnClickOutside(inputRef, onUpdateData);
return (
<Input
{...props}
label=""
// @ts-ignore
itemRef={inputRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && onUpdateData()}
onBlur={onUpdateData}
/>
<Form
onSubmit={handleSubmit(onSubmit)}
className="flex items-center gap-2"
{...formProps}
>
<Form.Field id="filter" />
<Button type="submit" color="primary" className="h-full">
<CheckIcon className="h-5 w-5" />
</Button>
</Form>
);
}
40 changes: 26 additions & 14 deletions components/Permissions/PermissionSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,40 @@ import useClient from "@/arke/useClient";
import { Switch } from "@arkejs/ui";
import { useState } from "react";
import toast from "react-hot-toast";
import { TUnit } from "@arkejs/client";

interface PermissionSwitchProps {
role: string;
type PermissionSwitchProps = {
roleID: string;
unitID: string;
method: string;
checked: boolean;
}
} & TUnit<true>;

export function PermissionSwitch(props: PermissionSwitchProps) {
export function PermissionSwitch({
roleID,
unitID,
method,
link,
}: PermissionSwitchProps) {
const [checked, setChecked] = useState(link?.metadata[method]);
const client = useClient();
const { role, method } = props;
const [checked, setChecked] = useState(props.checked);
function onChange(status: boolean) {
console.log(role, method, status);
/*client.unit.edit("arke", role, {}).then((res) => console.log(res.data));
setChecked(!checked);
toast.success(`Permission ${role} (${method.toUpperCase()}) updated`);*/
// TODO: reset check if api call failed
async function onChange(value: boolean) {
try {
await client.unit.topology.editLink(
{ arkeId: "arke", id: roleID },
"permission",
{ arkeId: "arke", id: unitID },
{ metadata: { ...link?.metadata, [method]: value } }
);
setChecked(value);
toast.success(`Permission ${roleID} (${method.toUpperCase()}) updated`);
} catch (err) {
toast.error("Something went wrong");
}
}

return (
<Switch
checked={checked}
checked={checked as boolean}
onChange={() => onChange(!checked)}
color="primary"
/>
Expand Down
30 changes: 15 additions & 15 deletions components/Permissions/PermissionTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Client, LinkDirection, TUnit } from "@arkejs/client";
import useClient from "@/arke/useClient";
import { CrudState } from "@/types/crud";
import { Filter, Sort, useTable } from "@arkejs/table";
import { columns } from "@/crud/permission/columns";
import { getColumns } from "@/crud/permission/columns";
import { Table } from "@/components/Table";
import { AddIcon } from "@/components/Icon";
import { Button } from "@arkejs/ui";
Expand Down Expand Up @@ -53,13 +53,25 @@ const fetchArkePermission = async (
);
};

export function PermissionTable(props: { role: string }) {
const { role } = props;
export function PermissionTable({ role }: { role: string }) {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState<TUnit[] | undefined>([]);
const [count, setCount] = useState<number | undefined>(0);
const client = useClient();

const loadData = useCallback(
(page?: number, filters?: Filter[], sort?: Sort[]) => {
setIsLoading(true);
fetchArkePermission(role, client, page, filters, sort).then((res) => {
setData(res.data.content.items);
setCount(res.data.content.count);
setIsLoading(false);
});
},
[]
);
const columns = getColumns(role);

const [crud, setCrud] = useState<CrudState>({
add: false,
edit: false,
Expand Down Expand Up @@ -92,18 +104,6 @@ export function PermissionTable(props: { role: string }) {
: null
);

const loadData = useCallback(
(page?: number, filters?: Filter[], sort?: Sort[]) => {
setIsLoading(true);
fetchArkePermission(role, client, page, filters, sort).then((res) => {
setData(res.data.content.items);
setCount(res.data.content.count);
setIsLoading(false);
});
},
[]
);

useEffect(() => {
loadData();
}, []);
Expand Down
Loading
Loading