diff --git a/package.json b/package.json
index 3d122a7..17d12a6 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"@auth0/auth0-react": "1.10.2",
"@emotion/react": "11.7.1",
"@emotion/styled": "11.6.0",
- "@kingsrook/qqq-frontend-core": "1.0.57",
+ "@kingsrook/qqq-frontend-core": "1.0.61",
"@mui/icons-material": "5.4.1",
"@mui/material": "5.11.1",
"@mui/styles": "5.11.1",
diff --git a/src/qqq/components/forms/EntityForm.tsx b/src/qqq/components/forms/EntityForm.tsx
index b27a9f3..471735c 100644
--- a/src/qqq/components/forms/EntityForm.tsx
+++ b/src/qqq/components/forms/EntityForm.tsx
@@ -26,9 +26,8 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
import {QPossibleValue} from "@kingsrook/qqq-frontend-core/lib/model/QPossibleValue";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
-import {Alert} from "@mui/material";
+import {Alert, Box} from "@mui/material";
import Avatar from "@mui/material/Avatar";
-import Box from "@mui/material/Box";
import Card from "@mui/material/Card";
import Grid from "@mui/material/Grid";
import Icon from "@mui/material/Icon";
@@ -80,6 +79,7 @@ function EntityForm(props: Props): JSX.Element
const [nonT1Sections, setNonT1Sections] = useState([] as QTableSection[]);
const [alertContent, setAlertContent] = useState("");
+ const [warningContent, setWarningContent] = useState("");
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
const [formValues, setFormValues] = useState({} as { [key: string]: string });
@@ -424,7 +424,16 @@ function EntityForm(props: Props): JSX.Element
{
console.log("Caught:");
console.log(error);
- setAlertContent(error.message);
+
+ if(error.message.toLowerCase().startsWith("warning"))
+ {
+ const path = `${location.pathname.replace(/\/edit$/, "")}?updateSuccess=true&warning=${encodeURIComponent(error.message)}`;
+ navigate(path);
+ }
+ else
+ {
+ setAlertContent(error.message);
+ }
});
}
else
@@ -445,7 +454,15 @@ function EntityForm(props: Props): JSX.Element
})
.catch((error) =>
{
- setAlertContent(error.message);
+ if(error.message.toLowerCase().startsWith("warning"))
+ {
+ const path = `${location.pathname.replace(/create$/, record.values.get(tableMetaData.primaryKeyField))}?createSuccess=true&warning=${encodeURIComponent(error.message)}`;
+ navigate(path);
+ }
+ else
+ {
+ setAlertContent(error.message);
+ }
});
}
})();
@@ -485,6 +502,11 @@ function EntityForm(props: Props): JSX.Element
{alertContent}
) : ("")}
+ {warningContent ? (
+
+ {warningContent}
+
+ ) : ("")}
diff --git a/src/qqq/pages/records/query/RecordQuery.tsx b/src/qqq/pages/records/query/RecordQuery.tsx
index 78ff247..09d23cd 100644
--- a/src/qqq/pages/records/query/RecordQuery.tsx
+++ b/src/qqq/pages/records/query/RecordQuery.tsx
@@ -27,8 +27,7 @@ import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJo
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
-import {Alert, Collapse, TablePagination} from "@mui/material";
-import Box from "@mui/material/Box";
+import {Alert, Box, Collapse, TablePagination} from "@mui/material";
import Button from "@mui/material/Button";
import Card from "@mui/material/Card";
import Dialog from "@mui/material/Dialog";
diff --git a/src/qqq/pages/records/view/RecordView.tsx b/src/qqq/pages/records/view/RecordView.tsx
index 7652795..90c1b0c 100644
--- a/src/qqq/pages/records/view/RecordView.tsx
+++ b/src/qqq/pages/records/view/RecordView.tsx
@@ -26,9 +26,8 @@ import {QProcessMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import {QTableSection} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableSection";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
-import {Alert, Typography} from "@mui/material";
+import {Alert, Box, Typography} from "@mui/material";
import Avatar from "@mui/material/Avatar";
-import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Card from "@mui/material/Card";
import Dialog from "@mui/material/Dialog";
@@ -43,7 +42,7 @@ import ListItemIcon from "@mui/material/ListItemIcon";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import Modal from "@mui/material/Modal";
-import React, {useContext, useEffect, useReducer, useState} from "react";
+import React, {useContext, useEffect, useState} from "react";
import {useLocation, useNavigate, useParams, useSearchParams} from "react-router-dom";
import QContext from "QContext";
import AuditBody from "qqq/components/audits/AuditBody";
@@ -86,6 +85,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
const [sectionFieldElements, setSectionFieldElements] = useState(null as Map);
+ const [adornmentFieldsMap, setAdornmentFieldsMap] = useState(new Map);
const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false);
const [tableMetaData, setTableMetaData] = useState(null);
const [metaData, setMetaData] = useState(null as QInstance);
@@ -99,10 +99,11 @@ function RecordView({table, launchProcess}: Props): JSX.Element
const [actionsMenu, setActionsMenu] = useState(null);
const [notFoundMessage, setNotFoundMessage] = useState(null);
const [successMessage, setSuccessMessage] = useState(null as string);
+ const [warningMessage, setWarningMessage] = useState(null as string);
const [searchParams] = useSearchParams();
const {setPageHeader} = useContext(QContext);
const [activeModalProcess, setActiveModalProcess] = useState(null as QProcessMetaData);
- const [, forceUpdate] = useReducer((x) => x + 1, 0);
+ const [reloadCounter, setReloadCounter] = useState(0);
const [launchingProcess, setLaunchingProcess] = useState(launchProcess);
const [showEditChildForm, setShowEditChildForm] = useState(null as any);
@@ -368,8 +369,8 @@ function RecordView({table, launchProcess}: Props): JSX.Element
{
section.fieldNames.map((fieldName: string) => (
-
-
+
+
{tableMetaData.fields.get(fieldName).label}:
@@ -426,8 +427,10 @@ function RecordView({table, launchProcess}: Props): JSX.Element
{
setSuccessMessage(`${tableMetaData.label} successfully ${searchParams.get("createSuccess") ? "created" : "updated"}`);
}
-
- forceUpdate();
+ if (searchParams.get("warning"))
+ {
+ setWarningMessage(searchParams.get("warning"));
+ }
})();
}
@@ -455,6 +458,13 @@ function RecordView({table, launchProcess}: Props): JSX.Element
})();
};
+ function handleRevealIconClick(fieldName: string)
+ {
+ adornmentFieldsMap.set(fieldName, !adornmentFieldsMap.get(fieldName));
+ setAdornmentFieldsMap(adornmentFieldsMap);
+ setReloadCounter(reloadCounter + 1);
+ }
+
function processClicked(process: QProcessMetaData)
{
openModalProcess(process);
@@ -646,6 +656,16 @@ function RecordView({table, launchProcess}: Props): JSX.Element
: ("")
}
+ {
+ warningMessage ?
+
+ {
+ setWarningMessage(null);
+ }}>
+ {warningMessage}
+
+ : ("")
+ }
diff --git a/src/qqq/styles/qqq-override-styles.css b/src/qqq/styles/qqq-override-styles.css
index 13393ff..3f8024f 100644
--- a/src/qqq/styles/qqq-override-styles.css
+++ b/src/qqq/styles/qqq-override-styles.css
@@ -385,4 +385,10 @@ input[type="search"]::-webkit-search-results-decoration { display: none; }
.dashboardDropdownMenu #timeframe-form label
{
font-size: 0.875rem;
-}
\ No newline at end of file
+}
+
+.MuiGrid-root > .MuiBox-root > .material-icons-round,
+.MuiBox-root > .MuiBox-root > .material-icons-round
+{
+ font-size: 2rem !important;
+}
diff --git a/src/qqq/utils/qqq/ValueUtils.tsx b/src/qqq/utils/qqq/ValueUtils.tsx
index f7fa71d..6300ae9 100644
--- a/src/qqq/utils/qqq/ValueUtils.tsx
+++ b/src/qqq/utils/qqq/ValueUtils.tsx
@@ -25,10 +25,11 @@ import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QField
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
import "datejs"; // https://github.com/datejs/Datejs
-import {Box, Chip, Icon} from "@mui/material";
+import {Box, Chip, ClickAwayListener, Icon} from "@mui/material";
import Button from "@mui/material/Button";
+import Tooltip from "@mui/material/Tooltip";
import parse from "html-react-parser";
-import React, {Fragment, useState} from "react";
+import React, {Fragment, useReducer, useState} from "react";
import AceEditor from "react-ace";
import {Link} from "react-router-dom";
import Client from "qqq/utils/qqq/Client";
@@ -77,6 +78,7 @@ class ValueUtils
return ValueUtils.getValueForDisplay(field, rawValue, displayValue, usage);
}
+
/*******************************************************************************
** When you have a field and a value (either just a raw value, or a raw and
** display value), call this method to get a string Element to display.
@@ -130,6 +132,11 @@ class ValueUtils
}
}
+ if (field.hasAdornment(AdornmentType.REVEAL))
+ {
+ return ();
+ }
+
if (field.hasAdornment(AdornmentType.RENDER_HTML))
{
return (rawValue ? parse(rawValue) : "");
@@ -468,6 +475,68 @@ function CodeViewer({name, mode, code}: {name: string; mode: string; code: strin
>);
}
+////////////////////////////////////////////////////////////////////////////////////////////////
+// little private component here, for rendering an AceEditor with some buttons/controls/state //
+////////////////////////////////////////////////////////////////////////////////////////////////
+function RevealComponent({fieldName, value, usage}: {fieldName: string, value: string, usage: string;}): JSX.Element
+{
+ const [adornmentFieldsMap, setAdornmentFieldsMap] = useState(new Map);
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
+ const [tooltipOpen, setToolTipOpen] = React.useState(false);
+ const [displayValue, setDisplayValue] = React.useState(value.replace(/./g, "*"));
+
+ const handleTooltipClose = () =>
+ {
+ setToolTipOpen(false);
+ };
+
+ const handleTooltipOpen = () =>
+ {
+ setToolTipOpen(true);
+ };
+
+ const handleRevealIconClick = (fieldName: string) =>
+ {
+ const displayValue = (adornmentFieldsMap.get(fieldName)) ? value.replace(/./g, "*") : value;
+ setDisplayValue(displayValue);
+ adornmentFieldsMap.set(fieldName, !adornmentFieldsMap.get(fieldName));
+ setAdornmentFieldsMap(adornmentFieldsMap);
+ forceUpdate();
+ };
+
+ const copyToClipboard = (value: string) =>
+ {
+ navigator.clipboard.writeText(value);
+ setToolTipOpen(true);
+ };
+
+ return (
+ usage === "view" && adornmentFieldsMap.get(fieldName) === true ? (
+
+ handleRevealIconClick(fieldName)} sx={{cursor: "pointer", fontSize: "15px !important", position: "relative", top: "3px", marginRight: "5px"}}>visibility_on
+ {displayValue}
+
+
+ copyToClipboard(value)} sx={{cursor: "pointer", fontSize: "15px !important", position: "relative", top: "3px", marginLeft: "5px"}}>copy
+
+
+
+ ):(
+ handleRevealIconClick(fieldName)} sx={{cursor: "pointer", fontSize: "15px !important", position: "relative", top: "3px", marginRight: "5px"}}>visibility_off{displayValue}
+ )
+ );
+}
+
export default ValueUtils;