add table view save settings.

This commit is contained in:
Simon Larsen 2024-10-02 19:26:40 +01:00
parent c833bcb37a
commit fd15e66f83
No known key found for this signature in database
GPG Key ID: 96C5DCA24769DBCA
5 changed files with 324 additions and 4 deletions

View File

@ -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: [

View 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;

View File

@ -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;

View File

@ -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>
);
};

View 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;