import Includes from "../../../Types/BaseDatabase/Includes";
import QueryOperator from "../../../Types/BaseDatabase/QueryOperator";
import { API_DOCS_URL, BILLING_ENABLED, getAllEnvVars } from "../../Config";
import API from "../../Utils/API/API";
import useTranslateValue from "../../Utils/Translation";
import { Logger } from "../../Utils/Logger";
import Navigation from "../../Utils/Navigation";
import TableFilterUrlState from "../../Utils/TableFilterUrlState";
import PermissionUtil from "../../Utils/Permission";
import ProjectUtil from "../../Utils/Project";
import User from "../../Utils/User";
import Button, { ButtonSize, ButtonStyleType } from "../Button/Button";
import MoreMenu from "../MoreMenu/MoreMenu";
import MoreMenuItem from "../MoreMenu/MoreMenuItem";
import Card from "../Card/Card";
import { getRefreshButton } from "../Card/CardButtons/Refresh";
import ErrorMessage from "../ErrorMessage/ErrorMessage";
import List from "../List/List";
import ConfirmModal from "../Modal/ConfirmModal";
import Modal, { ModalWidth } from "../Modal/Modal";
import MarkdownViewer from "../Markdown.tsx/MarkdownViewer";
import Icon from "../Icon/Icon";
import OrderedStatesList from "../OrderedStatesList/OrderedStatesList";
import Pill from "../Pill/Pill";
import Table from "../Table/Table";
import FieldType from "../Types/FieldType";
import AnalyticsBaseModel from "../../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
import Route from "../../../Types/API/Route";
import URL from "../../../Types/API/URL";
import InBetween from "../../../Types/BaseDatabase/InBetween";
import Search from "../../../Types/BaseDatabase/Search";
import MultiSearch from "../../../Types/BaseDatabase/MultiSearch";
import SortOrder from "../../../Types/BaseDatabase/SortOrder";
import SubscriptionPlan from "../../../Types/Billing/SubscriptionPlan";
import { Yellow } from "../../../Types/BrandColors";
import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
import BadDataException from "../../../Types/Exception/BadDataException";
import Color from "../../../Types/Color";
import IconProp from "../../../Types/Icon/IconProp";
import ObjectID from "../../../Types/ObjectID";
import Permission, { PermissionHelper, } from "../../../Types/Permission";
import Typeof from "../../../Types/Typeof";
import React, { useEffect, useMemo, useState, } from "react";
import TableViewElement from "./TableView";
import UserPreferences, { UserPreferenceType, } from "../../../Utils/UserPreferences";
export var ShowAs;
(function (ShowAs) {
    ShowAs[ShowAs["Table"] = 0] = "Table";
    ShowAs[ShowAs["List"] = 1] = "List";
    ShowAs[ShowAs["OrderedStatesList"] = 2] = "OrderedStatesList";
})(ShowAs || (ShowAs = {}));
export var ModalTableBulkDefaultActions;
(function (ModalTableBulkDefaultActions) {
    ModalTableBulkDefaultActions["Delete"] = "Delete";
})(ModalTableBulkDefaultActions || (ModalTableBulkDefaultActions = {}));
export var ModalType;
(function (ModalType) {
    ModalType[ModalType["Create"] = 0] = "Create";
    ModalType[ModalType["Edit"] = 1] = "Edit";
})(ModalType || (ModalType = {}));
const BaseModelTable = (props) => {
    var _a, _b;
    const { translateValue, translateString } = useTranslateValue();
    const tx = (value) => {
        var _a;
        return (_a = translateString(value)) !== null && _a !== void 0 ? _a : value;
    };
    const [tableView, setTableView] = useState(null);
    const matchBulkSelectedItemByField = ((_a = props.bulkActions) === null || _a === void 0 ? void 0 : _a.matchBulkSelectedItemByField) || "_id";
    const model = new props.modelType();
    const [bulkSelectedItems, setBulkSelectedItems] = useState([]);
    let showAs = props.showAs;
    if (!showAs) {
        showAs = ShowAs.Table;
    }
    const propsQueryRef = React.useRef(props.query || {});
    const [showViewIdModal, setShowViewIdModal] = useState(false);
    const [showHelpModal, setShowHelpModal] = useState(false);
    const [viewId, setViewId] = useState(null);
    const [tableColumns, setColumns] = useState([]);
    const [classicTableFilters, setClassicTableFilters] = useState([]);
    const [cardButtons, setCardButtons] = useState([]);
    const [actionButtonSchema, setActionButtonSchema] = useState([]);
    useEffect(() => {
        if (props.showCreateForm) {
            setShowModal(true);
            setModalType(ModalType.Create);
        }
    }, [props.showCreateForm]);
    useEffect(() => {
        if (props.onShowFormType) {
            props.onShowFormType(modalType);
        }
    }, [props.modelType]);
    const getItemsOnPage = () => {
        if (props.userPreferencesKey) {
            const itemsOnPage = UserPreferences.getUserPreferenceByTypeAsNumber({
                userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
                key: props.userPreferencesKey,
            });
            if (itemsOnPage) {
                return itemsOnPage;
            }
        }
        return props.initialItemsOnPage || 10;
    };
    const [orderedStatesListNewItemOrder, setOrderedStatesListNewItemOrder] = useState(null);
    const [onBeforeFetchData, setOnBeforeFetchData] = useState(undefined);
    const [data, setData] = useState([]);
    const [query, setQuery] = useState({});
    const [currentPageNumber, setCurrentPageNumber] = useState(1);
    const [totalItemsCount, setTotalItemsCount] = useState(0);
    /*
     * Analytics endpoints (Log/Span/Metric/...) skip COUNT(*) for
     * performance and instead return `hasMore`. When `hasMore` is
     * `undefined`, the endpoint emitted an exact `count` and the
     * pagination falls back to the count-based UI.
     */
    const [hasMore, setHasMore] = useState(undefined);
    const [isLoading, setIsLoading] = useState(true);
    const [error, setError] = useState("");
    const [tableFilterError, setTableFilterError] = useState("");
    const [searchText, setSearchText] = useState("");
    const [debouncedSearchText, setDebouncedSearchText] = useState("");
    const [isSearchFocused, setIsSearchFocused] = useState(false);
    const [isSearchExpanded, setIsSearchExpanded] = useState(false);
    const searchInputRef = React.useRef(null);
    const [availableLabels, setAvailableLabels] = useState([]);
    const [selectedLabels, setSelectedLabels] = useState([]);
    const [isLabelsLoading, setIsLabelsLoading] = useState(false);
    const [labelsFetched, setLabelsFetched] = useState(false);
    const [labelDropdownIndex, setLabelDropdownIndex] = useState(0);
    useEffect(() => {
        const handle = setTimeout(() => {
            setDebouncedSearchText(searchText);
        }, 300);
        return () => {
            clearTimeout(handle);
        };
    }, [searchText]);
    /*
     * "/" focuses the search input — same affordance as GitHub / Linear.
     * Skip while the user is typing in another input/textarea or has a modal open.
     */
    useEffect(() => {
        if (!props.searchableFields || props.searchableFields.length === 0) {
            return undefined;
        }
        const handleKey = (e) => {
            if (e.key !== "/" || e.metaKey || e.ctrlKey || e.altKey) {
                return;
            }
            const target = e.target;
            if (target &&
                (target.tagName === "INPUT" ||
                    target.tagName === "TEXTAREA" ||
                    target.isContentEditable)) {
                return;
            }
            e.preventDefault();
            setIsSearchExpanded(true);
            // Wait one frame so the input mounts/becomes visible before focusing.
            requestAnimationFrame(() => {
                var _a;
                (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
            });
        };
        document.addEventListener("keydown", handleKey);
        return () => {
            document.removeEventListener("keydown", handleKey);
        };
    }, [props.searchableFields]);
    /*
     * Keep the search expanded whenever there is an active search term — so
     * results stay visible alongside the box. Collapsing only happens when the
     * user blurs an empty input.
     */
    useEffect(() => {
        if ((debouncedSearchText.trim().length > 0 || selectedLabels.length > 0) &&
            !isSearchExpanded) {
            setIsSearchExpanded(true);
        }
    }, [debouncedSearchText, selectedLabels]);
    useEffect(() => {
        // reset to first page whenever the active search term or labels change
        setCurrentPageNumber(1);
    }, [debouncedSearchText, selectedLabels]);
    const labelFilterConfig = useMemo(() => {
        const filter = props.filters.find((f) => {
            return (f.filterEntityType &&
                f.filterEntityType.name === "Label" &&
                f.field &&
                f.filterDropdownField);
        });
        if (!filter || !filter.field || !filter.filterDropdownField) {
            return null;
        }
        const fieldKey = Object.keys(filter.field)[0];
        if (!fieldKey) {
            return null;
        }
        return {
            fieldKey,
            entityType: filter.filterEntityType,
            fetchQuery: filter.filterQuery || {},
            labelField: filter.filterDropdownField.label,
        };
    }, [props.filters]);
    // Fetch labels on first search expansion if this resource supports them.
    useEffect(() => {
        if (!isSearchExpanded || !labelFilterConfig || labelsFetched) {
            return;
        }
        let cancelled = false;
        setIsLabelsLoading(true);
        (async () => {
            try {
                const result = await props.callbacks.getList({
                    modelType: labelFilterConfig.entityType,
                    query: labelFilterConfig.fetchQuery,
                    limit: 200,
                    skip: 0,
                    select: {
                        _id: true,
                        [labelFilterConfig.labelField]: true,
                        color: true,
                    },
                    sort: { [labelFilterConfig.labelField]: SortOrder.Ascending },
                });
                if (cancelled) {
                    return;
                }
                const mapped = (result.data || [])
                    .map((item) => {
                    var _a;
                    const raw = item;
                    const colorAny = raw["color"];
                    const colorHex = (colorAny &&
                        (typeof colorAny === "string"
                            ? colorAny
                            : colorAny.value ||
                                (colorAny.toString && colorAny.toString()))) ||
                        "#94a3b8";
                    return {
                        id: ((_a = raw["_id"]) === null || _a === void 0 ? void 0 : _a.toString()) || "",
                        name: raw[labelFilterConfig.labelField] || "",
                        color: colorHex,
                    };
                })
                    .filter((l) => {
                    return l.id && l.name;
                });
                setAvailableLabels(mapped);
                setLabelsFetched(true);
            }
            catch (_a) {
                // Silently fail — search still works without label suggestions.
            }
            finally {
                if (!cancelled) {
                    setIsLabelsLoading(false);
                }
            }
        })();
        return () => {
            cancelled = true;
        };
    }, [isSearchExpanded, labelFilterConfig, labelsFetched]);
    const [showModel, setShowModal] = useState(false);
    const [showFilterModal, setShowFilterModal] = useState(false);
    const [modalType, setModalType] = useState(ModalType.Create);
    const [sortBy, setSortBy] = useState(props.sortBy || null);
    const [sortOrder, setSortOrder] = useState(props.sortOrder || SortOrder.Ascending);
    const [showDeleteConfirmModal, setShowDeleteConfirmModal] = useState(false);
    const [currentEditableItem, setCurrentEditableItem] = useState(null);
    const [currentDeleteableItem, setCurrentDeleteableItem] = useState(null);
    const [itemsOnPage, setItemsOnPage] = useState(getItemsOnPage());
    useEffect(() => {
        // update items on page in localstorage.
        if (itemsOnPage && props.userPreferencesKey) {
            UserPreferences.saveUserPreferenceByTypeAsNumber({
                userPreferenceType: UserPreferenceType.BaseModelTablePageSize,
                key: props.userPreferencesKey,
                value: itemsOnPage,
            });
        }
    }, [itemsOnPage]);
    const [fields, setFields] = useState([]);
    const [isFilterFetchLoading, setIsFilterFetchLoading] = useState(false);
    const [errorModalText, setErrorModalText] = useState("");
    useEffect(() => {
        const currentQuery = propsQueryRef.current;
        const newQuery = props.query || {};
        // only update if the query has changed.
        if (JSON.stringify(currentQuery) !== JSON.stringify(newQuery)) {
            propsQueryRef.current = newQuery;
            fetchItems().catch((err) => {
                setError(API.getFriendlyMessage(err));
            });
        }
    }, [props.query]);
    useEffect(() => {
        var _a;
        if (!showModel) {
            (_a = props.onCreateEditModalClose) === null || _a === void 0 ? void 0 : _a.call(props);
        }
    }, [showModel]);
    useEffect(() => {
        const detailFields = [];
        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,
                hideOnMobile: column.hideOnMobile, // Propagate hideOnMobile property
                getElement: column.getElement
                    ? (item) => {
                        return column.getElement(item, onBeforeFetchData);
                    }
                    : undefined,
            });
            setFields(detailFields);
        }
    }, [tableColumns]);
    const getRelationSelect = () => {
        const relationSelect = {};
        for (const column of props.columns || []) {
            const key = column.field
                ? Object.keys(column.field)[0]
                : null;
            if (key && model.isFileColumn(key)) {
                relationSelect[key] = {
                    file: true,
                    _id: true,
                    fileType: true,
                    name: true,
                };
            }
            else if (key && model.isEntityColumn(key)) {
                if (!relationSelect[key]) {
                    relationSelect[key] = {};
                }
                relationSelect[key] = Object.assign(Object.assign({}, relationSelect[key]), column.field[key]);
            }
        }
        return relationSelect;
    };
    const deleteItem = async (item) => {
        var _a;
        if (!item.id) {
            throw new BadDataException("item.id cannot be null");
        }
        setIsLoading(true);
        try {
            await props.callbacks.deleteItem(item);
            (_a = props.onItemDeleted) === null || _a === void 0 ? void 0 : _a.call(props, item);
            if (data.length === 1 && currentPageNumber > 1) {
                setCurrentPageNumber(currentPageNumber - 1);
            }
            await fetchItems();
        }
        catch (err) {
            setErrorModalText(API.getFriendlyMessage(err));
        }
        setIsLoading(false);
    };
    const serializeToTableColumns = () => {
        // Convert ModelColumns to TableColumns.
        var _a, _b;
        const columns = [];
        let selectFields = {
            _id: true,
        };
        selectFields = props.callbacks.addSlugToSelect(selectFields);
        const userPermissions = getUserPermissions();
        const accessControl = model.getColumnAccessControlForAllColumns();
        for (const column of props.columns || []) {
            const hasPermission = hasPermissionToReadColumn(column) || User.isMasterAdmin();
            const key = getColumnKey(column);
            if (hasPermission) {
                let tooltipText = undefined;
                if (column.tooltipText) {
                    tooltipText = (item) => {
                        if (item instanceof BaseModel ||
                            item instanceof AnalyticsBaseModel) {
                            return column.tooltipText(item);
                        }
                        return column.tooltipText(props.callbacks.getModelFromJSON(item));
                    };
                }
                // get filter options if they were already loaded
                const columnKey = column.selectedProperty
                    ? (key +
                        "." +
                        column.selectedProperty)
                    : key;
                columns.push(Object.assign(Object.assign({}, column), { disableSort: column.disableSort || shouldDisableSort(key), key: columnKey, tooltipText, getElement: column.getElement }));
                if (key) {
                    selectFields[key] = true;
                }
            }
        }
        const selectMoreFields = props.selectMoreFields
            ? Object.keys(props.selectMoreFields)
            : [];
        for (const moreField of selectMoreFields) {
            let hasPermissionToSelectField = true;
            let fieldPermissions = [];
            fieldPermissions = ((_a = accessControl[moreField]) === null || _a === void 0 ? void 0 : _a.read) || [];
            if (((_b = accessControl[moreField]) === null || _b === void 0 ? void 0 : _b.read) &&
                !PermissionHelper.doesPermissionsIntersect(userPermissions, fieldPermissions)) {
                hasPermissionToSelectField = false;
            }
            if (hasPermissionToSelectField) {
                selectFields[moreField] = true;
            }
            else {
                Logger.warn("User does not have read permissions to read - " + moreField);
            }
        }
        const permissions = PermissionUtil.getAllPermissions();
        let showActionsColumn = Boolean((permissions &&
            ((props.isDeleteable && model.hasDeletePermissions(permissions)) ||
                (props.isEditable && model.hasUpdatePermissions(permissions)) ||
                (props.isViewable && model.hasReadPermissions(permissions)))) ||
            (props.actionButtons && props.actionButtons.length > 0) ||
            props.showViewIdButton);
        if (User.isMasterAdmin()) {
            if ((props.actionButtons && props.actionButtons.length > 0) ||
                props.showViewIdButton ||
                props.isDeleteable ||
                props.isEditable ||
                props.isViewable) {
                showActionsColumn = true;
            }
        }
        if (showActionsColumn) {
            columns.push({
                title: tx("Actions"),
                type: FieldType.Actions,
            });
        }
        setActionSchema();
        setHeaderButtons();
        setColumns(columns);
    };
    const getFilterDropdownItems = async () => {
        setTableFilterError("");
        setIsFilterFetchLoading(true);
        const filters = [...props.filters];
        try {
            for (const filter of filters) {
                const key = getFilterKey(filter);
                if (!key) {
                    continue;
                }
                if (!filter.filterEntityType) {
                    continue;
                }
                if (!filter.filterDropdownField) {
                    Logger.warn(`Cannot filter on ${key.toString()} because filter.dropdownField is not set.`);
                    continue;
                }
                const hasPermission = hasPermissionToReadFilter(filter);
                if (!hasPermission) {
                    continue;
                }
                if (filter.fetchFilterDropdownOptions) {
                    // fetch filter dropdown options.
                    filter.filterDropdownOptions =
                        await filter.fetchFilterDropdownOptions();
                    continue;
                }
                const query = filter.filterQuery || {};
                let colorColumnName = null;
                let accessControlColumnName = null;
                if (filter.filterEntityType &&
                    filter.filterEntityType.prototype instanceof BaseModel) {
                    const filterModel = new filter.filterEntityType();
                    colorColumnName = filterModel.getFirstColorColumn();
                    accessControlColumnName = filterModel.getAccessControlColumn();
                }
                const select = {
                    [filter.filterDropdownField.label]: true,
                    [filter.filterDropdownField.value]: true,
                };
                if (colorColumnName) {
                    select[colorColumnName] = true;
                }
                if (accessControlColumnName) {
                    select[accessControlColumnName] = {
                        _id: true,
                        name: true,
                        color: true,
                    };
                }
                const listResult = await props.callbacks.getList({
                    modelType: filter.filterEntityType,
                    query: query,
                    limit: LIMIT_PER_PROJECT,
                    skip: 0,
                    select: select,
                    sort: {},
                });
                filter.filterDropdownOptions = [];
                for (const item of listResult.data) {
                    const option = {
                        value: item.getColumnValue(filter.filterDropdownField.value),
                        label: item.getColumnValue(filter.filterDropdownField.label),
                    };
                    if (colorColumnName) {
                        const colorValue = item.getColumnValue(colorColumnName);
                        if (colorValue instanceof Color) {
                            option.color = colorValue;
                        }
                        else if (typeof colorValue === "string" &&
                            colorValue.trim().length > 0) {
                            try {
                                option.color = new Color(colorValue);
                            }
                            catch (_a) {
                                // ignore invalid colors
                            }
                        }
                    }
                    if (accessControlColumnName) {
                        const accessControlValue = item.getColumnValue(accessControlColumnName) || null;
                        const accessControlItems = Array.isArray(accessControlValue)
                            ? accessControlValue
                            : accessControlValue
                                ? [accessControlValue]
                                : [];
                        const dropdownLabels = accessControlItems
                            .map((label) => {
                            if (!label) {
                                return null;
                            }
                            const labelNameRaw = label.getColumnValue("name");
                            if (!labelNameRaw) {
                                return null;
                            }
                            const labelName = labelNameRaw.toString().trim();
                            if (!labelName) {
                                return null;
                            }
                            const labelColorValue = label.getColumnValue("color");
                            const normalizedLabel = {
                                name: labelName,
                            };
                            const labelId = label.id;
                            if (labelId) {
                                normalizedLabel.id = labelId.toString();
                            }
                            if (labelColorValue) {
                                normalizedLabel.color = labelColorValue;
                            }
                            return normalizedLabel;
                        })
                            .filter((label) => {
                            return label !== null;
                        });
                        if (dropdownLabels.length > 0) {
                            option.labels = dropdownLabels;
                        }
                    }
                    filter.filterDropdownOptions.push(option);
                }
            }
            const classicFilters = filters
                .map((filter) => {
                const key = getFilterKey(filter);
                if (!key) {
                    return null;
                }
                return {
                    title: filter.title,
                    filterDropdownOptions: filter.filterDropdownOptions,
                    key: key,
                    type: filter.type,
                    jsonKeys: filter.jsonKeys,
                    isAdvancedFilter: filter.isAdvancedFilter,
                };
            })
                .filter((filter) => {
                return filter !== null;
            });
            setClassicTableFilters(classicFilters);
            setHeaderButtons();
        }
        catch (err) {
            setTableFilterError(API.getFriendlyMessage(err));
        }
        setIsFilterFetchLoading(false);
    };
    const buildSearchQueryFragment = () => {
        const fragment = {};
        /*
         * Strip the trailing @<prefix> mention before searching — that token
         * is a label-autocomplete trigger, not part of the user's free-text
         * query.
         */
        const stripTrailingMention = (v) => {
            const atIndex = v.lastIndexOf("@");
            if (atIndex < 0) {
                return v;
            }
            if (atIndex > 0) {
                const prev = v[atIndex - 1] || "";
                if (prev !== " " && prev !== "\t") {
                    return v;
                }
            }
            const after = v.substring(atIndex + 1);
            if (after.includes(" ") ||
                after.includes("\t") ||
                after.includes("\n")) {
                return v;
            }
            return v.substring(0, atIndex).trimEnd();
        };
        const effectiveSearch = stripTrailingMention(debouncedSearchText).trim();
        if (effectiveSearch.length > 0 &&
            props.searchableFields &&
            props.searchableFields.length > 0) {
            fragment._multiFieldSearch = new MultiSearch({
                fields: props.searchableFields.map((f) => {
                    return f;
                }),
                value: effectiveSearch,
            });
        }
        if (labelFilterConfig && selectedLabels.length > 0) {
            fragment[labelFilterConfig.fieldKey] = new Includes(selectedLabels.map((l) => {
                return l.id;
            }));
        }
        return fragment;
    };
    const fetchAllBulkItems = async () => {
        setError("");
        setIsLoading(true);
        try {
            const listResult = await props.callbacks.getList({
                modelType: props.modelType,
                query: Object.assign(Object.assign(Object.assign({}, props.query), query), buildSearchQueryFragment()),
                limit: LIMIT_PER_PROJECT,
                skip: 0,
                select: {
                    _id: true,
                },
                sort: {},
                requestOptions: props.fetchRequestOptions,
            });
            setBulkSelectedItems(listResult.data);
        }
        catch (err) {
            setError(API.getFriendlyMessage(err));
        }
        setIsLoading(false);
    };
    const fetchItems = async () => {
        setError("");
        setIsLoading(true);
        if (props.onFetchInit) {
            props.onFetchInit(currentPageNumber, itemsOnPage);
        }
        if (props.onBeforeFetch) {
            const model = await props.onBeforeFetch();
            setOnBeforeFetchData(model);
        }
        try {
            const listResult = await props.callbacks.getList({
                modelType: props.modelType,
                query: Object.assign(Object.assign(Object.assign({}, props.query), query), buildSearchQueryFragment()),
                groupBy: Object.assign({}, props.groupBy),
                limit: itemsOnPage,
                skip: (currentPageNumber - 1) * itemsOnPage,
                select: Object.assign(Object.assign({}, getSelect()), getRelationSelect()),
                sort: sortBy
                    ? {
                        [sortBy]: sortOrder,
                    }
                    : {},
                requestOptions: props.fetchRequestOptions,
            });
            setTotalItemsCount(listResult.count);
            setHasMore(listResult.hasMore);
            setData(listResult.data);
            /*
             * Fire onFetchSuccess so consumers (e.g. the resource-owners hook)
             * can react to the loaded page. Previously the prop was declared
             * but never invoked, which broke per-row owner enrichment.
             */
            if (props.onFetchSuccess) {
                props.onFetchSuccess(listResult.data, listResult.count);
            }
        }
        catch (err) {
            setError(API.getFriendlyMessage(err));
        }
        setIsLoading(false);
    };
    useEffect(() => {
        if (showFilterModal) {
            getFilterDropdownItems().catch((err) => {
                setTableFilterError(API.getFriendlyMessage(err));
            });
        }
    }, [showFilterModal, props.filters]);
    const getSelect = () => {
        const selectFields = {
            _id: true,
        };
        for (const column of props.columns || []) {
            const key = column.field
                ? Object.keys(column.field)[0]
                : null;
            if (key) {
                if (model.hasColumn(key)) {
                    selectFields[key] = true;
                }
                else {
                    throw new BadDataException(`${key} column not found on ${model.singularName}`);
                }
            }
        }
        const selectMoreFields = 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[moreField] = props.selectMoreFields[moreField];
            }
            else if (model.hasColumn(moreField)) {
                selectFields[moreField] = true;
            }
            else {
                throw new BadDataException(`${moreField} column not found on ${model.singularName}`);
            }
        }
        return selectFields;
    };
    const getSaveFilterDropdown = () => {
        if (!props.saveFilterProps) {
            return React.createElement(React.Fragment, null);
        }
        if (props.saveFilterProps && props.saveFilterProps.tableId) {
            return (React.createElement(TableViewElement, { tableId: props.saveFilterProps.tableId, tableView: tableView, currentQuery: query, currentSortBy: sortBy, currentItemsOnPage: itemsOnPage, currentSortOrder: sortOrder, currentFacetState: props.currentFacetState, onViewChange: async (tableView) => {
                    setTableView(tableView);
                    if (tableView) {
                        const sortBy = Object.keys(tableView.sort || {})[0];
                        let sortOrder = SortOrder.Descending;
                        if (sortBy && tableView.sort) {
                            sortOrder =
                                tableView.sort[sortBy] ||
                                    SortOrder.Descending;
                        }
                        // then set query, sort and items on the page
                        setQuery(tableView.query || {});
                        setFilterData(tableView.query || {});
                        setItemsOnPage(tableView.itemsOnPage || 10);
                        setSortBy(sortBy || null);
                        setSortOrder(sortOrder);
                        setCurrentPageNumber(1);
                        if (classicTableFilters.length === 0) {
                            await getFilterDropdownItems();
                        }
                        if (props.onFacetStateRestored) {
                            props.onFacetStateRestored(tableView.facets || null);
                        }
                    }
                    else {
                        setQuery({});
                        setSortBy(null);
                        setSortOrder(SortOrder.Descending);
                        setItemsOnPage(10);
                        setCurrentPageNumber(1);
                        if (props.onFacetStateRestored) {
                            props.onFacetStateRestored(null);
                        }
                    }
                } }));
        }
        return React.createElement(React.Fragment, null);
    };
    const setHeaderButtons = () => {
        var _a, _b;
        // add header buttons.
        let headerbuttons = [];
        // Add documentation link button first if provided
        if (props.documentationLink) {
            headerbuttons.push({
                title: tx("View Documentation"),
                icon: IconProp.Book,
                buttonStyle: ButtonStyleType.HOVER_PRIMARY_OUTLINE,
                buttonSize: ButtonSize.Small,
                className: "hidden md:flex",
                onClick: () => {
                    Navigation.navigate(props.documentationLink, {
                        openInNewTab: true,
                    });
                },
            });
        }
        // Add help content button if provided
        if (props.helpContent) {
            headerbuttons.push({
                title: "",
                icon: IconProp.Help,
                buttonStyle: ButtonStyleType.ICON,
                buttonSize: ButtonSize.Small,
                className: "",
                onClick: () => {
                    setShowHelpModal(true);
                },
            });
        }
        // Add video link button if provided
        if (props.videoLink) {
            headerbuttons.push({
                title: tx("Watch Demo"),
                icon: IconProp.Play,
                buttonStyle: ButtonStyleType.HOVER_PRIMARY_OUTLINE,
                buttonSize: ButtonSize.Small,
                className: "hidden md:flex",
                onClick: () => {
                    Navigation.navigate(props.videoLink, {
                        openInNewTab: true,
                    });
                },
            });
        }
        if (((_a = props.cardProps) === null || _a === void 0 ? void 0 : _a.buttons) && ((_b = props.cardProps) === null || _b === void 0 ? void 0 : _b.buttons.length) > 0) {
            headerbuttons = [...headerbuttons, ...props.cardProps.buttons];
        }
        const permissions = PermissionUtil.getAllPermissions();
        let hasPermissionToCreate = false;
        if (permissions) {
            hasPermissionToCreate =
                model.hasCreatePermissions(permissions) || User.isMasterAdmin();
        }
        const showFilterButton = props.filters.length > 0;
        // because ordered list add button is inside the table and not on the card header.
        if (props.isCreateable &&
            hasPermissionToCreate &&
            showAs !== ShowAs.OrderedStatesList) {
            headerbuttons.push({
                title: `${tx(props.createVerb || "Create")} ${tx(props.singularName || model.singularName || "")}`,
                buttonStyle: ButtonStyleType.NORMAL,
                buttonSize: ButtonSize.Normal,
                className: "",
                onClick: () => {
                    if (props.onCreateClick) {
                        props.onCreateClick();
                        return;
                    }
                    setModalType(ModalType.Create);
                    setShowModal(true);
                },
                icon: IconProp.Add,
            });
        }
        if (props.showRefreshButton) {
            headerbuttons.push(Object.assign(Object.assign({}, getRefreshButton()), { buttonSize: ButtonSize.Small, className: "", onClick: async () => {
                    await fetchItems();
                }, disabled: isFilterFetchLoading }));
        }
        if (showFilterButton) {
            headerbuttons.push({
                title: "",
                buttonStyle: ButtonStyleType.ICON,
                buttonSize: ButtonSize.Small,
                className: "",
                onClick: () => {
                    setQuery({});
                    setShowFilterModal(true);
                },
                disabled: isFilterFetchLoading,
                icon: IconProp.Filter,
            });
        }
        setCardButtons(headerbuttons);
    };
    useEffect(() => {
        fetchItems().catch((err) => {
            setError(API.getFriendlyMessage(err));
        });
    }, [
        currentPageNumber,
        sortBy,
        sortOrder,
        itemsOnPage,
        query,
        debouncedSearchText,
        selectedLabels,
        props.refreshToggle,
    ]);
    const shouldDisableSort = (columnName) => {
        if (!columnName) {
            return true;
        }
        return model.isEntityColumn(columnName);
    };
    const getColumnKey = (column) => {
        const key = column.field
            ? Object.keys(column.field)[0]
            : null;
        return key;
    };
    const getFilterKey = (filter) => {
        const key = filter.field
            ? Object.keys(filter.field)[0]
            : null;
        return key;
    };
    const hasPermissionToReadColumn = (column) => {
        const key = getColumnKey(column);
        if (!key) {
            return true;
        }
        return hasPermissionToReadField(key);
    };
    const hasPermissionToReadFilter = (filter) => {
        const key = getFilterKey(filter);
        if (!key) {
            return true;
        }
        return hasPermissionToReadField(key);
    };
    const hasPermissionToReadField = (field) => {
        var _a, _b;
        const accessControl = model.getColumnAccessControlForAllColumns();
        const userPermissions = getUserPermissions();
        const key = field;
        // check permissions.
        let hasPermission = false;
        if (!key) {
            hasPermission = true;
        }
        if (key) {
            hasPermission = true;
            let fieldPermissions = [];
            fieldPermissions = ((_a = accessControl[key]) === null || _a === void 0 ? void 0 : _a.read) || [];
            if (((_b = accessControl[key]) === null || _b === void 0 ? void 0 : _b.read) &&
                !PermissionHelper.doesPermissionsIntersect(userPermissions, fieldPermissions)) {
                hasPermission = false;
            }
        }
        return hasPermission;
    };
    const getUserPermissions = () => {
        var _a, _b;
        let userPermissions = ((_a = PermissionUtil.getGlobalPermissions()) === null || _a === void 0 ? void 0 : _a.globalPermissions) || [];
        if (PermissionUtil.getProjectPermissions() &&
            ((_b = PermissionUtil.getProjectPermissions()) === null || _b === void 0 ? void 0 : _b.permissions) &&
            PermissionUtil.getProjectPermissions().permissions.length > 0) {
            userPermissions = userPermissions.concat(PermissionUtil.getProjectPermissions().permissions.map((i) => {
                return i.permission;
            }));
        }
        userPermissions.push(Permission.Public);
        return userPermissions;
    };
    useEffect(() => {
        serializeToTableColumns();
    }, []);
    useEffect(() => {
        serializeToTableColumns();
    }, [data]);
    /*
     * Re-serialize whenever the parent passes a new `columns` reference. The
     * column array carries `getElement` closures that capture parent state
     * (e.g. an owners map populated asynchronously). Without this, the cell
     * renders are frozen at first paint and never see updated state — which
     * is what made the Owners column stick on "Loading…" forever.
     */
    useEffect(() => {
        serializeToTableColumns();
    }, [props.columns]);
    const setActionSchema = () => {
        const permissions = PermissionUtil.getAllPermissions();
        const actionsSchema = [];
        if (props.showViewIdButton) {
            actionsSchema.push({
                title: tx("Show ID"),
                buttonStyleType: ButtonStyleType.OUTLINE,
                hideOnMobile: true,
                onClick: async (item, onCompleteAction, onError) => {
                    try {
                        setViewId(item["_id"]);
                        setShowViewIdModal(true);
                        onCompleteAction();
                    }
                    catch (err) {
                        onError(err);
                    }
                },
            });
        }
        // 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
                        ? tx(props.viewButtonText)
                        : `${tx("View")} ${tx(props.singularName || model.singularName || "")}`,
                    buttonStyleType: ButtonStyleType.NORMAL,
                    onClick: async (item, onCompleteAction, onError) => {
                        var _a;
                        try {
                            let baseModel = item;
                            if (!(item instanceof BaseModel) &&
                                !(item instanceof AnalyticsBaseModel)) {
                                baseModel = props.callbacks.getModelFromJSON(item);
                            }
                            if (props.onBeforeView) {
                                item = await props.onBeforeView(baseModel);
                            }
                            if (props.onViewPage) {
                                const 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 = ((_a = baseModel.id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
                            return Navigation.navigate(new Route(props.viewPageRoute.toString()).addRoute("/" + id));
                        }
                        catch (err) {
                            onError(err);
                        }
                    },
                });
            }
            if (props.isEditable &&
                (model.hasUpdatePermissions(permissions) || User.isMasterAdmin())) {
                actionsSchema.push({
                    title: tx(props.editButtonText || "Edit"),
                    buttonStyleType: ButtonStyleType.OUTLINE,
                    onClick: async (item, onCompleteAction, onError) => {
                        try {
                            if (props.onBeforeEdit) {
                                item = await props.onBeforeEdit(item);
                            }
                            setModalType(ModalType.Edit);
                            setShowModal(true);
                            setCurrentEditableItem(item);
                            onCompleteAction();
                        }
                        catch (err) {
                            onError(err);
                        }
                    },
                });
            }
            if (props.isDeleteable &&
                (model.hasDeletePermissions(permissions) || User.isMasterAdmin())) {
                actionsSchema.push({
                    title: tx(props.deleteButtonText || "Delete"),
                    icon: IconProp.Trash,
                    buttonStyleType: ButtonStyleType.DANGER_OUTLINE,
                    onClick: async (item, onCompleteAction, onError) => {
                        try {
                            if (props.onBeforeDelete) {
                                item = await props.onBeforeDelete(item);
                            }
                            setShowDeleteConfirmModal(true);
                            setCurrentDeleteableItem(item);
                            onCompleteAction();
                        }
                        catch (err) {
                            onError(err);
                        }
                    },
                });
            }
        }
        setActionButtonSchema(actionsSchema);
    };
    const [filterData, setFilterData] = useState(props.initialFilterData || {});
    const onFilterChanged = (filterData) => {
        var _a;
        const newQuery = {};
        setFilterData(filterData);
        setCurrentPageNumber(1);
        for (const key in filterData) {
            if (filterData[key] && typeof filterData[key] === Typeof.String) {
                newQuery[key] = (filterData[key] || "").toString();
            }
            if (typeof filterData[key] === Typeof.Boolean) {
                newQuery[key] = Boolean(filterData[key]);
            }
            if (typeof filterData[key] === Typeof.Number) {
                newQuery[key] = filterData[key];
            }
            if (filterData[key] instanceof Date) {
                newQuery[key] = filterData[key];
            }
            if (filterData[key] instanceof Search) {
                newQuery[key] = filterData[key];
            }
            if (filterData[key] instanceof InBetween) {
                newQuery[key] = filterData[key];
            }
            if (((_a = props.filters.find((f) => {
                return f.field && f.field[key];
            })) === null || _a === void 0 ? void 0 : _a.type) === FieldType.JSON &&
                typeof filterData[key] === Typeof.Object) {
                newQuery[key] = filterData[key];
            }
            if (Array.isArray(filterData[key])) {
                newQuery[key] = new Includes(filterData[key]);
            }
            /*
             * Pass any QueryOperator instance (IncludesAll, IncludesNone, StartsWith,
             * EndsWith, NotContains, EqualTo, NotEqual, GreaterThan, LessThan,
             * InBetween, IsNull, NotNull, ...) through to the query as-is.
             */
            if (filterData[key] instanceof QueryOperator) {
                newQuery[key] = filterData[key];
            }
        }
        setQuery(Object.assign({}, newQuery));
    };
    /*
     * URL persistence for classic (column) filters, keyed by the table's
     * `saveFilterProps.tableId`. Mirrors the facet persistence in
     * `useResourceOwners` so filters survive navigating to a detail page and
     * back, and so a filtered view is shareable. `hasRestoredUrlFilters` is
     * state (not a ref) so the persist effect only runs after the restore has
     * been applied — never clobbering the snapshot with the empty default.
     */
    const [hasRestoredUrlFilters, setHasRestoredUrlFilters] = useState(false);
    useEffect(() => {
        var _a;
        const restored = TableFilterUrlState.read((_a = props.saveFilterProps) === null || _a === void 0 ? void 0 : _a.tableId, "filter");
        if (restored) {
            /*
             * Re-run through onFilterChanged so the derived query is rebuilt and the
             * first fetch uses the restored filters.
             */
            onFilterChanged(restored);
        }
        setHasRestoredUrlFilters(true);
    }, []);
    useEffect(() => {
        var _a;
        if (!hasRestoredUrlFilters) {
            return;
        }
        TableFilterUrlState.write((_a = props.saveFilterProps) === null || _a === void 0 ? void 0 : _a.tableId, "filter", filterData);
    }, [hasRestoredUrlFilters, filterData]);
    const getDeleteBulkAction = () => {
        return {
            title: "Delete",
            buttonStyleType: ButtonStyleType.DANGER,
            icon: IconProp.Trash,
            confirmMessage: (items) => {
                const itemLabel = items.length === 1
                    ? props.singularName || model.singularName || "item"
                    : props.pluralName || model.pluralName || "items";
                return `Are you sure you want to delete ${items.length} ${itemLabel}? This action cannot be undone.`;
            },
            confirmTitle: (items) => {
                const itemLabel = items.length === 1
                    ? props.singularName || model.singularName || "item"
                    : props.pluralName || model.pluralName || "items";
                return `Delete ${items.length} ${itemLabel}`;
            },
            confirmButtonStyleType: ButtonStyleType.DANGER,
            onClick: async ({ items, onProgressInfo, onBulkActionStart, onBulkActionEnd, }) => {
                onBulkActionStart();
                const inProgressItems = [...items];
                const successItems = [];
                const failedItems = [];
                for (let i = 0; i < items.length; i++) {
                    try {
                        const item = items[i];
                        // remove items from inProgressItems
                        inProgressItems.splice(inProgressItems.indexOf(item), 1);
                        await props.callbacks.deleteItem(item);
                        successItems.push(item);
                        onProgressInfo({
                            inProgressItems: inProgressItems,
                            successItems: successItems,
                            failed: failedItems,
                            totalItems: items,
                        });
                    }
                    catch (err) {
                        failedItems.push({
                            item: items[i],
                            failedMessage: API.getFriendlyMessage(err),
                        });
                    }
                }
                onBulkActionEnd();
            },
        };
    };
    const isSearchActive = () => {
        return debouncedSearchText.trim().length > 0 || selectedLabels.length > 0;
    };
    const getTableLoadingState = () => {
        return isSearchActive() ? false : isLoading;
    };
    const getTable = () => {
        return (React.createElement(Table, { onFilterChanged: (filterData) => {
                onFilterChanged(filterData);
                setTableView(null);
                // check if there's anything in the filter data and update the isFilterApplied prop.
                let isFilterApplied = false;
                for (const key in filterData) {
                    if (filterData[key]) {
                        isFilterApplied = true;
                        break;
                    }
                }
                if (props.onFilterApplied) {
                    props.onFilterApplied(isFilterApplied);
                }
            }, filterData: filterData, className: props.cardProps
                ? ""
                : "rounded-lg border-2 border-gray-200 p-6 pt-0 pb-5", tableContainerClassName: props.cardProps ? "" : "overflow-hidden rounded", onFilterRefreshClick: async () => {
                await getFilterDropdownItems();
            }, bulkActions: (() => {
                var _a, _b;
                const permissions = PermissionUtil.getAllPermissions();
                const userCanDelete = model.hasDeletePermissions(permissions);
                const sourceButtons = [...((_b = (_a = props.bulkActions) === null || _a === void 0 ? void 0 : _a.buttons) !== null && _b !== void 0 ? _b : [])];
                /*
                 * Auto-include the default Delete bulk action whenever the user has
                 * model-level delete permission, so every table that exposes row
                 * selection also exposes a way to delete the selected rows. This is
                 * intentionally decoupled from `isDeleteable` — that flag governs the
                 * per-row Delete button in the Actions column, not bulk operations.
                 * The confirmation modal is wired up via the schema's confirmMessage
                 * / confirmTitle below. Skip if the table author already added it.
                 */
                const alreadyHasDeleteAction = sourceButtons.some((action) => {
                    if (action === ModalTableBulkDefaultActions.Delete) {
                        return true;
                    }
                    if (typeof action === "object" &&
                        action !== null &&
                        "title" in action &&
                        action.title ===
                            "Delete") {
                        return true;
                    }
                    return false;
                });
                if (userCanDelete && !alreadyHasDeleteAction) {
                    sourceButtons.push(ModalTableBulkDefaultActions.Delete);
                }
                return {
                    buttons: sourceButtons.map((action) => {
                        if (action === ModalTableBulkDefaultActions.Delete &&
                            userCanDelete) {
                            return getDeleteBulkAction();
                        }
                        return action;
                    }),
                };
            })(), onBulkActionEnd: async () => {
                setBulkSelectedItems([]);
                await fetchItems();
            }, onBulkActionStart: () => { }, bulkSelectedItems: bulkSelectedItems, onBulkSelectedItemAdded: (item) => {
                setBulkSelectedItems([...bulkSelectedItems, item]);
            }, onBulkSelectedItemRemoved: (item) => {
                setBulkSelectedItems(bulkSelectedItems.filter((i) => {
                    var _a, _b;
                    return (((_a = i[matchBulkSelectedItemByField]) === null || _a === void 0 ? void 0 : _a.toString()) !==
                        ((_b = item[matchBulkSelectedItemByField]) === null || _b === void 0 ? void 0 : _b.toString()));
                }));
            }, sortBy: sortBy, sortOrder: sortOrder, onBulkSelectItemsOnCurrentPage: () => {
                const items = [...bulkSelectedItems, ...data];
                // remove duplicates
                const uniqueItems = items.filter((item, index, self) => {
                    return (index ===
                        self.findIndex((t) => {
                            var _a, _b;
                            return (((_a = t[matchBulkSelectedItemByField]) === null || _a === void 0 ? void 0 : _a.toString()) ===
                                ((_b = item[matchBulkSelectedItemByField]) === null || _b === void 0 ? void 0 : _b.toString()));
                        }));
                });
                setBulkSelectedItems(uniqueItems);
            }, onBulkClearAllItems: () => {
                setBulkSelectedItems([]);
            }, onBulkSelectAllItems: async () => {
                await fetchAllBulkItems();
            }, matchBulkSelectedItemByField: matchBulkSelectedItemByField || "_id", bulkItemToString: (item) => {
                var _a, _b;
                const label = props.singularName || item.singularName || "";
                const name = ((_a = item["name"]) === null || _a === void 0 ? void 0 : _a.toString()) ||
                    "";
                if (name) {
                    return label ? `${label}: ${name}` : name;
                }
                const id = ((_b = item[matchBulkSelectedItemByField]) === null || _b === void 0 ? void 0 : _b.toString()) || "";
                return label ? `${label} ${id}` : id;
            }, filters: classicTableFilters, filterError: tableFilterError, isFilterLoading: isFilterFetchLoading, showFilterModal: showFilterModal, onFilterModalClose: () => {
                setShowFilterModal(false);
            }, onFilterModalOpen: () => {
                setShowFilterModal(true);
            }, onAdvancedFiltersToggle: props.onAdvancedFiltersToggle, onSortChanged: (sortBy, sortOrder) => {
                setSortBy(sortBy);
                setSortOrder(sortOrder);
                setTableView(null);
            }, singularLabel: props.singularName || model.singularName || "Item", pluralLabel: props.pluralName || model.pluralName || "Items", error: error, currentPageNumber: currentPageNumber, isLoading: getTableLoadingState(), enableDragAndDrop: props.enableDragAndDrop, dragDropIdField: "_id", dragDropIndexField: props.dragDropIndexField, totalItemsCount: totalItemsCount, hasMore: hasMore, data: data, id: props.id, columns: tableColumns, itemsOnPage: itemsOnPage, onDragDrop: async (id, newOrder) => {
                if (!props.dragDropIndexField) {
                    return;
                }
                setIsLoading(true);
                await props.callbacks.updateById({
                    id: new ObjectID(id),
                    data: {
                        [props.dragDropIndexField]: newOrder,
                    },
                });
                await fetchItems();
            }, disablePagination: props.disablePagination || false, onNavigateToPage: async (pageNumber, newItemsOnPage) => {
                setCurrentPageNumber(pageNumber);
                if (newItemsOnPage !== itemsOnPage) {
                    setTableView(null);
                }
                setItemsOnPage(newItemsOnPage);
            }, noItemsMessage: props.noItemsMessage || "", onRefreshClick: async () => {
                await fetchItems();
            }, actionButtons: actionButtonSchema }));
    };
    const getOrderedStatesList = () => {
        var _a, _b, _c;
        if (!props.orderedStatesListProps) {
            throw new BadDataException("props.orderedStatesListProps required when showAs === ShowAs.OrderedStatesList");
        }
        let getTitleElement = undefined;
        let getDescriptionElement = undefined;
        for (const column of props.columns) {
            const key = Object.keys(column.field)[0];
            if (key === props.orderedStatesListProps.titleField) {
                getTitleElement = column.getElement;
            }
            if (key === props.orderedStatesListProps.descriptionField) {
                getDescriptionElement = column.getElement;
            }
        }
        return (React.createElement(OrderedStatesList, { error: error, isLoading: isLoading, data: data, id: props.id, titleField: (_a = props.orderedStatesListProps) === null || _a === void 0 ? void 0 : _a.titleField, descriptionField: (_b = props.orderedStatesListProps) === null || _b === void 0 ? void 0 : _b.descriptionField, orderField: (_c = props.orderedStatesListProps) === null || _c === void 0 ? void 0 : _c.orderField, shouldAddItemInTheBeginning: props.orderedStatesListProps.shouldAddItemInTheBeginning, shouldAddItemInTheEnd: props.orderedStatesListProps.shouldAddItemInTheEnd, noItemsMessage: props.noItemsMessage || "", onRefreshClick: async () => {
                await fetchItems();
            }, onCreateNewItem: props.isCreateable
                ? (order) => {
                    setOrderedStatesListNewItemOrder(order);
                    setModalType(ModalType.Create);
                    setShowModal(true);
                }
                : undefined, singularLabel: props.singularName || model.singularName || "Item", actionButtons: actionButtonSchema, getTitleElement: getTitleElement, getDescriptionElement: getDescriptionElement }));
    };
    const getList = () => {
        return (React.createElement(List, { onFilterChanged: (filterData) => {
                onFilterChanged(filterData);
            }, onFilterRefreshClick: async () => {
                await getFilterDropdownItems();
            }, filters: classicTableFilters, filterError: tableFilterError, isFilterLoading: isFilterFetchLoading, showFilterModal: showFilterModal, onFilterModalClose: () => {
                setShowFilterModal(false);
            }, onFilterModalOpen: () => {
                setShowFilterModal(true);
            }, onAdvancedFiltersToggle: props.onAdvancedFiltersToggle, 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, newOrder) => {
                if (!props.dragDropIndexField) {
                    return;
                }
                setIsLoading(true);
                await props.callbacks.updateById({
                    id: new ObjectID(id),
                    data: {
                        [props.dragDropIndexField]: newOrder,
                    },
                });
                await fetchItems();
            }, dragDropIdField: "_id", dragDropIndexField: props.dragDropIndexField, isLoading: getTableLoadingState(), totalItemsCount: totalItemsCount, hasMore: hasMore, data: data, id: props.id, fields: fields, itemsOnPage: itemsOnPage, disablePagination: props.disablePagination || false, onNavigateToPage: async (pageNumber, itemsOnPage) => {
                setCurrentPageNumber(pageNumber);
                setItemsOnPage(itemsOnPage);
            }, noItemsMessage: props.noItemsMessage || "", onRefreshClick: async () => {
                await fetchItems();
            }, actionButtons: actionButtonSchema }));
    };
    const getCardTitle = (title) => {
        var _a;
        const renderedTitle = typeof title === "string"
            ? (_a = translateValue(title)) !== null && _a !== void 0 ? _a : title
            : title;
        const plan = ProjectUtil.getCurrentPlan();
        let showPlan = Boolean(BILLING_ENABLED &&
            plan &&
            new props.modelType().getReadBillingPlan() &&
            !SubscriptionPlan.isFeatureAccessibleOnCurrentPlan(new props.modelType().getReadBillingPlan(), plan, getAllEnvVars()));
        let planName = 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 (React.createElement("span", null,
            renderedTitle,
            showPlan && (React.createElement("span", { style: {
                    marginLeft: "5px",
                } },
                React.createElement(Pill, { text: `${planName} Plan`, color: Yellow })))));
    };
    const collapseSearch = () => {
        setSearchText("");
        setSelectedLabels([]);
        setIsSearchExpanded(false);
    };
    const parseLabelMention = (value) => {
        const atIndex = value.lastIndexOf("@");
        if (atIndex < 0) {
            return { hasMention: false, prefix: "", atIndex: -1 };
        }
        if (atIndex > 0) {
            const prev = value[atIndex - 1] || "";
            if (prev !== " " && prev !== "\t") {
                return { hasMention: false, prefix: "", atIndex: -1 };
            }
        }
        const after = value.substring(atIndex + 1);
        if (after.includes(" ") || after.includes("\t") || after.includes("\n")) {
            return { hasMention: false, prefix: "", atIndex: -1 };
        }
        return { hasMention: true, prefix: after, atIndex };
    };
    const addLabel = (label) => {
        setSelectedLabels((prev) => {
            if (prev.find((l) => {
                return l.id === label.id;
            })) {
                return prev;
            }
            return [...prev, label];
        });
        // Strip the `@<prefix>` token from the input.
        const mention = parseLabelMention(searchText);
        if (mention.hasMention) {
            const before = searchText.substring(0, mention.atIndex);
            setSearchText(before.replace(/\s+$/, ""));
        }
        setLabelDropdownIndex(0);
    };
    const removeLabel = (labelId) => {
        setSelectedLabels((prev) => {
            return prev.filter((l) => {
                return l.id !== labelId;
            });
        });
    };
    const getSearchControl = () => {
        if (!props.searchableFields || props.searchableFields.length === 0) {
            return React.createElement(React.Fragment, null);
        }
        const pluralLabel = (props.pluralName ||
            model.pluralName ||
            "items").toLowerCase();
        const hasLabelSupport = Boolean(labelFilterConfig);
        const defaultPlaceholder = hasLabelSupport
            ? `Search ${pluralLabel}… (try @ for labels)`
            : `Search ${pluralLabel} by name, description…`;
        const placeholder = props.searchPlaceholder || defaultPlaceholder;
        /*
         * Effective search = input minus the trailing @<prefix> mention. The pill
         * + result-count UI should reflect this so typing "@bug" doesn't claim
         * "0 matches" before the user has actually committed the label.
         */
        const stripTrailingMentionForUi = (v) => {
            const m = parseLabelMention(v);
            return m.hasMention ? v.substring(0, m.atIndex).trimEnd() : v;
        };
        const trimmedSearch = stripTrailingMentionForUi(searchText).trim();
        const trimmedActive = stripTrailingMentionForUi(debouncedSearchText).trim();
        const hasActiveSearch = trimmedActive.length > 0;
        const hasSelectedLabels = selectedLabels.length > 0;
        /*
         * "isSearching" covers both phases — typing-in-flight (before the
         * debounce fires) AND the actual API request that follows. The table
         * loader is suppressed during the latter, so this spinner is the only
         * loading indicator the user sees during a search-driven fetch.
         */
        const isTypingInFlight = trimmedSearch.length > 0 && trimmedSearch !== trimmedActive;
        const isSearching = isTypingInFlight || (isLoading && (hasActiveSearch || hasSelectedLabels));
        const showMatchPill = !isSearching && (hasActiveSearch || hasSelectedLabels);
        const expanded = isSearchExpanded || hasActiveSearch || hasSelectedLabels;
        const mention = parseLabelMention(searchText);
        const showLabelDropdown = hasLabelSupport && isSearchFocused && mention.hasMention;
        const lowerPrefix = mention.prefix.toLowerCase();
        const dropdownLabels = availableLabels
            .filter((l) => {
            return !selectedLabels.find((s) => {
                return s.id === l.id;
            });
        })
            .filter((l) => {
            return (lowerPrefix.length === 0 ||
                l.name.toLowerCase().startsWith(lowerPrefix));
        })
            .slice(0, 8);
        const borderClass = isSearchFocused
            ? "border-gray-400 ring-4 ring-gray-100 shadow-sm"
            : hasActiveSearch || hasSelectedLabels
                ? "border-gray-300 shadow-sm"
                : "border-gray-200 shadow-sm";
        const iconColorClass = isSearchFocused || hasActiveSearch || hasSelectedLabels
            ? "text-gray-700"
            : "text-gray-400";
        const selectDropdownItemAt = (idx) => {
            const item = dropdownLabels[idx];
            if (item) {
                addLabel(item);
            }
        };
        return (React.createElement("div", { className: "relative flex w-full items-center" },
            React.createElement("div", { className: "relative flex w-full flex-col gap-1" },
                React.createElement("div", { className: `flex w-full items-center gap-2 rounded-md border bg-white px-3 py-2 transition-all duration-200 ${borderClass}`, onClick: () => {
                        var _a;
                        (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
                    }, role: "presentation" },
                    React.createElement(Icon, { icon: IconProp.Search, className: `h-4 w-4 flex-none transition-colors duration-200 ${iconColorClass}` }),
                    React.createElement("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-1.5" },
                        selectedLabels.map((label) => {
                            return (React.createElement("span", { key: label.id, className: "inline-flex items-center gap-1 rounded-full bg-gray-50 py-0.5 pl-2 pr-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-200 transition-all hover:bg-gray-100", title: `Label: ${label.name}` },
                                React.createElement("span", { className: "h-2 w-2 flex-none rounded-full", style: { backgroundColor: label.color }, "aria-hidden": "true" }),
                                React.createElement("span", { className: "max-w-[8rem] truncate" }, label.name),
                                React.createElement("button", { type: "button", onMouseDown: (e) => {
                                        e.preventDefault();
                                    }, onClick: () => {
                                        removeLabel(label.id);
                                    }, title: "Remove label", "aria-label": `Remove ${label.name}`, className: "ml-0.5 flex-none rounded-full p-0.5 text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700" },
                                    React.createElement(Icon, { icon: IconProp.Close, className: "h-3 w-3" }))));
                        }),
                        React.createElement("input", { ref: searchInputRef, type: "text", value: searchText, onChange: (e) => {
                                setSearchText(e.target.value);
                                setLabelDropdownIndex(0);
                            }, onFocus: () => {
                                setIsSearchFocused(true);
                            }, onBlur: () => {
                                setIsSearchFocused(false);
                                /*
                                 * Collapse only when the user blurs with nothing active —
                                 * no text and no selected labels.
                                 */
                                if (searchText.trim().length === 0 &&
                                    selectedLabels.length === 0) {
                                    setIsSearchExpanded(false);
                                }
                            }, onKeyDown: (e) => {
                                var _a;
                                if (showLabelDropdown && dropdownLabels.length > 0) {
                                    if (e.key === "ArrowDown") {
                                        e.preventDefault();
                                        setLabelDropdownIndex((i) => {
                                            return Math.min(i + 1, dropdownLabels.length - 1);
                                        });
                                        return;
                                    }
                                    if (e.key === "ArrowUp") {
                                        e.preventDefault();
                                        setLabelDropdownIndex((i) => {
                                            return Math.max(i - 1, 0);
                                        });
                                        return;
                                    }
                                    if (e.key === "Enter" || e.key === "Tab") {
                                        e.preventDefault();
                                        selectDropdownItemAt(labelDropdownIndex);
                                        return;
                                    }
                                }
                                if (e.key === "Backspace" && searchText.length === 0) {
                                    // Pop last label when backspacing on empty input.
                                    if (selectedLabels.length > 0) {
                                        const last = selectedLabels[selectedLabels.length - 1];
                                        if (last) {
                                            removeLabel(last.id);
                                        }
                                    }
                                    return;
                                }
                                if (e.key === "Escape") {
                                    if (showLabelDropdown) {
                                        // Just cancel the @ mention parse — clear @ prefix.
                                        const m = parseLabelMention(searchText);
                                        if (m.hasMention) {
                                            setSearchText(searchText
                                                .substring(0, m.atIndex)
                                                .replace(/\s+$/, ""));
                                        }
                                        return;
                                    }
                                    if (searchText) {
                                        setSearchText("");
                                    }
                                    else if (selectedLabels.length > 0) {
                                        setSelectedLabels([]);
                                    }
                                    else {
                                        collapseSearch();
                                        (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.blur();
                                    }
                                }
                            }, placeholder: selectedLabels.length === 0 ? placeholder : "Refine search…", spellCheck: false, autoComplete: "off", tabIndex: expanded ? 0 : -1, className: "min-w-[6rem] flex-1 bg-transparent text-sm leading-5 text-gray-900 placeholder-gray-400 outline-none" })),
                    isSearching && (React.createElement("div", { className: "flex-none text-gray-400", title: "Searching\u2026" },
                        React.createElement(Icon, { icon: IconProp.Spinner, className: "h-4 w-4 animate-spin" }))),
                    showMatchPill && totalItemsCount >= 0 && hasMore === undefined && (React.createElement("span", { className: "flex-none whitespace-nowrap rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700", title: `${totalItemsCount} ${totalItemsCount === 1 ? "result" : "results"}` },
                        totalItemsCount,
                        " ",
                        totalItemsCount === 1 ? "match" : "matches")),
                    showMatchPill && hasMore !== undefined && data.length > 0 && (React.createElement("span", { className: "flex-none whitespace-nowrap rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-700", title: `${data.length}${hasMore ? "+" : ""} ${data.length === 1 ? "result" : "results"} on this page` },
                        data.length,
                        hasMore ? "+" : "",
                        " ",
                        data.length === 1 ? "match" : "matches")),
                    searchText.length > 0 || selectedLabels.length > 0 ? (React.createElement("button", { type: "button", onMouseDown: (e) => {
                            e.preventDefault();
                        }, onClick: () => {
                            collapseSearch();
                        }, title: "Clear search (Esc Esc)", "aria-label": "Clear search", className: "flex-none rounded-full p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600" },
                        React.createElement(Icon, { icon: IconProp.Close, className: "h-3.5 w-3.5" }))) : (React.createElement("kbd", { className: "hidden flex-none select-none items-center rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[10px] font-medium text-gray-500 sm:inline-flex", title: "Press / to focus search" }, "/"))),
                showLabelDropdown && (React.createElement("div", { className: "absolute left-0 right-0 top-full z-20 mt-1.5 origin-top overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg ring-1 ring-black/5 transition-all duration-150 animate-in fade-in slide-in-from-top-1", onMouseDown: (e) => {
                        // Prevent input blur on dropdown clicks.
                        e.preventDefault();
                    } },
                    React.createElement("div", { className: "flex items-center justify-between border-b border-gray-100 px-3 py-2" },
                        React.createElement("span", { className: "text-[11px] font-semibold uppercase tracking-wide text-gray-500" }, isLabelsLoading ? "Loading labels…" : "Filter by label"),
                        React.createElement("span", { className: "text-[10px] text-gray-400" },
                            React.createElement("kbd", { className: "font-mono" }, "\u2191"),
                            React.createElement("kbd", { className: "ml-0.5 font-mono" }, "\u2193"),
                            React.createElement("span", { className: "ml-1" }, "to navigate"),
                            React.createElement("span", { className: "mx-1.5" }, "\u00B7"),
                            React.createElement("kbd", { className: "font-mono" }, "\u21B5"),
                            React.createElement("span", { className: "ml-1" }, "to select"))),
                    React.createElement("div", { className: "max-h-64 overflow-y-auto py-1" },
                        !isLabelsLoading && dropdownLabels.length === 0 && (React.createElement("div", { className: "px-3 py-3 text-sm text-gray-500" }, availableLabels.length === 0
                            ? "No labels available for this resource."
                            : `No labels matching "${mention.prefix}"`)),
                        dropdownLabels.map((label, idx) => {
                            const isActive = idx === labelDropdownIndex;
                            return (React.createElement("button", { key: label.id, type: "button", onMouseEnter: () => {
                                    setLabelDropdownIndex(idx);
                                }, onMouseDown: (e) => {
                                    e.preventDefault();
                                }, onClick: () => {
                                    var _a;
                                    addLabel(label);
                                    (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
                                }, className: `flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm transition-colors ${isActive
                                    ? "bg-gray-100 text-gray-900"
                                    : "text-gray-700 hover:bg-gray-50"}` },
                                React.createElement("span", { className: "h-2.5 w-2.5 flex-none rounded-full ring-1 ring-inset ring-black/5", style: { backgroundColor: label.color }, "aria-hidden": "true" }),
                                React.createElement("span", { className: "flex-1 truncate" }, label.name),
                                isActive && (React.createElement("span", { className: "text-[10px] font-medium uppercase tracking-wide text-gray-500" }, "\u21B5"))));
                        })))))));
    };
    const splitButtonsForHeader = (buttons) => {
        let mainIndex = -1;
        for (let i = 0; i < buttons.length; i++) {
            const b = buttons[i];
            if (React.isValidElement(b)) {
                continue;
            }
            const c = b;
            if (c.buttonStyle === ButtonStyleType.NORMAL && c.icon === IconProp.Add) {
                mainIndex = i;
                break;
            }
        }
        if (mainIndex < 0) {
            for (let i = 0; i < buttons.length; i++) {
                const b = buttons[i];
                if (React.isValidElement(b)) {
                    continue;
                }
                const style = b
                    .buttonStyle;
                if (style === ButtonStyleType.NORMAL ||
                    style === ButtonStyleType.PRIMARY) {
                    mainIndex = i;
                    break;
                }
            }
        }
        if (mainIndex < 0) {
            return { main: null, rest: buttons };
        }
        const main = buttons[mainIndex];
        const rest = [
            ...buttons.slice(0, mainIndex),
            ...buttons.slice(mainIndex + 1),
        ];
        return { main, rest };
    };
    const renderMainButton = (b) => {
        return (React.createElement(Button, { key: "model-table-main-action", title: b.title, buttonStyle: b.buttonStyle, buttonSize: b.buttonSize, className: b.className, onClick: () => {
                var _a;
                (_a = b.onClick) === null || _a === void 0 ? void 0 : _a.call(b);
            }, disabled: b.disabled, icon: b.icon, shortcutKey: b.shortcutKey, dataTestId: "card-button", isLoading: b.isLoading }));
    };
    /*
     * Icon-only card buttons (Refresh, Filter, …) have empty titles, so derive
     * a label from the icon for the More menu.
     */
    // Fallback labels for icon-only buttons (Refresh, Filter, ...) in the More menu.
    const labelForIconButton = (icon) => {
        switch (icon) {
            case IconProp.Refresh:
                return "Refresh";
            case IconProp.Filter:
                return "Filter";
            case IconProp.Add:
                return "Add";
            case IconProp.Help:
                return "Help";
            case IconProp.Book:
                return "Documentation";
            case IconProp.Play:
                return "Watch Demo";
            case IconProp.Search:
                return "Search";
            default:
                return "Action";
        }
    };
    const renderMoreMenu = (items) => {
        if (items.length === 0) {
            return null;
        }
        const children = items.map((item, idx) => {
            if (React.isValidElement(item)) {
                return (React.createElement("div", { key: `more-${idx}`, className: "px-2 py-1" }, item));
            }
            const b = item;
            const label = b.title || labelForIconButton(b.icon);
            return (React.createElement(MoreMenuItem, { key: `more-${idx}`, text: label, icon: b.icon, onClick: () => {
                    var _a;
                    if (!b.disabled) {
                        (_a = b.onClick) === null || _a === void 0 ? void 0 : _a.call(b);
                    }
                }, className: b.disabled ? "opacity-40 pointer-events-none" : "" }));
        });
        return (React.createElement(MoreMenu, { key: "model-table-more-menu", menuIcon: IconProp.EllipsisHorizontal, text: "" }, children));
    };
    /*
     * Builds the right-hand side of the card header. All slots — search
     * trigger/bar, saved views, main button, more menu — stay mounted at all
     * times. State transitions are purely CSS so the inputs keep their focus,
     * the dropdown keeps its open state, and there's no mount/unmount flicker.
     *
     * Layout when collapsed:  [🔍 trigger] [Saved Views] [main] [⋯]
     * Layout when expanded:   [🔍 ━━━ wide search bar ━━━]
     * Saved views + main button + more menu fade and collapse-to-zero-width
     * when the search expands, freeing horizontal space for the bar.
     */
    const getHeaderButtonsWithSearch = () => {
        const hasSearch = Boolean(props.searchableFields && props.searchableFields.length > 0);
        /*
         * Saved views get their own first-class slot in the header — never
         * collapsed into the overflow ⋯ menu. Render fresh on every call so the
         * inner TableViewElement always sees the current query / sort / itemsOnPage.
         */
        const savedViewsElement = props.saveFilterProps
            ? getSaveFilterDropdown()
            : null;
        if (cardButtons.length === 0 && !hasSearch) {
            return savedViewsElement ? [savedViewsElement] : cardButtons;
        }
        if (!hasSearch) {
            // Without search, just split into [Saved Views] [main] [⋯]; no special wrapping.
            const { main, rest } = splitButtonsForHeader(cardButtons);
            const composed = [];
            if (savedViewsElement) {
                composed.push(savedViewsElement);
            }
            if (main) {
                composed.push(renderMainButton(main));
            }
            const moreMenu = renderMoreMenu(rest);
            if (moreMenu) {
                composed.push(moreMenu);
            }
            return composed;
        }
        const trimmedActive = debouncedSearchText.trim();
        const isExpanded = isSearchExpanded || trimmedActive.length > 0 || selectedLabels.length > 0;
        const { main, rest } = splitButtonsForHeader(cardButtons);
        const moreMenu = renderMoreMenu(rest);
        const wrapped = (React.createElement("div", { key: "model-table-header-actions", className: "flex items-center gap-3" },
            React.createElement("div", { className: `relative shrink-0 transition-[width] duration-300 ease-out ${isExpanded ? "w-[22rem] sm:w-[26rem] lg:w-[32rem]" : "w-44 sm:w-56"}` },
                React.createElement("button", { type: "button", onClick: () => {
                        setIsSearchExpanded(true);
                        requestAnimationFrame(() => {
                            var _a;
                            (_a = searchInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
                        });
                    }, title: "Search (/)", "aria-label": "Open search", tabIndex: isExpanded ? -1 : 0, className: `absolute inset-0 inline-flex items-center gap-2 rounded-md border bg-white px-3 text-sm shadow-sm transition-all duration-200 ease-out ${isExpanded
                        ? "pointer-events-none border-gray-200 opacity-0"
                        : "border-gray-200 opacity-100 hover:border-gray-300 hover:bg-gray-50"}` },
                    React.createElement(Icon, { icon: IconProp.Search, className: "h-4 w-4 flex-none text-gray-400" }),
                    React.createElement("span", { className: "flex-1 truncate text-left text-gray-400" }, "Search\u2026"),
                    React.createElement("kbd", { className: "hidden flex-none select-none items-center rounded border border-gray-200 bg-gray-50 px-1.5 py-0.5 font-mono text-[10px] font-medium text-gray-500 sm:inline-flex" }, "/")),
                React.createElement("div", { className: `transition-opacity duration-200 ease-out ${isExpanded
                        ? "opacity-100 delay-150"
                        : "pointer-events-none opacity-0"}` }, getSearchControl())),
            React.createElement("div", { className: `flex items-center transition-all duration-300 ease-out [&_button]:md:ml-0 ${isExpanded
                    ? "max-w-0 -ml-3 gap-0 opacity-0 pointer-events-none"
                    : "max-w-[600px] gap-3 opacity-100"}`, "aria-hidden": isExpanded },
                savedViewsElement,
                main && renderMainButton(main),
                moreMenu)));
        return [wrapped];
    };
    const getCardHeaderTitle = (originalTitle) => {
        return originalTitle;
    };
    const getCardComponent = () => {
        const headerButtons = getHeaderButtonsWithSearch();
        if (showAs === ShowAs.Table || showAs === ShowAs.List) {
            return (React.createElement("div", null,
                props.cardProps && (React.createElement(Card, Object.assign({}, props.cardProps, { buttons: headerButtons, bodyClassName: showAs === ShowAs.List
                        ? "-ml-6 -mr-6 bg-gray-50 border-top"
                        : "", title: getCardHeaderTitle(getCardTitle(props.cardProps.title || "")) }),
                    tableColumns.length === 0 && props.columns.length > 0 ? (React.createElement(ErrorMessage, { message: `You are not authorized to view this table. You need any one of these permissions: ${PermissionHelper.getPermissionTitles(model.getReadPermissions()).join(", ")}` })) : (React.createElement(React.Fragment, null)),
                    props.topContent || React.createElement(React.Fragment, null),
                    tableColumns.length > 0 && showAs === ShowAs.Table ? (getTable()) : (React.createElement(React.Fragment, null)),
                    tableColumns.length > 0 && showAs === ShowAs.List ? (getList()) : (React.createElement(React.Fragment, null)))),
                !props.cardProps &&
                    (showAs === ShowAs.Table || showAs === ShowAs.List) &&
                    props.searchableFields &&
                    props.searchableFields.length > 0 ? (React.createElement("div", { className: "mb-3 flex justify-end" }, getSearchControl())) : (React.createElement(React.Fragment, null)),
                !props.cardProps && props.topContent,
                !props.cardProps && showAs === ShowAs.Table ? getTable() : React.createElement(React.Fragment, null),
                !props.cardProps && showAs === ShowAs.List ? getList() : React.createElement(React.Fragment, null)));
        }
        return (React.createElement("div", null,
            props.cardProps && (React.createElement(Card, Object.assign({}, props.cardProps, { buttons: headerButtons, title: getCardTitle(props.cardProps.title || "") }), getOrderedStatesList())),
            !props.cardProps && getOrderedStatesList()));
    };
    return (React.createElement(React.Fragment, null,
        React.createElement("div", { className: "" }, getCardComponent()),
        showModel ? (props.callbacks.showCreateEditModal({
            onClose: () => {
                setShowModal(false);
            },
            modalType: modalType,
            onBeforeCreate: async (item, miscDataProps) => {
                var _a;
                if (showAs === ShowAs.OrderedStatesList &&
                    ((_a = props.orderedStatesListProps) === null || _a === void 0 ? void 0 : _a.orderField) &&
                    orderedStatesListNewItemOrder) {
                    item.setColumnValue(props.orderedStatesListProps.orderField, orderedStatesListNewItemOrder);
                }
                if (props.onBeforeCreate) {
                    item = await props.onBeforeCreate(item, miscDataProps);
                }
                return item;
            },
            onSuccess: async (item) => {
                setShowModal(false);
                setCurrentPageNumber(1);
                await fetchItems();
                if (props.onCreateSuccess) {
                    await props.onCreateSuccess(item);
                }
                return Promise.resolve();
            },
            modelIdToEdit: currentEditableItem
                ? new ObjectID(currentEditableItem["_id"])
                : undefined,
        })) : (React.createElement(React.Fragment, null)),
        showDeleteConfirmModal && (React.createElement(ConfirmModal, { title: `Delete ${props.singularName || model.singularName}`, description: `Are you sure you want to delete this ${(_b = (props.singularName ||
                model.singularName ||
                "item")) === null || _b === void 0 ? void 0 : _b.toLowerCase()}?`, onClose: () => {
                setShowDeleteConfirmModal(false);
            }, submitButtonText: "Delete", onSubmit: async () => {
                if (currentDeleteableItem && currentDeleteableItem["_id"]) {
                    await deleteItem(currentDeleteableItem);
                    setShowDeleteConfirmModal(false);
                }
            }, submitButtonType: ButtonStyleType.DANGER })),
        errorModalText && (React.createElement(ConfirmModal, { title: `Error`, description: `${errorModalText}`, submitButtonText: "Close", onSubmit: () => {
                setErrorModalText("");
            }, submitButtonType: ButtonStyleType.NORMAL })),
        showViewIdModal && (React.createElement(ConfirmModal, { title: `${props.singularName || model.singularName || ""} ID`, description: React.createElement("div", null,
                React.createElement("span", null,
                    "ID of this ",
                    props.singularName || model.singularName || "",
                    ":",
                    " ",
                    viewId),
                React.createElement("br", null),
                React.createElement("br", null),
                React.createElement("span", null,
                    "You can use this ID to interact with",
                    " ",
                    props.singularName || model.singularName || "",
                    " via the OneUptime API. Click the button below to go to API Reference.")), 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 })),
        showHelpModal && props.helpContent && (React.createElement(Modal, { title: props.helpContent.title, description: props.helpContent.description, onClose: () => {
                setShowHelpModal(false);
            }, modalWidth: ModalWidth.Large, submitButtonText: "Close", onSubmit: () => {
                setShowHelpModal(false);
            } },
            React.createElement("div", { className: "p-2" },
                React.createElement(MarkdownViewer, { text: props.helpContent.markdown }))))));
};
export default BaseModelTable;
//# sourceMappingURL=BaseModelTable.js.map