/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
import {Capability} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Capability";
import {QAppMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppMetaData";
import {QAppNodeType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppNodeType";
import {QAppTreeNode} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QAppTreeNode";
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle";
import Grid from "@mui/material/Grid";
import Icon from "@mui/material/Icon";
import Typography from "@mui/material/Typography";
import {makeStyles} from "@mui/styles";
import {Command} from "cmdk";
import React, {useContext, useEffect, useRef, useState} from "react";
import {useNavigate} from "react-router-dom";
import QContext from "QContext";
import HistoryUtils, {QHistoryEntry} from "qqq/utils/HistoryUtils";
interface Props
{
metaData?: QInstance;
}
const useStyles = makeStyles((theme: any) => ({
item: {
whiteSpace: "nowrap"
},
keyboardKey: {
border: "1px solid gray",
borderRadius: "5px",
width: "28px",
display: "inline-block",
textAlign: "center",
marginRight: "5px",
fontWeight: "bold",
background: "#f0f0f0"
}
}));
const CommandMenu = ({metaData}: Props) =>
{
const navigate = useNavigate();
const pathParts = location.pathname.replace(/\/+$/, "").split("/");
const {accentColor, tableMetaData, dotMenuOpen, setDotMenuOpen, keyboardHelpOpen, setKeyboardHelpOpen, setTableMetaData, tableProcesses} = useContext(QContext);
const classes = useStyles();
function evalueKeyPress(e: KeyboardEvent)
{
///////////////////////////////////////////////////////////////////////////
// if a dot pressed, not from a "text" element, then toggle command menu //
///////////////////////////////////////////////////////////////////////////
const type = (e.target as any).type;
if (type !== "text" && type !== "textarea" && type !== "input" && type !== "search" && type !== "number")
{
if (e.key === "." && !keyboardHelpOpen)
{
e.preventDefault();
setDotMenuOpen(true);
}
else if (e.key === "?" && !dotMenuOpen)
{
e.preventDefault();
setKeyboardHelpOpen(true);
}
}
}
////////////////////////////////////////////
// Toggle the menu when period is pressed //
////////////////////////////////////////////
useEffect(() =>
{
/////////////////////////////////////////////////////////////////
// if we are not in the right table, clear the table meta data //
/////////////////////////////////////////////////////////////////
if (metaData && tableMetaData && !location.pathname.startsWith(`${metaData.getTablePath(tableMetaData)}/`))
{
setTableMetaData(null);
}
const down = (e: KeyboardEvent) =>
{
evalueKeyPress(e);
}
document.addEventListener("keydown", down)
return () =>
{
document.removeEventListener("keydown", down)
}
}, [tableMetaData, dotMenuOpen, keyboardHelpOpen])
useEffect(() =>
{
setDotMenuOpen(false);
}, [location.pathname])
function goToItem(path: string)
{
navigate(path, {replace: true});
setDotMenuOpen(false);
}
function getIconName(iconName: string, defaultIconName: string)
{
return iconName ?? defaultIconName;
}
/*******************************************************************************
**
*******************************************************************************/
function getFullAppLabel(nodes: QAppTreeNode[] | undefined, name: string, depth: number, path: string): string | null
{
if (nodes === undefined)
{
return (null);
}
for (let i = 0; i < nodes.length; i++)
{
if (nodes[i].type === QAppNodeType.APP && nodes[i].name === name)
{
return (`${path}${nodes[i].label}`);
}
else if (nodes[i].type === QAppNodeType.APP)
{
const result = getFullAppLabel(nodes[i].children, name, depth + 1, `${path}${nodes[i].label} > `);
if (result !== null)
{
return (result);
}
}
}
return (null);
}
/*******************************************************************************
**
*******************************************************************************/
function ActionsSection()
{
let tableNames : string[]= [];
metaData.tables.forEach((value: QTableMetaData, key: string) =>
{
tableNames.push(value.name);
})
tableNames = tableNames.sort((a: string, b:string) =>
{
return (metaData.tables.get(a).label.localeCompare(metaData.tables.get(b).label));
})
const path = location.pathname;
return tableMetaData && !path.endsWith("/edit") && !path.endsWith("/create") && !path.endsWith("#audit") && ! path.endsWith("copy") &&
(
{
tableMetaData.capabilities.has(Capability.TABLE_INSERT) && tableMetaData.insertPermission &&
goToItem(`${pathParts.slice(0, -1).join("/")}/create`)} key={`${tableMetaData.label}-new`} value="New">addNew
}
{
tableMetaData.capabilities.has(Capability.TABLE_INSERT) && tableMetaData.insertPermission &&
goToItem(`${pathParts.join("/")}/copy`)} key={`${tableMetaData.label}-copy`} value="Copy">copyCopy
}
{
tableMetaData.capabilities.has(Capability.TABLE_UPDATE) && tableMetaData.editPermission &&
goToItem(`${pathParts.join("/")}/edit`)} key={`${tableMetaData.label}-edit`} value="Edit">editEdit
}
{
metaData && metaData.tables.has("audit") &&
goToItem(`${pathParts.join("/")}#audit`)} key={`${tableMetaData.label}-audit`} value="Audit">checklistAudit
}
{
tableProcesses && tableProcesses.length > 0 &&
(
tableProcesses.map((process) => (
goToItem(`${pathParts.join("/")}/${process.name}`)} key={`${process.name}`} value={`${process.label}`}>{getIconName(process.iconName, "play_arrow")}{process.label}
))
)
}
);
}
/*******************************************************************************
**
*******************************************************************************/
function TablesSection()
{
let tableNames : string[]= [];
metaData.tables.forEach((value: QTableMetaData, key: string) =>
{
tableNames.push(value.name);
})
tableNames = tableNames.sort((a: string, b:string) =>
{
return (metaData.tables.get(a).label.localeCompare(metaData.tables.get(b).label));
})
return(
{
tableNames.map((tableName: string, index: number) =>
!metaData.tables.get(tableName).isHidden && metaData.getTablePath(metaData.tables.get(tableName)) &&
(
goToItem(`${metaData.getTablePath(metaData.tables.get(tableName))}`)} key={`${tableName}-${index}`} value={metaData.tables.get(tableName).label}>{getIconName(metaData.tables.get(tableName).iconName, "table_rows")}{metaData.tables.get(tableName).label}
)
)
}
);
}
/*******************************************************************************
**
*******************************************************************************/
function AppsSection()
{
let appNames: string[] = [];
metaData.apps.forEach((value: QAppMetaData, key: string) =>
{
appNames.push(value.name);
})
appNames = appNames.sort((a: string, b:string) =>
{
return (getFullAppLabel(metaData.appTree, a, 1, "").localeCompare(getFullAppLabel(metaData.appTree, b, 1, "")));
})
return(
{
appNames.map((appName: string, index: number) =>
metaData.getAppPath(metaData.apps.get(appName)) &&
(
goToItem(`${metaData.getAppPath(metaData.apps.get(appName))}`)} key={`${appName}-${index}`} value={getFullAppLabel(metaData.appTree, appName, 1, "")}>{getIconName(metaData.apps.get(appName).iconName, "apps")}{getFullAppLabel(metaData.appTree, appName, 1, "")}
)
)
}
);
}
function RecentlyViewedSection()
{
const history = HistoryUtils.get();
const options = [] as any;
history.entries.reverse().forEach((entry, index) =>
options.push({label: `${entry.label} index`, id: index, key: index, path: entry.path, iconName: entry.iconName})
)
let appNames: string[] = [];
metaData.apps.forEach((value: QAppMetaData, key: string) =>
{
appNames.push(value.name);
})
appNames = appNames.sort((a: string, b:string) =>
{
return (metaData.apps.get(a).label.localeCompare(metaData.apps.get(b).label));
})
const entryMap = new Map();
return(
{
history.entries.reverse().map((entry: QHistoryEntry, index: number) =>
! entryMap.has(entry.label) && entryMap.set(entry.label, true) && (
goToItem(`${entry.path}`)} key={`${entry.label}-${index}`} value={entry.label}>{entry.iconName}{entry.label}
)
)
}
);
}
const containerElement = useRef(null)
function closeKeyboardHelp()
{
setKeyboardHelpOpen(false);
}
function closeDotMenu()
{
setDotMenuOpen(false);
}
return (
{
}
{
keyboardHelpOpen &&
}
)
}
export default CommandMenu;