mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
add table view save settings.
This commit is contained in:
parent
c833bcb37a
commit
fd15e66f83
@ -380,7 +380,7 @@ export default class TableView extends BaseModel {
|
||||
unique: false,
|
||||
nullable: false,
|
||||
})
|
||||
public filters?: Query<BaseModel> = undefined;
|
||||
public query?: Query<BaseModel> = undefined;
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
|
294
Common/UI/Components/ModelTable/TableView.tsx
Normal file
294
Common/UI/Components/ModelTable/TableView.tsx
Normal file
@ -0,0 +1,294 @@
|
||||
import React, {
|
||||
FunctionComponent,
|
||||
ReactElement,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import TableView from "../../../Models/DatabaseModels/TableView";
|
||||
import ObjectID from "../../../Types/ObjectID";
|
||||
import MoreMenu from "../MoreMenu/MoreMenu";
|
||||
import MoreMenuItem from "../MoreMenu/MoreMenuItem";
|
||||
import ListResult from "../../Utils/BaseDatabase/ListResult";
|
||||
import ModelAPI from "../../Utils/ModelAPI/ModelAPI";
|
||||
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
|
||||
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
|
||||
import API from "../../../Utils/API";
|
||||
import MoreMenuSection from "../MoreMenu/MoreMenuSection";
|
||||
import Button, { ButtonStyleType } from "../Button/Button";
|
||||
import IconProp from "../../../Types/Icon/IconProp";
|
||||
import { BarLoader } from "react-spinners";
|
||||
import ConfirmModal from "../Modal/ConfirmModal";
|
||||
import ModelFormModal from "../ModelFormModal/ModelFormModal";
|
||||
import { FormType } from "../Forms/ModelForm";
|
||||
import FormFieldSchemaType from "../Forms/Types/FormFieldSchemaType";
|
||||
import { PromiseVoidFunction } from "../../../Types/FunctionTypes";
|
||||
import { GetReactElementFunction } from "../../Types/FunctionTypes";
|
||||
|
||||
export interface ComponentProps {
|
||||
tableId: string;
|
||||
onViewChange: (tableView: TableView | null) => void;
|
||||
currentTableView: TableView | null;
|
||||
projectId: ObjectID;
|
||||
userId: ObjectID;
|
||||
}
|
||||
|
||||
const TableViewElement: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [allTableViews, setAllTableViews] = useState<Array<TableView>>([]);
|
||||
const [tableViewToDelete, setTableViewToDelete] = useState<
|
||||
TableView | undefined
|
||||
>(undefined);
|
||||
const [tableViewToEdit, setTableViewToEdit] = useState<TableView | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [showCreateNewViewModal, setShowCreateNewViewModel] =
|
||||
useState<boolean>(false);
|
||||
|
||||
// load all the filters for this user and for this project.
|
||||
const fetchTableViews: PromiseVoidFunction = async (): Promise<void> => {
|
||||
try {
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
const tableViews: ListResult<TableView> = await ModelAPI.getList({
|
||||
modelType: TableView,
|
||||
query: {
|
||||
projectId: props.projectId,
|
||||
createdByUserId: props.userId,
|
||||
tableId: props.tableId,
|
||||
},
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
select: {
|
||||
sort: true,
|
||||
itemsOnPage: true,
|
||||
query: true,
|
||||
name: true,
|
||||
},
|
||||
sort: {
|
||||
name: SortOrder.Ascending,
|
||||
},
|
||||
});
|
||||
|
||||
setAllTableViews(tableViews.data);
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyErrorMessage(err as Error));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
type DeleteTableViewFunction = (tableView: TableView) => Promise<void>;
|
||||
|
||||
const deleteTableView: DeleteTableViewFunction = async (
|
||||
tableView: TableView,
|
||||
): Promise<void> => {
|
||||
const tableViewId: ObjectID = tableView.id!;
|
||||
|
||||
try {
|
||||
setError("");
|
||||
setIsLoading(true);
|
||||
|
||||
await ModelAPI.deleteItem({
|
||||
modelType: TableView,
|
||||
id: tableViewId,
|
||||
});
|
||||
|
||||
await fetchTableViews();
|
||||
} catch (err) {
|
||||
setError(API.getFriendlyErrorMessage(err as Error));
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchTableViews().catch((err: Error) => {
|
||||
setError(API.getFriendlyErrorMessage(err as Error));
|
||||
});
|
||||
}, []);
|
||||
|
||||
type GetRightElementForTableViewMenuItemFunction = (
|
||||
item: TableView,
|
||||
) => React.JSX.Element;
|
||||
|
||||
const getRightElementForTableViewMenuItem: GetRightElementForTableViewMenuItemFunction =
|
||||
(item: TableView): ReactElement => {
|
||||
return (
|
||||
<div className="flex">
|
||||
<Button
|
||||
icon={IconProp.Edit}
|
||||
buttonStyle={ButtonStyleType.ICON_LIGHT}
|
||||
onClick={() => {
|
||||
setTableViewToEdit(item);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
icon={IconProp.Trash}
|
||||
buttonStyle={ButtonStyleType.ICON_LIGHT}
|
||||
onClick={() => {
|
||||
setTableViewToDelete(item);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type GetViewItemsFunction = () => Array<ReactElement>;
|
||||
|
||||
const getViewItems: GetViewItemsFunction = (): Array<ReactElement> => {
|
||||
return allTableViews.map((item: TableView, index: number) => {
|
||||
return (
|
||||
<MoreMenuItem
|
||||
key={index}
|
||||
rightElement={getRightElementForTableViewMenuItem(item)}
|
||||
text={item.name || ""}
|
||||
onClick={() => {
|
||||
props.onViewChange && props.onViewChange(item);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const getMenuContents: GetReactElementFunction = (): ReactElement => {
|
||||
if (isLoading) {
|
||||
return <BarLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{allTableViews.length > 0 ? (
|
||||
<MoreMenuSection title="Saved Views">
|
||||
{getViewItems()}
|
||||
</MoreMenuSection>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<MoreMenuItem text="Save View" onClick={() => {}}></MoreMenuItem>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ConfirmModal
|
||||
title={`Something went wrong...`}
|
||||
description={error}
|
||||
isLoading={false}
|
||||
submitButtonText={"Close"}
|
||||
onSubmit={() => {
|
||||
return setError("");
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (tableViewToEdit) {
|
||||
return (
|
||||
<ModelFormModal<TableView>
|
||||
modelType={TableView}
|
||||
modelIdToEdit={tableViewToEdit.id!}
|
||||
name="Edit View"
|
||||
title="Edit View"
|
||||
description="You can rename this table view to any name you like."
|
||||
onClose={() => {
|
||||
setTableViewToEdit(undefined);
|
||||
}}
|
||||
submitButtonText="Save Changes"
|
||||
onSuccess={async () => {
|
||||
setTableViewToEdit(undefined);
|
||||
await fetchTableViews();
|
||||
}}
|
||||
formProps={{
|
||||
name: "Edit View",
|
||||
modelType: TableView,
|
||||
id: "edit-table-view",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "Name of the view",
|
||||
description: "Please enter the new name of the view",
|
||||
title: "Name",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
formType: FormType.Update,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (tableViewToDelete) {
|
||||
return (
|
||||
<ConfirmModal
|
||||
description={`Are you sure you want to delete view - ${tableViewToDelete.name}?`}
|
||||
title={`Delete ${tableViewToDelete.name}`}
|
||||
isLoading={isLoading}
|
||||
onSubmit={async () => {
|
||||
await deleteTableView(tableViewToDelete);
|
||||
setTableViewToDelete(undefined);
|
||||
}}
|
||||
onClose={() => {
|
||||
setTableViewToDelete(undefined);
|
||||
}}
|
||||
submitButtonText={`Delete`}
|
||||
submitButtonType={ButtonStyleType.DANGER}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (showCreateNewViewModal) {
|
||||
return (
|
||||
<ModelFormModal<TableView>
|
||||
modelType={TableView}
|
||||
name="Save New View"
|
||||
title="Save New View"
|
||||
description="You can save the current table settings as a new view."
|
||||
onClose={() => {
|
||||
setShowCreateNewViewModel(false);
|
||||
}}
|
||||
submitButtonText="Save Changes"
|
||||
onBeforeCreate={(tableView: TableView) => {
|
||||
tableView.query = props.currentTableView?.query || {};
|
||||
tableView.itemsOnPage = props.currentTableView?.itemsOnPage || 10;
|
||||
tableView.sort = props.currentTableView?.sort || {};
|
||||
return Promise.resolve(tableView);
|
||||
}}
|
||||
onSuccess={async () => {
|
||||
setShowCreateNewViewModel(false);
|
||||
await fetchTableViews();
|
||||
}}
|
||||
formProps={{
|
||||
name: "Save New View",
|
||||
modelType: TableView,
|
||||
id: "save-table-view",
|
||||
fields: [
|
||||
{
|
||||
field: {
|
||||
name: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Text,
|
||||
placeholder: "Name of the view",
|
||||
description: "Please enter the new name of the view",
|
||||
title: "Name",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
formType: FormType.Create,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <MoreMenu>{getMenuContents()}</MoreMenu>;
|
||||
};
|
||||
|
||||
export default TableViewElement;
|
@ -1,7 +1,7 @@
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
|
||||
const MoreMenuItem: FunctionComponent = (): ReactElement => {
|
||||
const MoreMenuDivider: FunctionComponent = (): ReactElement => {
|
||||
return <div className="py-1" role="none"></div>;
|
||||
};
|
||||
|
||||
export default MoreMenuItem;
|
||||
export default MoreMenuDivider;
|
||||
|
@ -6,6 +6,7 @@ export interface ComponentProps {
|
||||
icon?: IconProp | undefined;
|
||||
text: string;
|
||||
onClick: () => void;
|
||||
rightElement?: Array<ReactElement> | ReactElement | undefined;
|
||||
}
|
||||
|
||||
const MoreMenuItem: FunctionComponent<ComponentProps> = (
|
||||
@ -25,7 +26,10 @@ const MoreMenuItem: FunctionComponent<ComponentProps> = (
|
||||
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
||||
/>
|
||||
)}
|
||||
{props.text}
|
||||
<div className="flex justify-between">
|
||||
<div>{props.text}</div>
|
||||
<div>{props.rightElement}</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
22
Common/UI/Components/MoreMenu/MoreMenuSection.tsx
Normal file
22
Common/UI/Components/MoreMenu/MoreMenuSection.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React, { FunctionComponent, ReactElement } from "react";
|
||||
import MoreMenuDivider from "./Divider";
|
||||
export interface ComponentProps {
|
||||
title: string;
|
||||
children: Array<ReactElement> | ReactElement;
|
||||
}
|
||||
|
||||
const MoreMenuSection: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<div>
|
||||
<div className="text-gray-400 text-sm font-medium">
|
||||
{props.title.toLocaleUpperCase()}
|
||||
</div>
|
||||
{props.children}
|
||||
<MoreMenuDivider />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MoreMenuSection;
|
Loading…
Reference in New Issue
Block a user