mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
1525 lines
55 KiB
TypeScript
1525 lines
55 KiB
TypeScript
import BaseModel, { BaseModelType } from 'Common/Models/BaseModel';
|
|
import React, {
|
|
MutableRefObject,
|
|
ReactElement,
|
|
useEffect,
|
|
useState,
|
|
} from 'react';
|
|
import Columns from './Columns';
|
|
import {
|
|
ErrorFunction,
|
|
VoidFunction,
|
|
} from 'Common/Types/Functions';
|
|
import Table from '../Table/Table';
|
|
import TableColumn from '../Table/Types/Column';
|
|
import { JSONObject } from 'Common/Types/JSON';
|
|
import Card, {
|
|
CardButtonSchema,
|
|
ComponentProps as CardComponentProps,
|
|
} from '../Card/Card';
|
|
|
|
import RequestOptions from '../../Utils/BaseDatabase/RequestOptions';
|
|
import ListResult from '../../Utils/BaseDatabase/ListResult';
|
|
import Select from '../../Utils/BaseDatabase/Select';
|
|
import { ButtonStyleType } from '../Button/Button';
|
|
import IconProp from 'Common/Types/Icon/IconProp';
|
|
import { ModelField } from '../Forms/ModelForm';
|
|
|
|
import SortOrder from 'Common/Types/BaseDatabase/SortOrder';
|
|
import FieldType from '../Types/FieldType';
|
|
import Dictionary from 'Common/Types/Dictionary';
|
|
import ActionButtonSchema from '../ActionButton/ActionButtonSchema';
|
|
import ObjectID from 'Common/Types/ObjectID';
|
|
import ConfirmModal from '../Modal/ConfirmModal';
|
|
import Permission, {
|
|
PermissionHelper,
|
|
UserPermission,
|
|
} from 'Common/Types/Permission';
|
|
import PermissionUtil from '../../Utils/Permission';
|
|
import { ColumnAccessControl } from 'Common/Types/BaseDatabase/AccessControl';
|
|
import Query from '../../Utils/BaseDatabase/Query';
|
|
import Search from 'Common/Types/BaseDatabase/Search';
|
|
import Typeof from 'Common/Types/Typeof';
|
|
import Navigation from '../../Utils/Navigation';
|
|
import Route from 'Common/Types/API/Route';
|
|
import BadDataException from 'Common/Types/Exception/BadDataException';
|
|
import List from '../List/List';
|
|
import OrderedStatesList from '../OrderedStatesList/OrderedStatesList';
|
|
import Field from '../Detail/Field';
|
|
import FormValues from '../Forms/Types/FormValues';
|
|
import { FilterData } from '../Table/Filter';
|
|
import ModelTableColumn from './Column';
|
|
import { Logger } from '../../Utils/Logger';
|
|
import { LIMIT_PER_PROJECT } from 'Common/Types/Database/LimitMax';
|
|
import InBetween from 'Common/Types/BaseDatabase/InBetween';
|
|
import { API_DOCS_URL, BILLING_ENABLED, getAllEnvVars } from '../../Config';
|
|
import SubscriptionPlan, {
|
|
PlanSelect,
|
|
} from 'Common/Types/Billing/SubscriptionPlan';
|
|
import Pill from '../Pill/Pill';
|
|
import { Yellow } from 'Common/Types/BrandColors';
|
|
import { ModalWidth } from '../Modal/Modal';
|
|
import ProjectUtil from '../../Utils/Project';
|
|
import API from '../../Utils/API/API';
|
|
import ErrorMessage from '../ErrorMessage/ErrorMessage';
|
|
import { DropdownOption } from '../Dropdown/Dropdown';
|
|
import { FormStep } from '../Forms/Types/FormStep';
|
|
import URL from 'Common/Types/API/URL';
|
|
import { ListDetailProps } from '../List/ListRow';
|
|
import User from '../../Utils/User';
|
|
import AnalyticsBaseModel, {
|
|
AnalyticsBaseModelType,
|
|
} from 'Common/AnalyticsModels/BaseModel';
|
|
import Sort from '../../Utils/BaseDatabase/Sort';
|
|
import { FormProps } from '../Forms/BasicForm';
|
|
import { PromiseVoidFunction } from 'Common/Types/Functions';
|
|
import { GetReactElementFunction } from '../../Types/Functions';
|
|
|
|
export enum ShowTableAs {
|
|
Table,
|
|
List,
|
|
OrderedStatesList,
|
|
}
|
|
|
|
export interface BaseTableCallbacks<
|
|
TBaseModel extends BaseModel | AnalyticsBaseModel
|
|
> {
|
|
deleteItem: (item: TBaseModel) => Promise<void>;
|
|
getModelFromJSON: (item: JSONObject) => TBaseModel;
|
|
getJSONFromModel: (item: TBaseModel) => JSONObject;
|
|
addSlugToSelect: (select: Select<TBaseModel>) => Select<TBaseModel>;
|
|
getList: (data: {
|
|
modelType: BaseModelType | AnalyticsBaseModelType;
|
|
query: Query<TBaseModel>;
|
|
limit: number;
|
|
skip: number;
|
|
sort: Sort<TBaseModel>;
|
|
select: Select<TBaseModel>;
|
|
requestOptions?: RequestOptions | undefined;
|
|
}) => Promise<ListResult<TBaseModel>>;
|
|
toJSONArray: (data: Array<TBaseModel>) => Array<JSONObject>;
|
|
updateById: (data: { id: ObjectID; data: JSONObject }) => Promise<void>;
|
|
showCreateEditModal: (data: {
|
|
modalType: ModalType;
|
|
modelIdToEdit?: ObjectID | undefined;
|
|
onBeforeCreate?:
|
|
| ((
|
|
item: TBaseModel,
|
|
miscDataProps: JSONObject
|
|
) => Promise<TBaseModel>)
|
|
| undefined;
|
|
onSuccess?: ((item: TBaseModel) => void) | undefined;
|
|
onClose?: (() => void) | undefined;
|
|
}) => ReactElement;
|
|
}
|
|
|
|
export interface BaseTableProps<
|
|
TBaseModel extends BaseModel | AnalyticsBaseModel
|
|
> {
|
|
modelType: { new (): TBaseModel };
|
|
id: string;
|
|
onFetchInit?:
|
|
| undefined
|
|
| ((pageNumber: number, itemsOnPage: number) => void);
|
|
onFetchSuccess?:
|
|
| undefined
|
|
| ((data: Array<TBaseModel>, totalCount: number) => void);
|
|
cardProps?: CardComponentProps | undefined;
|
|
showCreateForm?: undefined | boolean;
|
|
columns: Columns<TBaseModel>;
|
|
listDetailOptions?: undefined | ListDetailProps;
|
|
selectMoreFields?: Select<TBaseModel>;
|
|
initialItemsOnPage?: number;
|
|
isDeleteable: boolean;
|
|
isEditable?: boolean | undefined;
|
|
isCreateable: boolean;
|
|
disablePagination?: undefined | boolean;
|
|
formFields?: undefined | Array<ModelField<TBaseModel>>;
|
|
formSteps?: undefined | Array<FormStep<TBaseModel>>;
|
|
noItemsMessage?: undefined | string;
|
|
showRefreshButton?: undefined | boolean;
|
|
showFilterButton?: undefined | boolean;
|
|
isViewable?: undefined | boolean;
|
|
showViewIdButton?: undefined | boolean;
|
|
enableDragAndDrop?: boolean | undefined;
|
|
viewPageRoute?: undefined | Route;
|
|
onViewPage?: (item: TBaseModel) => Promise<Route>;
|
|
query?: Query<TBaseModel>;
|
|
onBeforeFetch?: (() => Promise<JSONObject>) | undefined;
|
|
createInitialValues?: FormValues<TBaseModel> | undefined;
|
|
onBeforeCreate?:
|
|
| ((item: TBaseModel, miscDataProps: JSONObject) => Promise<TBaseModel>)
|
|
| undefined;
|
|
onCreateSuccess?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
|
createVerb?: string;
|
|
showTableAs?: ShowTableAs | undefined;
|
|
singularName?: string | undefined;
|
|
pluralName?: string | undefined;
|
|
actionButtons?: Array<ActionButtonSchema> | undefined;
|
|
deleteButtonText?: string | undefined;
|
|
onCreateEditModalClose?: (() => void) | undefined;
|
|
editButtonText?: string | undefined;
|
|
viewButtonText?: string | undefined;
|
|
refreshToggle?: boolean | undefined;
|
|
fetchRequestOptions?: RequestOptions | undefined;
|
|
deleteRequestOptions?: RequestOptions | undefined;
|
|
onItemDeleted?: ((item: TBaseModel) => void) | undefined;
|
|
onBeforeEdit?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
|
onBeforeDelete?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
|
onBeforeView?: ((item: TBaseModel) => Promise<TBaseModel>) | undefined;
|
|
sortBy?: string | undefined;
|
|
sortOrder?: SortOrder | undefined;
|
|
dragDropIdField?: string | undefined;
|
|
dragDropIndexField?: string | undefined;
|
|
createEditModalWidth?: ModalWidth | undefined;
|
|
orderedStatesListProps?: {
|
|
titleField: string;
|
|
descriptionField?: string | undefined;
|
|
orderField: string;
|
|
shouldAddItemInTheEnd?: boolean;
|
|
shouldAddItemInTheBeginning?: boolean;
|
|
};
|
|
onViewComplete?: ((item: TBaseModel) => void) | undefined;
|
|
createEditFromRef?:
|
|
| undefined
|
|
| MutableRefObject<FormProps<FormValues<TBaseModel>>>;
|
|
name: string;
|
|
}
|
|
|
|
export interface ComponentProps<
|
|
TBaseModel extends BaseModel | AnalyticsBaseModel
|
|
> extends BaseTableProps<TBaseModel> {
|
|
callbacks: BaseTableCallbacks<TBaseModel>;
|
|
}
|
|
|
|
export enum ModalType {
|
|
Create,
|
|
Edit,
|
|
}
|
|
|
|
const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
props: ComponentProps<TBaseModel>
|
|
) => ReactElement = <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
props: ComponentProps<TBaseModel>
|
|
): ReactElement => {
|
|
const model: TBaseModel = new props.modelType();
|
|
|
|
let showTableAs: ShowTableAs | undefined = props.showTableAs;
|
|
|
|
if (!showTableAs) {
|
|
showTableAs = ShowTableAs.Table;
|
|
}
|
|
|
|
const [showViewIdModal, setShowViewIdModal] = useState<boolean>(false);
|
|
const [viewId, setViewId] = useState<string | null>(null);
|
|
const [tableColumns, setColumns] = useState<Array<TableColumn>>([]);
|
|
const [cardButtons, setCardButtons] = useState<Array<CardButtonSchema>>([]);
|
|
|
|
const [actionButtonSchema, setActionButtonSchema] = useState<
|
|
Array<ActionButtonSchema>
|
|
>([]);
|
|
|
|
useEffect(() => {
|
|
if (props.showCreateForm) {
|
|
setShowModal(true);
|
|
setModalType(ModalType.Create);
|
|
}
|
|
}, [props.showCreateForm]);
|
|
|
|
const [orderedStatesListNewItemOrder, setOrderedStatesListNewItemOrder] =
|
|
useState<number | null>(null);
|
|
|
|
const [onBeforeFetchData, setOnBeforeFetchData] = useState<
|
|
JSONObject | undefined
|
|
>(undefined);
|
|
const [data, setData] = useState<Array<TBaseModel>>([]);
|
|
const [query, setQuery] = useState<Query<TBaseModel>>({});
|
|
const [currentPageNumber, setCurrentPageNumber] = useState<number>(1);
|
|
const [totalItemsCount, setTotalItemsCount] = useState<number>(0);
|
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
|
|
const [error, setError] = useState<string>('');
|
|
const [tableFilterError, setTableFilterError] = useState<string>('');
|
|
|
|
const [showModel, setShowModal] = useState<boolean>(false);
|
|
const [showTableFilter, setShowTableFilter] = useState<boolean>(false);
|
|
const [modalType, setModalType] = useState<ModalType>(ModalType.Create);
|
|
const [sortBy, setSortBy] = useState<string>(props.sortBy || '');
|
|
const [sortOrder, setSortOrder] = useState<SortOrder>(
|
|
props.sortOrder || SortOrder.Ascending
|
|
);
|
|
const [showDeleteConfirmModal, setShowDeleteConfirmModal] =
|
|
useState<boolean>(false);
|
|
const [currentEditableItem, setCurrentEditableItem] =
|
|
useState<JSONObject | null>(null);
|
|
const [currentDeleteableItem, setCurrentDeleteableItem] =
|
|
useState<JSONObject | null>(null);
|
|
|
|
const [itemsOnPage, setItemsOnPage] = useState<number>(
|
|
props.initialItemsOnPage || 10
|
|
);
|
|
|
|
const [fields, setFields] = useState<Array<Field>>([]);
|
|
|
|
const [isTableFilterFetchLoading, setIsTableFilterFetchLoading] =
|
|
useState(false);
|
|
|
|
const [errorModalText, setErrorModalText] = useState<string>('');
|
|
|
|
useEffect(() => {
|
|
if (!showModel) {
|
|
props.onCreateEditModalClose && props.onCreateEditModalClose();
|
|
}
|
|
}, [showModel]);
|
|
|
|
useEffect(() => {
|
|
const detailFields: Array<Field> = [];
|
|
for (const column of tableColumns) {
|
|
if (!column.key) {
|
|
// if its an action column, ignore.
|
|
continue;
|
|
}
|
|
|
|
detailFields.push({
|
|
title: column.title,
|
|
description: column.description || '',
|
|
key: column.key || '',
|
|
fieldType: column.type,
|
|
colSpan: column.colSpan,
|
|
contentClassName: column.contentClassName,
|
|
alignItem: column.alignItem,
|
|
getElement: column.getElement
|
|
? (item: JSONObject): ReactElement => {
|
|
return column.getElement!(item, onBeforeFetchData);
|
|
}
|
|
: undefined,
|
|
});
|
|
|
|
setFields(detailFields);
|
|
}
|
|
}, [tableColumns]);
|
|
|
|
const getRelationSelect: Function = (): Select<TBaseModel> => {
|
|
const relationSelect: Select<TBaseModel> = {};
|
|
|
|
for (const column of props.columns || []) {
|
|
const key: string | null = column.field
|
|
? (Object.keys(column.field)[0] as string)
|
|
: null;
|
|
|
|
if (key && model.isFileColumn(key)) {
|
|
(relationSelect as JSONObject)[key] = {
|
|
file: true,
|
|
_id: true,
|
|
type: true,
|
|
name: true,
|
|
};
|
|
} else if (key && model.isEntityColumn(key)) {
|
|
if (!(relationSelect as JSONObject)[key]) {
|
|
(relationSelect as JSONObject)[key] = {};
|
|
}
|
|
|
|
(relationSelect as JSONObject)[key] = {
|
|
...((relationSelect as JSONObject)[key] as JSONObject),
|
|
...(column.field as any)[key],
|
|
};
|
|
}
|
|
}
|
|
|
|
return relationSelect;
|
|
};
|
|
|
|
const deleteItem: Function = async (item: TBaseModel) => {
|
|
if (!item.id) {
|
|
throw new BadDataException('item.id cannot be null');
|
|
}
|
|
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
await props.callbacks.deleteItem(item);
|
|
|
|
props.onItemDeleted && props.onItemDeleted(item);
|
|
|
|
if (data.length === 1 && currentPageNumber > 1) {
|
|
setCurrentPageNumber(currentPageNumber - 1);
|
|
}
|
|
await fetchItems();
|
|
} catch (err) {
|
|
setErrorModalText(API.getFriendlyMessage(err));
|
|
}
|
|
|
|
setIsLoading(false);
|
|
};
|
|
|
|
const serializeToTableColumns: VoidFunction = (): void => {
|
|
// Convert ModelColumns to TableColumns.
|
|
|
|
const columns: Array<TableColumn> = [];
|
|
|
|
let selectFields: Select<TBaseModel> = {
|
|
_id: true,
|
|
};
|
|
|
|
selectFields = props.callbacks.addSlugToSelect(selectFields);
|
|
|
|
const userPermissions: Array<Permission> = getUserPermissions();
|
|
|
|
const accessControl: Dictionary<ColumnAccessControl> =
|
|
model.getColumnAccessControlForAllColumns();
|
|
|
|
for (const column of props.columns || []) {
|
|
const hasPermission: boolean =
|
|
hasPermissionToReadColumn(column) || User.isMasterAdmin();
|
|
const key: string | null = getColumnKey(column);
|
|
|
|
if (hasPermission) {
|
|
let tooltipText: ((item: JSONObject) => string) | undefined =
|
|
undefined;
|
|
|
|
if (column.tooltipText) {
|
|
tooltipText = (item: JSONObject): string => {
|
|
return column.tooltipText!(
|
|
props.callbacks.getModelFromJSON(item)
|
|
);
|
|
};
|
|
}
|
|
|
|
// get filter options if they were already loaded.
|
|
|
|
let filterDropdownOptions: Array<DropdownOption> | undefined =
|
|
undefined;
|
|
|
|
const columnKey: string | null = column.selectedProperty
|
|
? key + '.' + column.selectedProperty
|
|
: key;
|
|
|
|
const existingTableColumn: TableColumn | undefined =
|
|
tableColumns.find((i: TableColumn) => {
|
|
return i.key === columnKey;
|
|
});
|
|
|
|
if (column.filterDropdownOptions) {
|
|
filterDropdownOptions = column.filterDropdownOptions;
|
|
}
|
|
|
|
if (
|
|
tableColumns &&
|
|
existingTableColumn &&
|
|
existingTableColumn.filterDropdownOptions
|
|
) {
|
|
filterDropdownOptions =
|
|
existingTableColumn.filterDropdownOptions;
|
|
}
|
|
|
|
columns.push({
|
|
...column,
|
|
disableSort: column.disableSort || shouldDisableSort(key),
|
|
key: columnKey,
|
|
tooltipText,
|
|
filterDropdownOptions: filterDropdownOptions,
|
|
});
|
|
|
|
if (key) {
|
|
(selectFields as Dictionary<boolean>)[key] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
const selectMoreFields: Array<string> = props.selectMoreFields
|
|
? Object.keys(props.selectMoreFields)
|
|
: [];
|
|
|
|
for (const moreField of selectMoreFields) {
|
|
let hasPermissionToSelectField: boolean = true;
|
|
let fieldPermissions: Array<Permission> = [];
|
|
fieldPermissions = accessControl[moreField as string]?.read || [];
|
|
|
|
if (
|
|
accessControl[moreField]?.read &&
|
|
!PermissionHelper.doesPermissionsIntersect(
|
|
userPermissions,
|
|
fieldPermissions
|
|
)
|
|
) {
|
|
hasPermissionToSelectField = false;
|
|
}
|
|
|
|
if (hasPermissionToSelectField) {
|
|
(selectFields as Dictionary<boolean>)[moreField] = true;
|
|
} else {
|
|
Logger.warn(
|
|
'User does not have read permissions to read - ' + moreField
|
|
);
|
|
}
|
|
}
|
|
|
|
const permissions: Array<Permission> | null =
|
|
PermissionUtil.getAllPermissions();
|
|
|
|
if (
|
|
(permissions &&
|
|
((props.isDeleteable &&
|
|
model.hasDeletePermissions(permissions)) ||
|
|
(props.isEditable &&
|
|
model.hasUpdatePermissions(permissions)) ||
|
|
(props.isViewable &&
|
|
model.hasReadPermissions(permissions)))) ||
|
|
(props.actionButtons && props.actionButtons.length > 0) ||
|
|
props.showViewIdButton
|
|
) {
|
|
columns.push({
|
|
title: 'Actions',
|
|
type: FieldType.Actions,
|
|
});
|
|
}
|
|
|
|
setActionSchema();
|
|
setHeaderButtons();
|
|
|
|
setColumns(columns);
|
|
};
|
|
|
|
const getFilterDropdownItems: PromiseVoidFunction =
|
|
async (): Promise<void> => {
|
|
setTableFilterError('');
|
|
setIsTableFilterFetchLoading(true);
|
|
|
|
const classicColumns: Array<TableColumn> = [...tableColumns];
|
|
|
|
try {
|
|
for (const column of props.columns) {
|
|
const key: string | null = getColumnKey(column);
|
|
|
|
const classicColumn: TableColumn | undefined =
|
|
classicColumns.find((i: TableColumn) => {
|
|
return i.key === key;
|
|
});
|
|
|
|
if (!classicColumn) {
|
|
continue;
|
|
}
|
|
|
|
if (!key) {
|
|
continue;
|
|
}
|
|
|
|
if (!column.filterEntityType) {
|
|
continue;
|
|
}
|
|
|
|
if (!column.isFilterable) {
|
|
continue;
|
|
}
|
|
|
|
if (!column.filterDropdownField) {
|
|
Logger.warn(
|
|
`Cannot filter on ${key} because column.dropdownField is not set.`
|
|
);
|
|
continue;
|
|
}
|
|
|
|
const hasPermission: boolean =
|
|
hasPermissionToReadColumn(column);
|
|
|
|
if (!hasPermission) {
|
|
continue;
|
|
}
|
|
|
|
const query: Query<TBaseModel> = column.filterQuery || {};
|
|
|
|
const listResult: ListResult<TBaseModel> =
|
|
await props.callbacks.getList({
|
|
modelType: column.filterEntityType,
|
|
query: query,
|
|
limit: LIMIT_PER_PROJECT,
|
|
skip: 0,
|
|
select: {
|
|
[column.filterDropdownField.label]: true,
|
|
[column.filterDropdownField.value]: true,
|
|
} as any,
|
|
sort: {},
|
|
});
|
|
|
|
classicColumn.filterDropdownOptions = [];
|
|
for (const item of listResult.data) {
|
|
classicColumn.filterDropdownOptions.push({
|
|
value: item.getColumnValue(
|
|
column.filterDropdownField.value
|
|
) as string,
|
|
label: item.getColumnValue(
|
|
column.filterDropdownField.label
|
|
) as string,
|
|
});
|
|
}
|
|
|
|
if (column.tooltipText) {
|
|
classicColumn.tooltipText = (
|
|
item: JSONObject
|
|
): string => {
|
|
return column.tooltipText!(
|
|
props.callbacks.getModelFromJSON(item)
|
|
);
|
|
};
|
|
}
|
|
|
|
classicColumn.colSpan = column.colSpan;
|
|
classicColumn.alignItem = column.alignItem;
|
|
classicColumn.contentClassName = column.contentClassName;
|
|
}
|
|
|
|
setColumns(classicColumns);
|
|
} catch (err) {
|
|
setTableFilterError(API.getFriendlyMessage(err));
|
|
}
|
|
|
|
setIsTableFilterFetchLoading(false);
|
|
};
|
|
|
|
const fetchItems: PromiseVoidFunction = async (): Promise<void> => {
|
|
setError('');
|
|
setIsLoading(true);
|
|
|
|
if (props.onFetchInit) {
|
|
props.onFetchInit(currentPageNumber, itemsOnPage);
|
|
}
|
|
|
|
if (props.onBeforeFetch) {
|
|
const jobject: JSONObject = await props.onBeforeFetch();
|
|
setOnBeforeFetchData(jobject);
|
|
}
|
|
|
|
try {
|
|
const listResult: ListResult<TBaseModel> =
|
|
await props.callbacks.getList({
|
|
modelType: props.modelType as
|
|
| BaseModelType
|
|
| AnalyticsBaseModelType,
|
|
query: {
|
|
...query,
|
|
...props.query,
|
|
},
|
|
limit: itemsOnPage,
|
|
skip: (currentPageNumber - 1) * itemsOnPage,
|
|
select: {
|
|
...getSelect(),
|
|
...getRelationSelect(),
|
|
},
|
|
sort: sortBy
|
|
? {
|
|
[sortBy as any]: sortOrder,
|
|
}
|
|
: {},
|
|
requestOptions: props.fetchRequestOptions,
|
|
});
|
|
|
|
setTotalItemsCount(listResult.count);
|
|
setData(listResult.data);
|
|
} catch (err) {
|
|
setError(API.getFriendlyMessage(err));
|
|
}
|
|
|
|
setIsLoading(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (showTableFilter) {
|
|
getFilterDropdownItems();
|
|
}
|
|
}, [showTableFilter]);
|
|
|
|
const getSelect: Function = (): Select<TBaseModel> => {
|
|
const selectFields: Select<TBaseModel> = {
|
|
_id: true,
|
|
};
|
|
|
|
for (const column of props.columns || []) {
|
|
const key: string | null = column.field
|
|
? (Object.keys(column.field)[0] as string)
|
|
: null;
|
|
|
|
if (key) {
|
|
if (model.hasColumn(key)) {
|
|
(selectFields as Dictionary<boolean>)[key] = true;
|
|
} else {
|
|
throw new BadDataException(
|
|
`${key} column not found on ${model.singularName}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
const selectMoreFields: Array<string> = props.selectMoreFields
|
|
? Object.keys(props.selectMoreFields)
|
|
: [];
|
|
|
|
if (props.dragDropIndexField) {
|
|
selectMoreFields.push(props.dragDropIndexField);
|
|
}
|
|
|
|
if (
|
|
props.dragDropIdField &&
|
|
!Object.keys(selectFields).includes(props.dragDropIdField) &&
|
|
!selectMoreFields.includes(props.dragDropIdField)
|
|
) {
|
|
selectMoreFields.push(props.dragDropIdField);
|
|
}
|
|
|
|
for (const moreField of selectMoreFields) {
|
|
if (model.hasColumn(moreField) && model.isEntityColumn(moreField)) {
|
|
(selectFields as Dictionary<boolean>)[moreField] = (
|
|
props.selectMoreFields as any
|
|
)[moreField];
|
|
} else if (model.hasColumn(moreField)) {
|
|
(selectFields as Dictionary<boolean>)[moreField] = true;
|
|
} else {
|
|
throw new BadDataException(
|
|
`${moreField} column not found on ${model.singularName}`
|
|
);
|
|
}
|
|
}
|
|
|
|
return selectFields;
|
|
};
|
|
|
|
const setHeaderButtons: VoidFunction = (): void => {
|
|
// add header buttons.
|
|
let headerbuttons: Array<CardButtonSchema> = [];
|
|
|
|
if (props.cardProps?.buttons && props.cardProps?.buttons.length > 0) {
|
|
headerbuttons = [...props.cardProps.buttons];
|
|
}
|
|
|
|
const permissions: Array<Permission> | null =
|
|
PermissionUtil.getAllPermissions();
|
|
|
|
let hasPermissionToCreate: boolean = false;
|
|
|
|
if (permissions) {
|
|
hasPermissionToCreate =
|
|
model.hasCreatePermissions(permissions) || User.isMasterAdmin();
|
|
}
|
|
|
|
// because ordered list add button is inside the table and not on the card header.
|
|
if (
|
|
props.isCreateable &&
|
|
hasPermissionToCreate &&
|
|
showTableAs !== ShowTableAs.OrderedStatesList
|
|
) {
|
|
headerbuttons.push({
|
|
title: `${props.createVerb || 'Create'} ${
|
|
props.singularName || model.singularName
|
|
}`,
|
|
buttonStyle: ButtonStyleType.NORMAL,
|
|
className:
|
|
props.showFilterButton || props.showRefreshButton
|
|
? 'mr-1'
|
|
: '',
|
|
onClick: () => {
|
|
setModalType(ModalType.Create);
|
|
setShowModal(true);
|
|
},
|
|
icon: IconProp.Add,
|
|
});
|
|
}
|
|
|
|
if (props.showRefreshButton) {
|
|
headerbuttons.push({
|
|
title: '',
|
|
className: props.showFilterButton
|
|
? 'p-1 px-1 pr-0 pl-0 py-0 mt-1'
|
|
: 'py-0 pr-0 pl-1 mt-1',
|
|
buttonStyle: ButtonStyleType.ICON,
|
|
onClick: () => {
|
|
fetchItems();
|
|
},
|
|
disabled: isTableFilterFetchLoading,
|
|
icon: IconProp.Refresh,
|
|
});
|
|
}
|
|
|
|
if (props.showFilterButton) {
|
|
headerbuttons.push({
|
|
title: '',
|
|
buttonStyle: ButtonStyleType.ICON,
|
|
className: props.showRefreshButton
|
|
? 'p-1 px-1 pr-0 pl-0 py-0 mt-1'
|
|
: 'py-0 pr-0 pl-1 mt-1',
|
|
onClick: () => {
|
|
const newValue: boolean = !showTableFilter;
|
|
|
|
setQuery({});
|
|
|
|
setShowTableFilter(newValue);
|
|
},
|
|
disabled: isTableFilterFetchLoading,
|
|
icon: IconProp.Filter,
|
|
});
|
|
}
|
|
|
|
setCardButtons(headerbuttons);
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchItems();
|
|
}, [
|
|
currentPageNumber,
|
|
sortBy,
|
|
sortOrder,
|
|
itemsOnPage,
|
|
query,
|
|
props.refreshToggle,
|
|
]);
|
|
|
|
type ShouldDisableSortFunction = (columnName: string) => boolean;
|
|
|
|
const shouldDisableSort: ShouldDisableSortFunction = (
|
|
columnName: string
|
|
): boolean => {
|
|
return model.isEntityColumn(columnName);
|
|
};
|
|
|
|
type GetColumnKeyFunction = (
|
|
column: ModelTableColumn<TBaseModel>
|
|
) => string | null;
|
|
|
|
const getColumnKey: GetColumnKeyFunction = (
|
|
column: ModelTableColumn<TBaseModel>
|
|
): string | null => {
|
|
const key: string | null = column.field
|
|
? (Object.keys(column.field)[0] as string)
|
|
: null;
|
|
|
|
return key;
|
|
};
|
|
|
|
type HasPermissionToReadColumnFunction = (
|
|
column: ModelTableColumn<TBaseModel>
|
|
) => boolean;
|
|
|
|
const hasPermissionToReadColumn: HasPermissionToReadColumnFunction = (
|
|
column: ModelTableColumn<TBaseModel>
|
|
): boolean => {
|
|
const accessControl: Dictionary<ColumnAccessControl> =
|
|
model.getColumnAccessControlForAllColumns();
|
|
|
|
const userPermissions: Array<Permission> = getUserPermissions();
|
|
|
|
const key: string | null = getColumnKey(column);
|
|
|
|
// check permissions.
|
|
let hasPermission: boolean = false;
|
|
|
|
if (!key) {
|
|
hasPermission = true;
|
|
}
|
|
|
|
if (key) {
|
|
hasPermission = true;
|
|
let fieldPermissions: Array<Permission> = [];
|
|
fieldPermissions = accessControl[key as string]?.read || [];
|
|
|
|
if (
|
|
accessControl[key]?.read &&
|
|
!PermissionHelper.doesPermissionsIntersect(
|
|
userPermissions,
|
|
fieldPermissions
|
|
)
|
|
) {
|
|
hasPermission = false;
|
|
}
|
|
}
|
|
|
|
return hasPermission;
|
|
};
|
|
|
|
type GetUserPermissionsFunction = () => Array<Permission>;
|
|
|
|
const getUserPermissions: GetUserPermissionsFunction =
|
|
(): Array<Permission> => {
|
|
let userPermissions: Array<Permission> =
|
|
PermissionUtil.getGlobalPermissions()?.globalPermissions || [];
|
|
if (
|
|
PermissionUtil.getProjectPermissions() &&
|
|
PermissionUtil.getProjectPermissions()?.permissions &&
|
|
PermissionUtil.getProjectPermissions()!.permissions.length > 0
|
|
) {
|
|
userPermissions = userPermissions.concat(
|
|
PermissionUtil.getProjectPermissions()!.permissions.map(
|
|
(i: UserPermission) => {
|
|
return i.permission;
|
|
}
|
|
)
|
|
);
|
|
}
|
|
|
|
userPermissions.push(Permission.Public);
|
|
|
|
return userPermissions;
|
|
};
|
|
|
|
useEffect(() => {
|
|
serializeToTableColumns();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
serializeToTableColumns();
|
|
}, [data]);
|
|
|
|
const setActionSchema: VoidFunction = () => {
|
|
const permissions: Array<Permission> =
|
|
PermissionUtil.getAllPermissions();
|
|
|
|
const actionsSchema: Array<ActionButtonSchema> = [];
|
|
|
|
if (props.showViewIdButton) {
|
|
actionsSchema.push({
|
|
title: 'Show ID',
|
|
buttonStyleType: ButtonStyleType.OUTLINE,
|
|
onClick: async (
|
|
item: JSONObject,
|
|
onCompleteAction: VoidFunction,
|
|
onError: ErrorFunction
|
|
) => {
|
|
try {
|
|
setViewId(item['_id'] as string);
|
|
setShowViewIdModal(true);
|
|
onCompleteAction();
|
|
} catch (err) {
|
|
onError(err as Error);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
// add actions buttons from props.
|
|
if (props.actionButtons) {
|
|
for (const moreSchema of props.actionButtons) {
|
|
actionsSchema.push(moreSchema);
|
|
}
|
|
}
|
|
|
|
if (permissions) {
|
|
if (
|
|
props.isViewable &&
|
|
(model.hasReadPermissions(permissions) || User.isMasterAdmin())
|
|
) {
|
|
actionsSchema.push({
|
|
title:
|
|
props.viewButtonText ||
|
|
`View ${props.singularName || model.singularName}`,
|
|
buttonStyleType: ButtonStyleType.NORMAL,
|
|
onClick: async (
|
|
item: JSONObject,
|
|
onCompleteAction: VoidFunction,
|
|
onError: ErrorFunction
|
|
) => {
|
|
try {
|
|
const baseModel: TBaseModel =
|
|
props.callbacks.getModelFromJSON(item);
|
|
|
|
if (props.onBeforeView) {
|
|
item = props.callbacks.getJSONFromModel(
|
|
await props.onBeforeView(baseModel)
|
|
);
|
|
}
|
|
|
|
if (props.onViewPage) {
|
|
const route: Route = await props.onViewPage(
|
|
baseModel
|
|
);
|
|
|
|
onCompleteAction();
|
|
|
|
if (props.onViewComplete) {
|
|
props.onViewComplete(baseModel);
|
|
}
|
|
|
|
return Navigation.navigate(route);
|
|
}
|
|
|
|
if (!props.viewPageRoute) {
|
|
throw new BadDataException(
|
|
'props.viewPageRoute not found'
|
|
);
|
|
}
|
|
|
|
onCompleteAction();
|
|
if (props.onViewComplete) {
|
|
props.onViewComplete(baseModel);
|
|
}
|
|
|
|
const id: string = baseModel.id?.toString() || '';
|
|
|
|
return Navigation.navigate(
|
|
new Route(
|
|
props.viewPageRoute.toString()
|
|
).addRoute('/' + id)
|
|
);
|
|
} catch (err) {
|
|
onError(err as Error);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
if (props.isEditable && model.hasUpdatePermissions(permissions)) {
|
|
actionsSchema.push({
|
|
title: props.editButtonText || 'Edit',
|
|
buttonStyleType: ButtonStyleType.OUTLINE,
|
|
onClick: async (
|
|
item: JSONObject,
|
|
onCompleteAction: VoidFunction,
|
|
onError: ErrorFunction
|
|
) => {
|
|
try {
|
|
if (props.onBeforeEdit) {
|
|
item = props.callbacks.getJSONFromModel(
|
|
await props.onBeforeEdit(
|
|
props.callbacks.getModelFromJSON(item)
|
|
)
|
|
);
|
|
}
|
|
|
|
setModalType(ModalType.Edit);
|
|
setShowModal(true);
|
|
setCurrentEditableItem(item);
|
|
|
|
onCompleteAction();
|
|
} catch (err) {
|
|
onError(err as Error);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
if (props.isDeleteable && model.hasDeletePermissions(permissions)) {
|
|
actionsSchema.push({
|
|
title: props.deleteButtonText || 'Delete',
|
|
icon: IconProp.Trash,
|
|
buttonStyleType: ButtonStyleType.DANGER_OUTLINE,
|
|
onClick: async (
|
|
item: JSONObject,
|
|
onCompleteAction: VoidFunction,
|
|
onError: ErrorFunction
|
|
) => {
|
|
try {
|
|
if (props.onBeforeDelete) {
|
|
item = props.callbacks.getJSONFromModel(
|
|
await props.onBeforeDelete(
|
|
props.callbacks.getModelFromJSON(item)
|
|
)
|
|
);
|
|
}
|
|
|
|
setShowDeleteConfirmModal(true);
|
|
setCurrentDeleteableItem(item);
|
|
onCompleteAction();
|
|
} catch (err) {
|
|
onError(err as Error);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
setActionButtonSchema(actionsSchema);
|
|
};
|
|
|
|
const getTable: GetReactElementFunction = (): ReactElement => {
|
|
return (
|
|
<Table
|
|
onFilterChanged={(filterData: FilterData) => {
|
|
const newQuery: Query<TBaseModel> = {};
|
|
|
|
for (const key in filterData) {
|
|
if (
|
|
filterData[key] &&
|
|
typeof filterData[key] === Typeof.String
|
|
) {
|
|
newQuery[key as keyof TBaseModel] = (
|
|
filterData[key] || ''
|
|
).toString();
|
|
}
|
|
|
|
if (typeof filterData[key] === Typeof.Boolean) {
|
|
newQuery[key as keyof TBaseModel] = Boolean(
|
|
filterData[key]
|
|
);
|
|
}
|
|
|
|
if (filterData[key] instanceof Date) {
|
|
newQuery[key as keyof TBaseModel] = filterData[key];
|
|
}
|
|
|
|
if (filterData[key] instanceof Search) {
|
|
newQuery[key as keyof TBaseModel] = filterData[key];
|
|
}
|
|
|
|
if (filterData[key] instanceof InBetween) {
|
|
newQuery[key as keyof TBaseModel] = filterData[key];
|
|
}
|
|
|
|
if (Array.isArray(filterData[key])) {
|
|
newQuery[key as keyof TBaseModel] = filterData[key];
|
|
}
|
|
}
|
|
|
|
setQuery(newQuery);
|
|
}}
|
|
onSortChanged={(sortBy: string, sortOrder: SortOrder) => {
|
|
setSortBy(sortBy);
|
|
setSortOrder(sortOrder);
|
|
}}
|
|
onTableFilterRefreshClick={() => {
|
|
getFilterDropdownItems();
|
|
}}
|
|
singularLabel={
|
|
props.singularName || model.singularName || 'Item'
|
|
}
|
|
pluralLabel={props.pluralName || model.pluralName || 'Items'}
|
|
error={error}
|
|
currentPageNumber={currentPageNumber}
|
|
isLoading={isLoading}
|
|
enableDragAndDrop={props.enableDragAndDrop}
|
|
dragDropIdField={'_id'}
|
|
dragDropIndexField={props.dragDropIndexField}
|
|
totalItemsCount={totalItemsCount}
|
|
data={props.callbacks.toJSONArray(data)}
|
|
filterError={tableFilterError}
|
|
id={props.id}
|
|
columns={tableColumns}
|
|
itemsOnPage={itemsOnPage}
|
|
onDragDrop={async (id: string, newOrder: number) => {
|
|
if (!props.dragDropIndexField) {
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
|
|
await props.callbacks.updateById({
|
|
id: new ObjectID(id),
|
|
data: {
|
|
[props.dragDropIndexField]: newOrder,
|
|
},
|
|
});
|
|
|
|
fetchItems();
|
|
}}
|
|
disablePagination={props.disablePagination || false}
|
|
isTableFilterLoading={isTableFilterFetchLoading}
|
|
onNavigateToPage={async (
|
|
pageNumber: number,
|
|
itemsOnPage: number
|
|
) => {
|
|
setCurrentPageNumber(pageNumber);
|
|
setItemsOnPage(itemsOnPage);
|
|
}}
|
|
showFilter={showTableFilter}
|
|
noItemsMessage={props.noItemsMessage || ''}
|
|
onRefreshClick={() => {
|
|
fetchItems();
|
|
}}
|
|
actionButtons={actionButtonSchema}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const getOrderedStatesList: GetReactElementFunction =
|
|
(): ReactElement => {
|
|
if (!props.orderedStatesListProps) {
|
|
throw new BadDataException(
|
|
'props.orderedStatesListProps required when showTableAs === ShowTableAs.OrderedStatesList'
|
|
);
|
|
}
|
|
|
|
let getTitleElement:
|
|
| ((
|
|
item: JSONObject,
|
|
onBeforeFetchData?: JSONObject | undefined
|
|
) => ReactElement)
|
|
| undefined = undefined;
|
|
let getDescriptionElement:
|
|
| ((item: JSONObject) => ReactElement)
|
|
| undefined = undefined;
|
|
|
|
for (const column of props.columns) {
|
|
const key: string | undefined = Object.keys(
|
|
column.field as Object
|
|
)[0];
|
|
|
|
if (key === props.orderedStatesListProps.titleField) {
|
|
getTitleElement = column.getElement;
|
|
}
|
|
|
|
if (key === props.orderedStatesListProps.descriptionField) {
|
|
getDescriptionElement = column.getElement;
|
|
}
|
|
}
|
|
|
|
return (
|
|
<OrderedStatesList
|
|
error={error}
|
|
isLoading={isLoading}
|
|
data={props.callbacks.toJSONArray(data)}
|
|
id={props.id}
|
|
titleField={props.orderedStatesListProps?.titleField || ''}
|
|
descriptionField={
|
|
props.orderedStatesListProps?.descriptionField || ''
|
|
}
|
|
orderField={props.orderedStatesListProps?.orderField || ''}
|
|
shouldAddItemInTheBeginning={
|
|
props.orderedStatesListProps.shouldAddItemInTheBeginning
|
|
}
|
|
shouldAddItemInTheEnd={
|
|
props.orderedStatesListProps.shouldAddItemInTheEnd
|
|
}
|
|
noItemsMessage={props.noItemsMessage || ''}
|
|
onRefreshClick={() => {
|
|
fetchItems();
|
|
}}
|
|
onCreateNewItem={
|
|
props.isCreateable
|
|
? (order: number) => {
|
|
setOrderedStatesListNewItemOrder(order);
|
|
setModalType(ModalType.Create);
|
|
setShowModal(true);
|
|
}
|
|
: undefined
|
|
}
|
|
singularLabel={
|
|
props.singularName || model.singularName || 'Item'
|
|
}
|
|
actionButtons={actionButtonSchema}
|
|
getTitleElement={getTitleElement}
|
|
getDescriptionElement={getDescriptionElement}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const getList: GetReactElementFunction = (): ReactElement => {
|
|
return (
|
|
<List
|
|
singularLabel={
|
|
props.singularName || model.singularName || 'Item'
|
|
}
|
|
pluralLabel={props.pluralName || model.pluralName || 'Items'}
|
|
error={error}
|
|
currentPageNumber={currentPageNumber}
|
|
listDetailOptions={props.listDetailOptions}
|
|
enableDragAndDrop={props.enableDragAndDrop}
|
|
onDragDrop={async (id: string, newOrder: number) => {
|
|
if (!props.dragDropIndexField) {
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
|
|
await props.callbacks.updateById({
|
|
id: new ObjectID(id),
|
|
data: {
|
|
[props.dragDropIndexField]: newOrder,
|
|
},
|
|
});
|
|
|
|
fetchItems();
|
|
}}
|
|
dragDropIdField={'_id'}
|
|
dragDropIndexField={props.dragDropIndexField}
|
|
isLoading={isLoading}
|
|
totalItemsCount={totalItemsCount}
|
|
data={props.callbacks.toJSONArray(data)}
|
|
id={props.id}
|
|
fields={fields}
|
|
itemsOnPage={itemsOnPage}
|
|
disablePagination={props.disablePagination || false}
|
|
onNavigateToPage={async (
|
|
pageNumber: number,
|
|
itemsOnPage: number
|
|
) => {
|
|
setCurrentPageNumber(pageNumber);
|
|
setItemsOnPage(itemsOnPage);
|
|
}}
|
|
noItemsMessage={props.noItemsMessage || ''}
|
|
onRefreshClick={() => {
|
|
fetchItems();
|
|
}}
|
|
actionButtons={actionButtonSchema}
|
|
/>
|
|
);
|
|
};
|
|
|
|
const getCardTitle: Function = (
|
|
title: ReactElement | string
|
|
): ReactElement => {
|
|
const plan: PlanSelect | null = ProjectUtil.getCurrentPlan();
|
|
|
|
let showPlan: boolean = Boolean(
|
|
BILLING_ENABLED &&
|
|
plan &&
|
|
new props.modelType().getReadBillingPlan() &&
|
|
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
|
new props.modelType().getReadBillingPlan()!,
|
|
plan,
|
|
getAllEnvVars()
|
|
)
|
|
);
|
|
|
|
let planName: string = new props.modelType().getReadBillingPlan()!;
|
|
|
|
if (props.isCreateable && !showPlan) {
|
|
// if createable then read create billing permissions.
|
|
showPlan = Boolean(
|
|
BILLING_ENABLED &&
|
|
plan &&
|
|
new props.modelType().getCreateBillingPlan() &&
|
|
!SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(
|
|
new props.modelType().getCreateBillingPlan()!,
|
|
plan,
|
|
getAllEnvVars()
|
|
)
|
|
);
|
|
|
|
planName = new props.modelType().getCreateBillingPlan()!;
|
|
}
|
|
|
|
return (
|
|
<span>
|
|
{title}
|
|
{showPlan && (
|
|
<span
|
|
style={{
|
|
marginLeft: '5px',
|
|
}}
|
|
>
|
|
<Pill text={`${planName} Plan`} color={Yellow} />
|
|
</span>
|
|
)}
|
|
</span>
|
|
);
|
|
};
|
|
|
|
const getCardComponent: GetReactElementFunction = (): ReactElement => {
|
|
if (showTableAs === ShowTableAs.List) {
|
|
return (
|
|
<div>
|
|
{props.cardProps && (
|
|
<Card
|
|
bodyClassName="-ml-6 -mr-6 bg-gray-50 border-top"
|
|
{...props.cardProps}
|
|
buttons={cardButtons}
|
|
title={getCardTitle(props.cardProps.title)}
|
|
>
|
|
<div className="mt-6 border-t border-gray-200">
|
|
<div className="ml-6 mr-6 pt-6">
|
|
{tableColumns.length === 0 &&
|
|
props.columns.length > 0 && (
|
|
<ErrorMessage
|
|
error={`You are not authorized to view this list. You need any one of these permissions: ${PermissionHelper.getPermissionTitles(
|
|
model.getReadPermissions()
|
|
).join(', ')}`}
|
|
/>
|
|
)}
|
|
{!(
|
|
tableColumns.length === 0 &&
|
|
props.columns.length > 0
|
|
) && getList()}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
)}
|
|
|
|
{!props.cardProps && getList()}
|
|
</div>
|
|
);
|
|
} else if (showTableAs === ShowTableAs.Table) {
|
|
return (
|
|
<div>
|
|
{props.cardProps && (
|
|
<Card
|
|
{...props.cardProps}
|
|
buttons={cardButtons}
|
|
title={getCardTitle(props.cardProps.title)}
|
|
>
|
|
{tableColumns.length === 0 &&
|
|
props.columns.length > 0 ? (
|
|
<ErrorMessage
|
|
error={`You are not authorized to view this table. You need any one of these permissions: ${PermissionHelper.getPermissionTitles(
|
|
model.getReadPermissions()
|
|
).join(', ')}`}
|
|
/>
|
|
) : (
|
|
<></>
|
|
)}
|
|
{!(
|
|
tableColumns.length === 0 &&
|
|
props.columns.length > 0
|
|
) ? (
|
|
getTable()
|
|
) : (
|
|
<></>
|
|
)}
|
|
</Card>
|
|
)}
|
|
|
|
{!props.cardProps && getTable()}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
{props.cardProps && (
|
|
<Card
|
|
{...props.cardProps}
|
|
buttons={cardButtons}
|
|
title={getCardTitle(props.cardProps.title)}
|
|
>
|
|
{getOrderedStatesList()}
|
|
</Card>
|
|
)}
|
|
|
|
{!props.cardProps && getOrderedStatesList()}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="mb-5 mt-5">{getCardComponent()}</div>
|
|
|
|
{showModel ? (
|
|
props.callbacks.showCreateEditModal({
|
|
onClose: () => {
|
|
setShowModal(false);
|
|
},
|
|
modalType: modalType,
|
|
onBeforeCreate: async (
|
|
item: TBaseModel,
|
|
miscDataProps: JSONObject
|
|
) => {
|
|
if (
|
|
showTableAs === ShowTableAs.OrderedStatesList &&
|
|
props.orderedStatesListProps?.orderField &&
|
|
orderedStatesListNewItemOrder
|
|
) {
|
|
item.setColumnValue(
|
|
props.orderedStatesListProps.orderField,
|
|
orderedStatesListNewItemOrder
|
|
);
|
|
}
|
|
|
|
if (props.onBeforeCreate) {
|
|
item = await props.onBeforeCreate(
|
|
item,
|
|
miscDataProps
|
|
);
|
|
}
|
|
|
|
return item;
|
|
},
|
|
onSuccess: async (item: TBaseModel): Promise<void> => {
|
|
setShowModal(false);
|
|
setCurrentPageNumber(1);
|
|
fetchItems();
|
|
if (props.onCreateSuccess) {
|
|
await props.onCreateSuccess(item);
|
|
}
|
|
|
|
return Promise.resolve();
|
|
},
|
|
modelIdToEdit: currentEditableItem
|
|
? new ObjectID(currentEditableItem['_id'] as string)
|
|
: undefined,
|
|
})
|
|
) : (
|
|
<></>
|
|
)}
|
|
|
|
{showDeleteConfirmModal && (
|
|
<ConfirmModal
|
|
title={`Delete ${props.singularName || model.singularName}`}
|
|
description={`Are you sure you want to delete this ${(
|
|
props.singularName ||
|
|
model.singularName ||
|
|
'item'
|
|
)?.toLowerCase()}?`}
|
|
onClose={() => {
|
|
setShowDeleteConfirmModal(false);
|
|
}}
|
|
submitButtonText={'Delete'}
|
|
onSubmit={() => {
|
|
if (
|
|
currentDeleteableItem &&
|
|
currentDeleteableItem['_id']
|
|
) {
|
|
deleteItem(
|
|
props.callbacks.getModelFromJSON(
|
|
currentDeleteableItem
|
|
)
|
|
);
|
|
setShowDeleteConfirmModal(false);
|
|
}
|
|
}}
|
|
submitButtonType={ButtonStyleType.DANGER}
|
|
/>
|
|
)}
|
|
|
|
{errorModalText && (
|
|
<ConfirmModal
|
|
title={`Error`}
|
|
description={`${errorModalText}`}
|
|
submitButtonText={'Close'}
|
|
onSubmit={() => {
|
|
setErrorModalText('');
|
|
}}
|
|
submitButtonType={ButtonStyleType.NORMAL}
|
|
/>
|
|
)}
|
|
|
|
{showViewIdModal && (
|
|
<ConfirmModal
|
|
title={`${
|
|
props.singularName || model.singularName || ''
|
|
} ID`}
|
|
description={
|
|
<div>
|
|
<span>
|
|
ID of this{' '}
|
|
{props.singularName || model.singularName || ''}
|
|
: {viewId}
|
|
</span>
|
|
<br />
|
|
<br />
|
|
|
|
<span>
|
|
You can use this ID to interact with{' '}
|
|
{props.singularName || model.singularName || ''}{' '}
|
|
via the OneUptime API. Click the button below to
|
|
go to API Documentation.
|
|
</span>
|
|
</div>
|
|
}
|
|
onClose={() => {
|
|
setShowViewIdModal(false);
|
|
}}
|
|
submitButtonText={'Go to API Docs'}
|
|
onSubmit={() => {
|
|
setShowViewIdModal(false);
|
|
Navigation.navigate(
|
|
URL.fromString(API_DOCS_URL.toString()).addRoute(
|
|
'/' + model.getAPIDocumentationPath()
|
|
),
|
|
{ openInNewTab: true }
|
|
);
|
|
}}
|
|
submitButtonType={ButtonStyleType.NORMAL}
|
|
closeButtonType={ButtonStyleType.OUTLINE}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default BaseModelTable;
|