mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-22 15:18:44 +00:00
Compare commits
6 Commits
snapshot-H
...
snapshot-f
Author | SHA1 | Date | |
---|---|---|---|
57098b5f05 | |||
de8594bfe1 | |||
3c8180cf51 | |||
2e48aa3eba | |||
feb1cc5c86 | |||
c2ad1c34be |
18466
package-lock.json
generated
18466
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -664,7 +664,13 @@ export default function App()
|
|||||||
const [dotMenuOpen, setDotMenuOpen] = useState(false);
|
const [dotMenuOpen, setDotMenuOpen] = useState(false);
|
||||||
const [keyboardHelpOpen, setKeyboardHelpOpen] = useState(false);
|
const [keyboardHelpOpen, setKeyboardHelpOpen] = useState(false);
|
||||||
const [helpHelpActive] = useState(queryParams.has("helpHelp"));
|
const [helpHelpActive] = useState(queryParams.has("helpHelp"));
|
||||||
const [userId] = useState(user.email);
|
const [userId, setUserId] = useState(user?.email);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setUserId(user?.email)
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
|
||||||
const [googleAnalyticsUtils] = useState(new GoogleAnalyticsUtils());
|
const [googleAnalyticsUtils] = useState(new GoogleAnalyticsUtils());
|
||||||
|
|
||||||
|
@ -25,8 +25,7 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT
|
|||||||
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
||||||
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||||
import {Alert, Button} from "@mui/material";
|
import {Alert, Box, Button} from "@mui/material";
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import Dialog from "@mui/material/Dialog";
|
import Dialog from "@mui/material/Dialog";
|
||||||
import DialogActions from "@mui/material/DialogActions";
|
import DialogActions from "@mui/material/DialogActions";
|
||||||
import DialogContent from "@mui/material/DialogContent";
|
import DialogContent from "@mui/material/DialogContent";
|
||||||
@ -60,7 +59,7 @@ interface Props
|
|||||||
view?: RecordQueryView;
|
view?: RecordQueryView;
|
||||||
viewAsJson?: string;
|
viewAsJson?: string;
|
||||||
viewOnChangeCallback?: (selectedSavedViewId: number) => void;
|
viewOnChangeCallback?: (selectedSavedViewId: number) => void;
|
||||||
loadingSavedView: boolean
|
loadingSavedView: boolean;
|
||||||
queryScreenUsage: QueryScreenUsage;
|
queryScreenUsage: QueryScreenUsage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +68,8 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const [savedViews, setSavedViews] = useState([] as QRecord[]);
|
const [savedViews, setSavedViews] = useState([] as QRecord[]);
|
||||||
|
const [yourSavedViews, setYourSavedViews] = useState([] as QRecord[]);
|
||||||
|
const [viewsSharedWithYou, setViewsSharedWithYou] = useState([] as QRecord[]);
|
||||||
const [savedViewsMenu, setSavedViewsMenu] = useState(null);
|
const [savedViewsMenu, setSavedViewsMenu] = useState(null);
|
||||||
const [savedViewsHaveLoaded, setSavedViewsHaveLoaded] = useState(false);
|
const [savedViewsHaveLoaded, setSavedViewsHaveLoaded] = useState(false);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
@ -91,7 +92,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
const CLEAR_OPTION = "New View";
|
const CLEAR_OPTION = "New View";
|
||||||
const NEW_REPORT_OPTION = "Create Report from Current View";
|
const NEW_REPORT_OPTION = "Create Report from Current View";
|
||||||
|
|
||||||
const {accentColor, accentColorLight} = useContext(QContext);
|
const {accentColor, accentColorLight, userId: currentUserId} = useContext(QContext);
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// this component is used by <RecordQuery> - but that component has different usages - //
|
// this component is used by <RecordQuery> - but that component has different usages - //
|
||||||
@ -114,13 +115,13 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
{
|
{
|
||||||
setSavedViewsHaveLoaded(true);
|
setSavedViewsHaveLoaded(true);
|
||||||
});
|
});
|
||||||
}, [location, tableMetaData])
|
}, [location, tableMetaData]);
|
||||||
|
|
||||||
|
|
||||||
const baseView = currentSavedView ? JSON.parse(currentSavedView.values.get("viewJson")) as RecordQueryView : tableDefaultView;
|
const baseView = currentSavedView ? JSON.parse(currentSavedView.values.get("viewJson")) as RecordQueryView : tableDefaultView;
|
||||||
const viewDiffs = SavedViewUtils.diffViews(tableMetaData, baseView, view);
|
const viewDiffs = SavedViewUtils.diffViews(tableMetaData, baseView, view);
|
||||||
let viewIsModified = false;
|
let viewIsModified = false;
|
||||||
if(viewDiffs.length > 0)
|
if (viewDiffs.length > 0)
|
||||||
{
|
{
|
||||||
viewIsModified = true;
|
viewIsModified = true;
|
||||||
}
|
}
|
||||||
@ -130,7 +131,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
async function loadSavedViews()
|
async function loadSavedViews()
|
||||||
{
|
{
|
||||||
if (! tableMetaData)
|
if (!tableMetaData)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -140,8 +141,24 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
|
|
||||||
let savedViews = await makeSavedViewRequest("querySavedView", formData);
|
let savedViews = await makeSavedViewRequest("querySavedView", formData);
|
||||||
setSavedViews(savedViews);
|
setSavedViews(savedViews);
|
||||||
}
|
|
||||||
|
|
||||||
|
const yourSavedViews: QRecord[] = [];
|
||||||
|
const viewsSharedWithYou: QRecord[] = [];
|
||||||
|
for (let i = 0; i < savedViews.length; i++)
|
||||||
|
{
|
||||||
|
const record = savedViews[i];
|
||||||
|
if (record.values.get("userId") == currentUserId)
|
||||||
|
{
|
||||||
|
yourSavedViews.push(record);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viewsSharedWithYou.push(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setYourSavedViews(yourSavedViews);
|
||||||
|
setViewsSharedWithYou(viewsSharedWithYou);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -152,14 +169,13 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
setSaveFilterPopupOpen(false);
|
setSaveFilterPopupOpen(false);
|
||||||
closeSavedViewsMenu();
|
closeSavedViewsMenu();
|
||||||
viewOnChangeCallback(record.values.get("id"));
|
viewOnChangeCallback(record.values.get("id"));
|
||||||
if(isQueryScreen)
|
if (isQueryScreen)
|
||||||
{
|
{
|
||||||
navigate(`${metaData.getTablePathByName(tableMetaData.name)}/savedView/${record.values.get("id")}`);
|
navigate(`${metaData.getTablePathByName(tableMetaData.name)}/savedView/${record.values.get("id")}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** fired when a save option is selected from the save... button/dropdown combo
|
** fired when a save option is selected from the save... button/dropdown combo
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -171,12 +187,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
setSaveFilterPopupOpen(true);
|
setSaveFilterPopupOpen(true);
|
||||||
setIsSaveFilterAs(false);
|
setIsSaveFilterAs(false);
|
||||||
setIsRenameFilter(false);
|
setIsRenameFilter(false);
|
||||||
setIsDeleteFilter(false)
|
setIsDeleteFilter(false);
|
||||||
|
|
||||||
switch(optionName)
|
switch (optionName)
|
||||||
{
|
{
|
||||||
case SAVE_OPTION:
|
case SAVE_OPTION:
|
||||||
if(currentSavedView == null)
|
if (currentSavedView == null)
|
||||||
{
|
{
|
||||||
setSavedViewNameInputValue("");
|
setSavedViewNameInputValue("");
|
||||||
}
|
}
|
||||||
@ -186,28 +202,28 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
setIsSaveFilterAs(true);
|
setIsSaveFilterAs(true);
|
||||||
break;
|
break;
|
||||||
case CLEAR_OPTION:
|
case CLEAR_OPTION:
|
||||||
setSaveFilterPopupOpen(false)
|
setSaveFilterPopupOpen(false);
|
||||||
viewOnChangeCallback(null);
|
viewOnChangeCallback(null);
|
||||||
if(isQueryScreen)
|
if (isQueryScreen)
|
||||||
{
|
{
|
||||||
navigate(metaData.getTablePathByName(tableMetaData.name));
|
navigate(metaData.getTablePathByName(tableMetaData.name));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RENAME_OPTION:
|
case RENAME_OPTION:
|
||||||
if(currentSavedView != null)
|
if (currentSavedView != null)
|
||||||
{
|
{
|
||||||
setSavedViewNameInputValue(currentSavedView.values.get("label"));
|
setSavedViewNameInputValue(currentSavedView.values.get("label"));
|
||||||
}
|
}
|
||||||
setIsRenameFilter(true);
|
setIsRenameFilter(true);
|
||||||
break;
|
break;
|
||||||
case DELETE_OPTION:
|
case DELETE_OPTION:
|
||||||
setIsDeleteFilter(true)
|
setIsDeleteFilter(true);
|
||||||
break;
|
break;
|
||||||
case NEW_REPORT_OPTION:
|
case NEW_REPORT_OPTION:
|
||||||
createNewReport();
|
createNewReport();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -215,11 +231,11 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
function createNewReport()
|
function createNewReport()
|
||||||
{
|
{
|
||||||
const defaultValues: {[key: string]: any} = {};
|
const defaultValues: { [key: string]: any } = {};
|
||||||
defaultValues.tableName = tableMetaData.name;
|
defaultValues.tableName = tableMetaData.name;
|
||||||
|
|
||||||
let filterForBackend = JSON.parse(JSON.stringify(view.queryFilter));
|
let filterForBackend = JSON.parse(JSON.stringify(view.queryFilter));
|
||||||
filterForBackend = FilterUtils.prepQueryFilterForBackend(tableMetaData, filterForBackend);
|
filterForBackend = FilterUtils.prepQueryFilterForBackend(tableMetaData, filterForBackend);
|
||||||
|
|
||||||
defaultValues.queryFilterJson = JSON.stringify(filterForBackend);
|
defaultValues.queryFilterJson = JSON.stringify(filterForBackend);
|
||||||
defaultValues.columnsJson = JSON.stringify(view.queryColumns);
|
defaultValues.columnsJson = JSON.stringify(view.queryColumns);
|
||||||
@ -227,7 +243,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** fired when save or delete button saved on confirmation dialogs
|
** fired when save or delete button saved on confirmation dialogs
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -247,7 +262,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
setSaveFilterPopupOpen(false);
|
setSaveFilterPopupOpen(false);
|
||||||
setSaveOptionsOpen(false);
|
setSaveOptionsOpen(false);
|
||||||
|
|
||||||
await(async() =>
|
await (async () =>
|
||||||
{
|
{
|
||||||
handleDropdownOptionClick(CLEAR_OPTION);
|
handleDropdownOptionClick(CLEAR_OPTION);
|
||||||
})();
|
})();
|
||||||
@ -267,14 +282,14 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// strip away incomplete filters too, just for cleaner saved view filters //
|
// strip away incomplete filters too, just for cleaner saved view filters //
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
FilterUtils.stripAwayIncompleteCriteria(viewObject.queryFilter)
|
FilterUtils.stripAwayIncompleteCriteria(viewObject.queryFilter);
|
||||||
|
|
||||||
formData.append("viewJson", JSON.stringify(viewObject));
|
formData.append("viewJson", JSON.stringify(viewObject));
|
||||||
|
|
||||||
if (isSaveFilterAs || isRenameFilter || currentSavedView == null)
|
if (isSaveFilterAs || isRenameFilter || currentSavedView == null)
|
||||||
{
|
{
|
||||||
formData.append("label", savedViewNameInputValue);
|
formData.append("label", savedViewNameInputValue);
|
||||||
if(currentSavedView != null && isRenameFilter)
|
if (currentSavedView != null && isRenameFilter)
|
||||||
{
|
{
|
||||||
formData.append("id", currentSavedView.values.get("id"));
|
formData.append("id", currentSavedView.values.get("id"));
|
||||||
}
|
}
|
||||||
@ -285,7 +300,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
formData.append("label", currentSavedView?.values.get("label"));
|
formData.append("label", currentSavedView?.values.get("label"));
|
||||||
}
|
}
|
||||||
const recordList = await makeSavedViewRequest("storeSavedView", formData);
|
const recordList = await makeSavedViewRequest("storeSavedView", formData);
|
||||||
await(async() =>
|
await (async () =>
|
||||||
{
|
{
|
||||||
if (recordList && recordList.length > 0)
|
if (recordList && recordList.length > 0)
|
||||||
{
|
{
|
||||||
@ -302,11 +317,11 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
catch (e: any)
|
catch (e: any)
|
||||||
{
|
{
|
||||||
let message = JSON.stringify(e);
|
let message = JSON.stringify(e);
|
||||||
if(typeof e == "string")
|
if (typeof e == "string")
|
||||||
{
|
{
|
||||||
message = e;
|
message = e;
|
||||||
}
|
}
|
||||||
else if(typeof e == "object" && e.message)
|
else if (typeof e == "object" && e.message)
|
||||||
{
|
{
|
||||||
message = e.message;
|
message = e.message;
|
||||||
}
|
}
|
||||||
@ -321,7 +336,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** hides/shows the save options
|
** hides/shows the save options
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -331,7 +345,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** closes save options menu (on clickaway)
|
** closes save options menu (on clickaway)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -346,7 +359,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** stores the current dialog input text to state
|
** stores the current dialog input text to state
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -356,7 +368,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** closes current dialog
|
** closes current dialog
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -366,7 +377,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** make a request to the backend for various savedView processes
|
** make a request to the backend for various savedView processes
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -375,7 +385,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
/////////////////////////
|
/////////////////////////
|
||||||
// fetch saved filters //
|
// fetch saved filters //
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
let savedViews = [] as QRecord[]
|
let savedViews = [] as QRecord[];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
@ -386,12 +396,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
if (processResult instanceof QJobError)
|
if (processResult instanceof QJobError)
|
||||||
{
|
{
|
||||||
const jobError = processResult as QJobError;
|
const jobError = processResult as QJobError;
|
||||||
throw(jobError.error);
|
throw (jobError.error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const result = processResult as QJobComplete;
|
const result = processResult as QJobComplete;
|
||||||
if(result.values.savedViewList)
|
if (result.values.savedViewList)
|
||||||
{
|
{
|
||||||
for (let i = 0; i < result.values.savedViewList.length; i++)
|
for (let i = 0; i < result.values.savedViewList.length; i++)
|
||||||
{
|
{
|
||||||
@ -403,7 +413,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
}
|
}
|
||||||
catch (e)
|
catch (e)
|
||||||
{
|
{
|
||||||
throw(e);
|
throw (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (savedViews);
|
return (savedViews);
|
||||||
@ -416,17 +426,27 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
|
|
||||||
const tooltipMaxWidth = (maxWidth: string) =>
|
const tooltipMaxWidth = (maxWidth: string) =>
|
||||||
{
|
{
|
||||||
return ({slotProps: {
|
return ({
|
||||||
tooltip: {
|
slotProps: {
|
||||||
sx: {
|
tooltip: {
|
||||||
maxWidth: maxWidth
|
sx: {
|
||||||
|
maxWidth: maxWidth
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const menuTooltipAttribs = {...tooltipMaxWidth("250px"), placement: "left", enterDelay: 1000} as TooltipProps;
|
const menuTooltipAttribs = {...tooltipMaxWidth("250px"), placement: "left", enterDelay: 1000} as TooltipProps;
|
||||||
|
|
||||||
|
let disabledBecauseNotOwner = false;
|
||||||
|
let notOwnerTooltipText = null;
|
||||||
|
if (currentSavedView && currentSavedView.values.get("userId") != currentUserId)
|
||||||
|
{
|
||||||
|
disabledBecauseNotOwner = true;
|
||||||
|
notOwnerTooltipText = "You may not save changes to this view, because you are not its owner.";
|
||||||
|
}
|
||||||
|
|
||||||
const renderSavedViewsMenu = tableMetaData && (
|
const renderSavedViewsMenu = tableMetaData && (
|
||||||
<Menu
|
<Menu
|
||||||
anchorEl={savedViewsMenu}
|
anchorEl={savedViewsMenu}
|
||||||
@ -443,75 +463,101 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen && hasStorePermission &&
|
isQueryScreen && hasStorePermission &&
|
||||||
<Tooltip {...menuTooltipAttribs} title={<>Save your current filters, columns and settings, for quick re-use at a later time.<br /><br />You will be prompted to enter a name if you choose this option.</>}>
|
<Tooltip {...menuTooltipAttribs} title={notOwnerTooltipText ?? <>Save your current filters, columns and settings, for quick re-use at a later time.<br /><br />You will be prompted to enter a name if you choose this option.</>}>
|
||||||
<MenuItem onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>
|
<span>
|
||||||
<ListItemIcon><Icon>save</Icon></ListItemIcon>
|
<MenuItem disabled={disabledBecauseNotOwner} onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>
|
||||||
{currentSavedView ? "Save..." : "Save As..."}
|
<ListItemIcon><Icon>save</Icon></ListItemIcon>
|
||||||
</MenuItem>
|
{currentSavedView ? "Save..." : "Save As..."}
|
||||||
|
</MenuItem>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen && hasStorePermission && currentSavedView != null &&
|
isQueryScreen && hasStorePermission && currentSavedView != null &&
|
||||||
<Tooltip {...menuTooltipAttribs} title="Change the name for this saved view.">
|
<Tooltip {...menuTooltipAttribs} title={notOwnerTooltipText ?? "Change the name for this saved view."}>
|
||||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(RENAME_OPTION)}>
|
<span>
|
||||||
<ListItemIcon><Icon>edit</Icon></ListItemIcon>
|
<MenuItem disabled={currentSavedView === null || disabledBecauseNotOwner} onClick={() => handleDropdownOptionClick(RENAME_OPTION)}>
|
||||||
Rename...
|
<ListItemIcon><Icon>edit</Icon></ListItemIcon>
|
||||||
</MenuItem>
|
Rename...
|
||||||
|
</MenuItem>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen && hasStorePermission && currentSavedView != null &&
|
isQueryScreen && hasStorePermission && currentSavedView != null &&
|
||||||
<Tooltip {...menuTooltipAttribs} title="Save a new copy this view, with a different name, separate from the original.">
|
<Tooltip {...menuTooltipAttribs} title="Save a new copy this view, with a different name, separate from the original.">
|
||||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DUPLICATE_OPTION)}>
|
<span>
|
||||||
<ListItemIcon><Icon>content_copy</Icon></ListItemIcon>
|
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DUPLICATE_OPTION)}>
|
||||||
Save As...
|
<ListItemIcon><Icon>content_copy</Icon></ListItemIcon>
|
||||||
</MenuItem>
|
Save As...
|
||||||
|
</MenuItem>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen && hasDeletePermission && currentSavedView != null &&
|
isQueryScreen && hasDeletePermission && currentSavedView != null &&
|
||||||
<Tooltip {...menuTooltipAttribs} title="Delete this saved view.">
|
<Tooltip {...menuTooltipAttribs} title={notOwnerTooltipText ?? "Delete this saved view."}>
|
||||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}>
|
<span>
|
||||||
<ListItemIcon><Icon>delete</Icon></ListItemIcon>
|
<MenuItem disabled={currentSavedView === null || disabledBecauseNotOwner} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}>
|
||||||
Delete...
|
<ListItemIcon><Icon>delete</Icon></ListItemIcon>
|
||||||
</MenuItem>
|
Delete...
|
||||||
|
</MenuItem>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen &&
|
isQueryScreen &&
|
||||||
<Tooltip {...menuTooltipAttribs} title="Create a new view of this table, resetting the filters and columns to their defaults.">
|
<Tooltip {...menuTooltipAttribs} title="Create a new view of this table, resetting the filters and columns to their defaults.">
|
||||||
<MenuItem onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>
|
<span>
|
||||||
<ListItemIcon><Icon>monitor</Icon></ListItemIcon>
|
<MenuItem onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>
|
||||||
New View
|
<ListItemIcon><Icon>monitor</Icon></ListItemIcon>
|
||||||
</MenuItem>
|
New View
|
||||||
|
</MenuItem>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen && hasSavedReportsPermission &&
|
isQueryScreen && hasSavedReportsPermission &&
|
||||||
<Tooltip {...menuTooltipAttribs} title="Create a new Saved Report using your current view of this table as a starting point.">
|
<Tooltip {...menuTooltipAttribs} title="Create a new Saved Report using your current view of this table as a starting point.">
|
||||||
<MenuItem onClick={() => handleDropdownOptionClick(NEW_REPORT_OPTION)}>
|
<span>
|
||||||
<ListItemIcon><Icon>article</Icon></ListItemIcon>
|
<MenuItem onClick={() => handleDropdownOptionClick(NEW_REPORT_OPTION)}>
|
||||||
Create Report from Current View
|
<ListItemIcon><Icon>article</Icon></ListItemIcon>
|
||||||
</MenuItem>
|
Create Report from Current View
|
||||||
|
</MenuItem>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
isQueryScreen && <Divider/>
|
isQueryScreen && <Divider />
|
||||||
}
|
}
|
||||||
<MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem>
|
<MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem>
|
||||||
{
|
{
|
||||||
savedViews && savedViews.length > 0 ? (
|
yourSavedViews && yourSavedViews.length > 0 ? (
|
||||||
savedViews.map((record: QRecord, index: number) =>
|
yourSavedViews.map((record: QRecord, index: number) =>
|
||||||
<MenuItem sx={{paddingLeft: "50px"}} key={`savedFiler-${index}`} onClick={() => handleSavedViewRecordOnClick(record)}>
|
<MenuItem sx={{paddingLeft: "50px"}} key={`savedFiler-${index}`} onClick={() => handleSavedViewRecordOnClick(record)}>
|
||||||
{record.values.get("label")}
|
{record.values.get("label")}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
)
|
||||||
): (
|
) : (
|
||||||
<MenuItem disabled sx={{opacity: "1 !important"}}>
|
<MenuItem disabled sx={{opacity: "1 !important"}}>
|
||||||
<i>You do not have any saved views for this table.</i>
|
<i>You do not have any saved views for this table.</i>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
<MenuItem disabled style={{"opacity": "initial"}}><b>Views Shared with you</b></MenuItem>
|
||||||
|
{
|
||||||
|
viewsSharedWithYou && viewsSharedWithYou.length > 0 ? (
|
||||||
|
viewsSharedWithYou.map((record: QRecord, index: number) =>
|
||||||
|
<MenuItem sx={{paddingLeft: "50px"}} key={`savedFiler-${index}`} onClick={() => handleSavedViewRecordOnClick(record)}>
|
||||||
|
{record.values.get("label")}
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<MenuItem disabled sx={{opacity: "1 !important"}}>
|
||||||
|
<i>You do not have any views shared with you for this table.</i>
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
|
}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -520,7 +566,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
let buttonBorder = colors.grayLines.main;
|
let buttonBorder = colors.grayLines.main;
|
||||||
let buttonColor = colors.gray.main;
|
let buttonColor = colors.gray.main;
|
||||||
|
|
||||||
if(currentSavedView)
|
if (currentSavedView)
|
||||||
{
|
{
|
||||||
if (viewIsModified)
|
if (viewIsModified)
|
||||||
{
|
{
|
||||||
@ -548,23 +594,23 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
color: buttonColor,
|
color: buttonColor,
|
||||||
backgroundColor: buttonBackground,
|
backgroundColor: buttonBackground,
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
function isSaveButtonDisabled(): boolean
|
function isSaveButtonDisabled(): boolean
|
||||||
{
|
{
|
||||||
if(isSubmitting)
|
if (isSubmitting)
|
||||||
{
|
{
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
const haveInputText = (savedViewNameInputValue != null && savedViewNameInputValue.trim() != "")
|
const haveInputText = (savedViewNameInputValue != null && savedViewNameInputValue.trim() != "");
|
||||||
|
|
||||||
if(isSaveFilterAs || isRenameFilter || currentSavedView == null)
|
if (isSaveFilterAs || isRenameFilter || currentSavedView == null)
|
||||||
{
|
{
|
||||||
if(!haveInputText)
|
if (!haveInputText)
|
||||||
{
|
{
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
@ -593,7 +639,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
fontSize: "0.875rem",
|
fontSize: "0.875rem",
|
||||||
p: "0.5rem",
|
p: "0.5rem",
|
||||||
... buttonStyles
|
...buttonStyles
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon sx={{mr: "0.5rem"}}>save</Icon>
|
<Icon sx={{mr: "0.5rem"}}>save</Icon>
|
||||||
@ -624,7 +670,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>Reset All Changes</Button>
|
<Button disableRipple={true} sx={{color: colors.gray.main, ...linkButtonStyle}} onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>Reset All Changes</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -635,16 +681,20 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
{
|
{
|
||||||
viewDiffs.map((s: string, i: number) => <li key={i}>{s}</li>)
|
viewDiffs.map((s: string, i: number) => <li key={i}>{s}</li>)
|
||||||
}
|
}
|
||||||
</ul></>}>
|
</ul>
|
||||||
|
{
|
||||||
|
notOwnerTooltipText && <i>{notOwnerTooltipText}</i>
|
||||||
|
}
|
||||||
|
</>}>
|
||||||
<Box display="inline" sx={{...linkButtonStyle, p: 0, cursor: "default", position: "relative", top: "-1px"}}>{viewDiffs.length} Unsaved Change{viewDiffs.length == 1 ? "" : "s"}</Box>
|
<Box display="inline" sx={{...linkButtonStyle, p: 0, cursor: "default", position: "relative", top: "-1px"}}>{viewDiffs.length} Unsaved Change{viewDiffs.length == 1 ? "" : "s"}</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Button disableRipple={true} sx={linkButtonStyle} onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>Save…</Button>
|
{disabledBecauseNotOwner ? <> </> : <Button disableRipple={true} sx={linkButtonStyle} onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>Save…</Button>}
|
||||||
|
|
||||||
{/* vertical rule */}
|
{/* vertical rule */}
|
||||||
<Box display="inline-block" borderLeft={`1px solid ${colors.grayLines.main}`} height="1rem" width="1px" position="relative" />
|
<Box display="inline-block" borderLeft={`1px solid ${colors.grayLines.main}`} height="1rem" width="1px" position="relative" />
|
||||||
|
|
||||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleSavedViewRecordOnClick(currentSavedView)}>Reset All Changes</Button>
|
<Button disableRipple={true} sx={{color: colors.gray.main, ...linkButtonStyle}} onClick={() => handleSavedViewRecordOnClick(currentSavedView)}>Reset All Changes</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -663,16 +713,17 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
{
|
{
|
||||||
viewDiffs.map((s: string, i: number) => <li key={i}>{s}</li>)
|
viewDiffs.map((s: string, i: number) => <li key={i}>{s}</li>)
|
||||||
}
|
}
|
||||||
</ul></>}>
|
</ul>
|
||||||
|
</>}>
|
||||||
<Box display="inline" ml="0.25rem" mr="0.25rem" sx={{...linkButtonStyle, p: 0, cursor: "default", position: "relative", top: "-1px"}}>with {viewDiffs.length} Change{viewDiffs.length == 1 ? "" : "s"}</Box>
|
<Box display="inline" ml="0.25rem" mr="0.25rem" sx={{...linkButtonStyle, p: 0, cursor: "default", position: "relative", top: "-1px"}}>with {viewDiffs.length} Change{viewDiffs.length == 1 ? "" : "s"}</Box>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleSavedViewRecordOnClick(currentSavedView)}>Reset Changes</Button>
|
<Button disableRipple={true} sx={{color: colors.gray.main, ...linkButtonStyle}} onClick={() => handleSavedViewRecordOnClick(currentSavedView)}>Reset Changes</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
{/* vertical rule */}
|
{/* vertical rule */}
|
||||||
<Box display="inline-block" ml="0.25rem" borderLeft={`1px solid ${colors.grayLines.main}`} height="1rem" width="1px" position="relative" />
|
<Box display="inline-block" ml="0.25rem" borderLeft={`1px solid ${colors.grayLines.main}`} height="1rem" width="1px" position="relative" />
|
||||||
<Button disableRipple={true} sx={{color: colors.gray.main, ... linkButtonStyle}} onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>Reset to New View</Button>
|
<Button disableRipple={true} sx={{color: colors.gray.main, ...linkButtonStyle}} onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>Reset to New View</Button>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
@ -702,15 +753,15 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
) : (
|
) : (
|
||||||
isSaveFilterAs ? (
|
isSaveFilterAs ? (
|
||||||
<DialogTitle id="alert-dialog-title">Save View As</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Save View As</DialogTitle>
|
||||||
):(
|
) : (
|
||||||
isRenameFilter ? (
|
isRenameFilter ? (
|
||||||
<DialogTitle id="alert-dialog-title">Rename View</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Rename View</DialogTitle>
|
||||||
):(
|
) : (
|
||||||
<DialogTitle id="alert-dialog-title">Update Existing View</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Update Existing View</DialogTitle>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
):(
|
) : (
|
||||||
<DialogTitle id="alert-dialog-title">Save New View</DialogTitle>
|
<DialogTitle id="alert-dialog-title">Save New View</DialogTitle>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -721,12 +772,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
</Box>
|
</Box>
|
||||||
) : ("")}
|
) : ("")}
|
||||||
{
|
{
|
||||||
(! currentSavedView || isSaveFilterAs || isRenameFilter) && ! isDeleteFilter ? (
|
(!currentSavedView || isSaveFilterAs || isRenameFilter) && !isDeleteFilter ? (
|
||||||
<Box>
|
<Box>
|
||||||
{
|
{
|
||||||
isSaveFilterAs ? (
|
isSaveFilterAs ? (
|
||||||
<Box mb={3}>Enter a name for this new saved view.</Box>
|
<Box mb={3}>Enter a name for this new saved view.</Box>
|
||||||
):(
|
) : (
|
||||||
<Box mb={3}>Enter a new name for this saved view.</Box>
|
<Box mb={3}>Enter a new name for this saved view.</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -744,10 +795,10 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
):(
|
) : (
|
||||||
isDeleteFilter ? (
|
isDeleteFilter ? (
|
||||||
<Box>Are you sure you want to delete the view {`'${currentSavedView?.values.get("label")}'`}?</Box>
|
<Box>Are you sure you want to delete the view {`'${currentSavedView?.values.get("label")}'`}?</Box>
|
||||||
):(
|
) : (
|
||||||
<Box>Are you sure you want to update the view {`'${currentSavedView?.values.get("label")}'`}?</Box>
|
<Box>Are you sure you want to update the view {`'${currentSavedView?.values.get("label")}'`}?</Box>
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -759,7 +810,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
|||||||
isDeleteFilter ?
|
isDeleteFilter ?
|
||||||
<QDeleteButton onClickHandler={handleFilterDialogButtonOnClick} disabled={isSubmitting} />
|
<QDeleteButton onClickHandler={handleFilterDialogButtonOnClick} disabled={isSubmitting} />
|
||||||
:
|
:
|
||||||
<QSaveButton label="Save" onClickHandler={handleFilterDialogButtonOnClick} disabled={isSaveButtonDisabled()}/>
|
<QSaveButton label="Save" onClickHandler={handleFilterDialogButtonOnClick} disabled={isSaveButtonDisabled()} />
|
||||||
}
|
}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
@ -37,6 +37,7 @@ import Typography from "@mui/material/Typography";
|
|||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
import colors from "qqq/assets/theme/base/colors";
|
import colors from "qqq/assets/theme/base/colors";
|
||||||
import {QCancelButton} from "qqq/components/buttons/DefaultButtons";
|
import {QCancelButton} from "qqq/components/buttons/DefaultButtons";
|
||||||
|
import DynamicSelect, {getAutocompleteOutlinedStyle} from "qqq/components/forms/DynamicSelect";
|
||||||
import Client from "qqq/utils/qqq/Client";
|
import Client from "qqq/utils/qqq/Client";
|
||||||
import React, {useEffect, useReducer, useState} from "react";
|
import React, {useEffect, useReducer, useState} from "react";
|
||||||
|
|
||||||
@ -75,6 +76,17 @@ const defaultScope = scopeOptions[0];
|
|||||||
|
|
||||||
const qController = Client.getInstance();
|
const qController = Client.getInstance();
|
||||||
|
|
||||||
|
interface ShareableTableMetaData
|
||||||
|
{
|
||||||
|
sharedRecordTableName: string;
|
||||||
|
assetIdFieldName: string;
|
||||||
|
scopeFieldName: string;
|
||||||
|
audienceTypesPossibleValueSourceName: string;
|
||||||
|
audiencePossibleValueSourceName: string;
|
||||||
|
thisTableOwnerIdFieldName: string;
|
||||||
|
audienceTypes: {[name: string]: any}; // values here are: ShareableAudienceType
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** component containing a Modal dialog for sharing records
|
** component containing a Modal dialog for sharing records
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -83,6 +95,7 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
const [statusString, setStatusString] = useState("Loading...");
|
const [statusString, setStatusString] = useState("Loading...");
|
||||||
const [alert, setAlert] = useState(null as string);
|
const [alert, setAlert] = useState(null as string);
|
||||||
|
|
||||||
|
const [selectedAudienceOption, setSelectedAudienceOption] = useState(null as {id: string, label: string});
|
||||||
const [selectedAudienceType, setSelectedAudienceType] = useState(null);
|
const [selectedAudienceType, setSelectedAudienceType] = useState(null);
|
||||||
const [selectedAudienceId, setSelectedAudienceId] = useState(null);
|
const [selectedAudienceId, setSelectedAudienceId] = useState(null);
|
||||||
const [selectedScopeId, setSelectedScopeId] = useState(defaultScope.id);
|
const [selectedScopeId, setSelectedScopeId] = useState(defaultScope.id);
|
||||||
@ -92,8 +105,14 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
const [needToLoadCurrentShares, setNeedToLoadCurrentShares] = useState(true);
|
const [needToLoadCurrentShares, setNeedToLoadCurrentShares] = useState(true);
|
||||||
const [everLoadedCurrentShares, setEverLoadedCurrentShares] = useState(false);
|
const [everLoadedCurrentShares, setEverLoadedCurrentShares] = useState(false);
|
||||||
|
|
||||||
|
const shareableTableMetaData = tableMetaData.shareableTableMetaData as ShareableTableMetaData;
|
||||||
|
|
||||||
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
||||||
|
|
||||||
|
if(!shareableTableMetaData)
|
||||||
|
{
|
||||||
|
console.error(`Did not find a shareableTableMetaData on table ${tableMetaData.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// trigger initial load, and post any changes, re-load //
|
// trigger initial load, and post any changes, re-load //
|
||||||
@ -124,7 +143,7 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
function handleAudienceChange(event: React.SyntheticEvent, value: any | any[], reason: string)
|
function handleAudienceChange(value: any | any[], reason: string)
|
||||||
{
|
{
|
||||||
if(value)
|
if(value)
|
||||||
{
|
{
|
||||||
@ -260,6 +279,7 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
const result = processResult as QJobComplete;
|
const result = processResult as QJobComplete;
|
||||||
setStatusString(null);
|
setStatusString(null);
|
||||||
setAlert(null);
|
setAlert(null);
|
||||||
|
setSelectedAudienceOption(null);
|
||||||
setNeedToLoadCurrentShares(true);
|
setNeedToLoadCurrentShares(true);
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
}
|
}
|
||||||
@ -297,16 +317,6 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// todo - need this to be real
|
|
||||||
const audienceOptions = [
|
|
||||||
{id: "user:1", label: "Darin Kelkhoff"},
|
|
||||||
{id: "user:2", label: "Tom Chutterloin"},
|
|
||||||
{id: "user:3", label: "Tylers Ample"},
|
|
||||||
{id: "user:4", label: "Mames Mames"},
|
|
||||||
{id: "group:2", label: "Cold Track Engineering"}
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -329,10 +339,11 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
function renderScopeDropdown(id: string, defaultValue: Scope, onChange: (event: React.SyntheticEvent, value: any | any[], reason: string) => void)
|
function renderScopeDropdown(id: string, defaultValue: Scope, onChange: (event: React.SyntheticEvent, value: any | any[], reason: string) => void)
|
||||||
{
|
{
|
||||||
|
const isDisabled = (id == "new-share-scope" && submitting);
|
||||||
return (
|
return (
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
id={id}
|
id={id}
|
||||||
disabled={id == "new-share-scope" && submitting}
|
disabled={isDisabled}
|
||||||
renderInput={(params) => (<TextField {...params} label="Scope" variant="outlined" autoComplete="off" type="search" InputProps={{...params.InputProps}} />)}
|
renderInput={(params) => (<TextField {...params} label="Scope" variant="outlined" autoComplete="off" type="search" InputProps={{...params.InputProps}} />)}
|
||||||
options={scopeOptions}
|
options={scopeOptions}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -345,7 +356,7 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
autoHighlight={true}
|
autoHighlight={true}
|
||||||
disableClearable
|
disableClearable
|
||||||
fullWidth
|
fullWidth
|
||||||
sx={autocompleteSX}
|
sx={getAutocompleteOutlinedStyle(isDisabled)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -369,7 +380,8 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
</Box>
|
</Box>
|
||||||
<Box fontSize={14} maxWidth="590px" pb={1} fontWeight="300">
|
<Box fontSize={14} maxWidth="590px" pb={1} fontWeight="300">
|
||||||
{alert && <Alert color="error" onClose={() => setAlert(null)}>{alert}</Alert>}
|
{alert && <Alert color="error" onClose={() => setAlert(null)}>{alert}</Alert>}
|
||||||
{statusString}
|
{statusString}
|
||||||
|
{!alert && !statusString && (<> </>)}
|
||||||
</Box>
|
</Box>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
@ -378,21 +390,14 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
<Box pb={3} display="flex" flexDirection="column">
|
<Box pb={3} display="flex" flexDirection="column">
|
||||||
{/* row for adding a new share */}
|
{/* row for adding a new share */}
|
||||||
<Box display="flex" flexDirection="row" alignItems="center">
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
<Box width="350px" pr={2}>
|
<Box width="350px" pr={2} mb={-1.5}>
|
||||||
<Autocomplete
|
<DynamicSelect
|
||||||
id="new-share-audience"
|
possibleValueSourceName={shareableTableMetaData.audiencePossibleValueSourceName}
|
||||||
disabled={submitting}
|
fieldLabel="User or Group" // todo should come from shareableTableMetaData
|
||||||
renderInput={(params) => (<TextField {...params} label="User or Group" variant="outlined" autoComplete="off" type="search" InputProps={{...params.InputProps}} />)}
|
initialValue={selectedAudienceOption?.id}
|
||||||
options={audienceOptions}
|
initialDisplayValue={selectedAudienceOption?.label}
|
||||||
|
inForm={false}
|
||||||
onChange={handleAudienceChange}
|
onChange={handleAudienceChange}
|
||||||
isOptionEqualToValue={(option, value) => option.id === value.id}
|
|
||||||
// @ts-ignore Property label does not exist on string | {thing with label}
|
|
||||||
getOptionLabel={(option) => option.label}
|
|
||||||
autoSelect={true}
|
|
||||||
autoHighlight={true}
|
|
||||||
disableClearable
|
|
||||||
fullWidth
|
|
||||||
sx={autocompleteSX}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box width="180px" pr={2}>
|
<Box width="180px" pr={2}>
|
||||||
@ -418,20 +423,22 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
}
|
}
|
||||||
</h5>
|
</h5>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{border: `1px solid ${colors.grayLines.main}`, borderRadius: "1rem", overflow: "auto"}} height="180px" pt="0.5rem">
|
<Box sx={{border: `1px solid ${colors.grayLines.main}`, borderRadius: "1rem", overflow: "hidden"}}>
|
||||||
{
|
<Box sx={{overflow: "auto"}} height="210px" pt="0.75rem">
|
||||||
currentShares.map((share) => (
|
{
|
||||||
<Box key={share.shareId} display="flex" justifyContent="space-between" alignItems="center" p="0.25rem" fontSize="1rem">
|
currentShares.map((share) => (
|
||||||
<Box display="flex" alignItems="center">
|
<Box key={share.shareId} display="flex" justifyContent="space-between" alignItems="center" p="0.25rem" pb="0.75rem" fontSize="1rem">
|
||||||
<Box width="310px" pl="1rem">{share.audienceLabel}</Box>
|
<Box display="flex" alignItems="center">
|
||||||
<Box width="160px">{renderScopeDropdown(`scope-${share.shareId}`, getScopeOption(share.scopeId), (event: React.SyntheticEvent, value: any | any[], reason: string) => editingExistingShareScope(share.shareId, value))}</Box>
|
<Box width="310px" pl="1rem">{share.audienceLabel}</Box>
|
||||||
|
<Box width="160px">{renderScopeDropdown(`scope-${share.shareId}`, getScopeOption(share.scopeId), (event: React.SyntheticEvent, value: any | any[], reason: string) => editingExistingShareScope(share.shareId, value))}</Box>
|
||||||
|
</Box>
|
||||||
|
<Box pr="1rem">
|
||||||
|
<Button sx={{...iconButtonSX, ...redIconButton}} onClick={() => removeShare(share.shareId)}><Icon>clear</Icon></Button>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Box pr="1rem">
|
))
|
||||||
<Button sx={{...iconButtonSX, ...redIconButton}} onClick={() => removeShare(share.shareId)}><Icon>clear</Icon></Button>
|
}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@ -448,12 +455,6 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const autocompleteSX =
|
|
||||||
{
|
|
||||||
"& .MuiAutocomplete-input": {padding: "0.125rem 0.5rem !important"},
|
|
||||||
"& .MuiOutlinedInput-root": {borderRadius: "0.75rem !important"}
|
|
||||||
};
|
|
||||||
|
|
||||||
const iconButtonSX =
|
const iconButtonSX =
|
||||||
{
|
{
|
||||||
border: `1px solid ${colors.grayLines.main} !important`,
|
border: `1px solid ${colors.grayLines.main} !important`,
|
||||||
|
@ -130,7 +130,7 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
|||||||
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
const openActionsMenu = (event: any) => setActionsMenu(event.currentTarget);
|
||||||
const closeActionsMenu = () => setActionsMenu(null);
|
const closeActionsMenu = () => setActionsMenu(null);
|
||||||
|
|
||||||
const {accentColor, setPageHeader, tableMetaData, setTableMetaData, tableProcesses, setTableProcesses, dotMenuOpen, keyboardHelpOpen, helpHelpActive, recordAnalytics} = useContext(QContext);
|
const {accentColor, setPageHeader, tableMetaData, setTableMetaData, tableProcesses, setTableProcesses, dotMenuOpen, keyboardHelpOpen, helpHelpActive, recordAnalytics, userId: currentUserId} = useContext(QContext);
|
||||||
|
|
||||||
if (localStorage.getItem(tableVariantLocalStorageKey))
|
if (localStorage.getItem(tableVariantLocalStorageKey))
|
||||||
{
|
{
|
||||||
@ -796,13 +796,37 @@ function RecordView({table, launchProcess}: Props): JSX.Element
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
const renderShareButton = () =>
|
const renderShareButton = () =>
|
||||||
{
|
{
|
||||||
if (tableMetaData && (tableMetaData.name == "savedReport" || tableMetaData.name == "savedView")) // todo - not just based on name
|
if (tableMetaData && tableMetaData.shareableTableMetaData)
|
||||||
{
|
{
|
||||||
const shareDisabled = false; // todo - only share if you're the owner? or do that in the modal?
|
let shareDisabled = true;
|
||||||
return (<Box width={standardWidth} mr={3}>
|
let disabledTooltipText = "";
|
||||||
<MDButton id="shareButton" type="button" color="info" size="small" onClick={() => openShareModal()} fullWidth startIcon={<Icon>share</Icon>} disabled={shareDisabled}>
|
if(tableMetaData.shareableTableMetaData.thisTableOwnerIdFieldName && record)
|
||||||
Share
|
{
|
||||||
</MDButton>
|
const ownerId = record.values.get(tableMetaData.shareableTableMetaData.thisTableOwnerIdFieldName);
|
||||||
|
if(ownerId != currentUserId)
|
||||||
|
{
|
||||||
|
disabledTooltipText = `Only the owner of a ${tableMetaData.label} may share it.`
|
||||||
|
shareDisabled = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
disabledTooltipText = "";
|
||||||
|
shareDisabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shareDisabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (<Box width={standardWidth} mr={2}>
|
||||||
|
<Tooltip title={disabledTooltipText}>
|
||||||
|
<span>
|
||||||
|
<MDButton id="shareButton" type="button" color="info" size="small" onClick={() => openShareModal()} fullWidth startIcon={<Icon>group_add</Icon>} disabled={shareDisabled}>
|
||||||
|
Share
|
||||||
|
</MDButton>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
</Box>);
|
</Box>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,8 @@ module.exports = function (app)
|
|||||||
app.use("/download/*", getRequestHandler());
|
app.use("/download/*", getRequestHandler());
|
||||||
app.use("/metaData/*", getRequestHandler());
|
app.use("/metaData/*", getRequestHandler());
|
||||||
app.use("/data/*", getRequestHandler());
|
app.use("/data/*", getRequestHandler());
|
||||||
|
app.use("/possibleValues/*", getRequestHandler());
|
||||||
|
app.use("/possibleValues", getRequestHandler());
|
||||||
app.use("/widget/*", getRequestHandler());
|
app.use("/widget/*", getRequestHandler());
|
||||||
app.use("/serverInfo", getRequestHandler());
|
app.use("/serverInfo", getRequestHandler());
|
||||||
app.use("/manageSession", getRequestHandler());
|
app.use("/manageSession", getRequestHandler());
|
||||||
|
Reference in New Issue
Block a user