mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 12:50:43 +00:00
Merged feature/CE-882-add-functionality-of-sharing into feature/CE-1068-add-basic-functionality-of
This commit is contained in:
@ -94,16 +94,19 @@ function QBreadcrumbs({icon, title, route, light}: Props): JSX.Element
|
||||
{
|
||||
////////////////////////////////////////////////////////
|
||||
// avoid showing "saved view" as a breadcrumb element //
|
||||
// e.g., if at /app/table/savedView/1 (so where i==2) //
|
||||
////////////////////////////////////////////////////////
|
||||
if(routes[i] === "savedView")
|
||||
if(routes[i] === "savedView" && i == 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// avoid showing the table name if it's the element before savedView //
|
||||
// e.g., when at /app/table/savedView/1 (so where i==1) //
|
||||
// we want to just be showing "App" //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
if(i < routes.length - 1 && routes[i+1] == "savedView")
|
||||
if(i < routes.length - 1 && routes[i+1] == "savedView" && i == 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -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 {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import {Alert, Button} from "@mui/material";
|
||||
import Box from "@mui/material/Box";
|
||||
import {Alert, Box, Button} from "@mui/material";
|
||||
import Dialog from "@mui/material/Dialog";
|
||||
import DialogActions from "@mui/material/DialogActions";
|
||||
import DialogContent from "@mui/material/DialogContent";
|
||||
@ -60,7 +59,7 @@ interface Props
|
||||
view?: RecordQueryView;
|
||||
viewAsJson?: string;
|
||||
viewOnChangeCallback?: (selectedSavedViewId: number) => void;
|
||||
loadingSavedView: boolean
|
||||
loadingSavedView: boolean;
|
||||
queryScreenUsage: QueryScreenUsage;
|
||||
}
|
||||
|
||||
@ -69,6 +68,8 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [savedViews, setSavedViews] = useState([] as QRecord[]);
|
||||
const [yourSavedViews, setYourSavedViews] = useState([] as QRecord[]);
|
||||
const [viewsSharedWithYou, setViewsSharedWithYou] = useState([] as QRecord[]);
|
||||
const [savedViewsMenu, setSavedViewsMenu] = useState(null);
|
||||
const [savedViewsHaveLoaded, setSavedViewsHaveLoaded] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@ -91,7 +92,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
const CLEAR_OPTION = "New 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 - //
|
||||
@ -114,13 +115,13 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
{
|
||||
setSavedViewsHaveLoaded(true);
|
||||
});
|
||||
}, [location, tableMetaData])
|
||||
}, [location, tableMetaData]);
|
||||
|
||||
|
||||
const baseView = currentSavedView ? JSON.parse(currentSavedView.values.get("viewJson")) as RecordQueryView : tableDefaultView;
|
||||
const viewDiffs = SavedViewUtils.diffViews(tableMetaData, baseView, view);
|
||||
let viewIsModified = false;
|
||||
if(viewDiffs.length > 0)
|
||||
if (viewDiffs.length > 0)
|
||||
{
|
||||
viewIsModified = true;
|
||||
}
|
||||
@ -130,7 +131,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
*******************************************************************************/
|
||||
async function loadSavedViews()
|
||||
{
|
||||
if (! tableMetaData)
|
||||
if (!tableMetaData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -140,8 +141,24 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
|
||||
let savedViews = await makeSavedViewRequest("querySavedView", formData);
|
||||
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);
|
||||
closeSavedViewsMenu();
|
||||
viewOnChangeCallback(record.values.get("id"));
|
||||
if(isQueryScreen)
|
||||
if (isQueryScreen)
|
||||
{
|
||||
navigate(`${metaData.getTablePathByName(tableMetaData.name)}/savedView/${record.values.get("id")}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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);
|
||||
setIsSaveFilterAs(false);
|
||||
setIsRenameFilter(false);
|
||||
setIsDeleteFilter(false)
|
||||
setIsDeleteFilter(false);
|
||||
|
||||
switch(optionName)
|
||||
switch (optionName)
|
||||
{
|
||||
case SAVE_OPTION:
|
||||
if(currentSavedView == null)
|
||||
if (currentSavedView == null)
|
||||
{
|
||||
setSavedViewNameInputValue("");
|
||||
}
|
||||
@ -186,28 +202,28 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
setIsSaveFilterAs(true);
|
||||
break;
|
||||
case CLEAR_OPTION:
|
||||
setSaveFilterPopupOpen(false)
|
||||
setSaveFilterPopupOpen(false);
|
||||
viewOnChangeCallback(null);
|
||||
if(isQueryScreen)
|
||||
if (isQueryScreen)
|
||||
{
|
||||
navigate(metaData.getTablePathByName(tableMetaData.name));
|
||||
}
|
||||
break;
|
||||
case RENAME_OPTION:
|
||||
if(currentSavedView != null)
|
||||
if (currentSavedView != null)
|
||||
{
|
||||
setSavedViewNameInputValue(currentSavedView.values.get("label"));
|
||||
}
|
||||
setIsRenameFilter(true);
|
||||
break;
|
||||
case DELETE_OPTION:
|
||||
setIsDeleteFilter(true)
|
||||
setIsDeleteFilter(true);
|
||||
break;
|
||||
case NEW_REPORT_OPTION:
|
||||
createNewReport();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -215,11 +231,11 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
*******************************************************************************/
|
||||
function createNewReport()
|
||||
{
|
||||
const defaultValues: {[key: string]: any} = {};
|
||||
const defaultValues: { [key: string]: any } = {};
|
||||
defaultValues.tableName = tableMetaData.name;
|
||||
|
||||
let filterForBackend = JSON.parse(JSON.stringify(view.queryFilter));
|
||||
filterForBackend = FilterUtils.prepQueryFilterForBackend(tableMetaData, filterForBackend);
|
||||
filterForBackend = FilterUtils.prepQueryFilterForBackend(tableMetaData, filterForBackend);
|
||||
|
||||
defaultValues.queryFilterJson = JSON.stringify(filterForBackend);
|
||||
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
|
||||
*******************************************************************************/
|
||||
@ -247,7 +262,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
setSaveFilterPopupOpen(false);
|
||||
setSaveOptionsOpen(false);
|
||||
|
||||
await(async() =>
|
||||
await (async () =>
|
||||
{
|
||||
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 //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
FilterUtils.stripAwayIncompleteCriteria(viewObject.queryFilter)
|
||||
FilterUtils.stripAwayIncompleteCriteria(viewObject.queryFilter);
|
||||
|
||||
formData.append("viewJson", JSON.stringify(viewObject));
|
||||
|
||||
if (isSaveFilterAs || isRenameFilter || currentSavedView == null)
|
||||
{
|
||||
formData.append("label", savedViewNameInputValue);
|
||||
if(currentSavedView != null && isRenameFilter)
|
||||
if (currentSavedView != null && isRenameFilter)
|
||||
{
|
||||
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"));
|
||||
}
|
||||
const recordList = await makeSavedViewRequest("storeSavedView", formData);
|
||||
await(async() =>
|
||||
await (async () =>
|
||||
{
|
||||
if (recordList && recordList.length > 0)
|
||||
{
|
||||
@ -302,11 +317,11 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
catch (e: any)
|
||||
{
|
||||
let message = JSON.stringify(e);
|
||||
if(typeof e == "string")
|
||||
if (typeof e == "string")
|
||||
{
|
||||
message = e;
|
||||
}
|
||||
else if(typeof e == "object" && e.message)
|
||||
else if (typeof e == "object" && e.message)
|
||||
{
|
||||
message = e.message;
|
||||
}
|
||||
@ -321,7 +336,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** hides/shows the save options
|
||||
*******************************************************************************/
|
||||
@ -331,7 +345,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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
|
||||
*******************************************************************************/
|
||||
@ -356,7 +368,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** closes current dialog
|
||||
*******************************************************************************/
|
||||
@ -366,7 +377,6 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** make a request to the backend for various savedView processes
|
||||
*******************************************************************************/
|
||||
@ -375,7 +385,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
/////////////////////////
|
||||
// fetch saved filters //
|
||||
/////////////////////////
|
||||
let savedViews = [] as QRecord[]
|
||||
let savedViews = [] as QRecord[];
|
||||
try
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@ -386,12 +396,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
if (processResult instanceof QJobError)
|
||||
{
|
||||
const jobError = processResult as QJobError;
|
||||
throw(jobError.error);
|
||||
throw (jobError.error);
|
||||
}
|
||||
else
|
||||
{
|
||||
const result = processResult as QJobComplete;
|
||||
if(result.values.savedViewList)
|
||||
if (result.values.savedViewList)
|
||||
{
|
||||
for (let i = 0; i < result.values.savedViewList.length; i++)
|
||||
{
|
||||
@ -403,7 +413,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
throw(e);
|
||||
throw (e);
|
||||
}
|
||||
|
||||
return (savedViews);
|
||||
@ -416,17 +426,27 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
|
||||
const tooltipMaxWidth = (maxWidth: string) =>
|
||||
{
|
||||
return ({slotProps: {
|
||||
tooltip: {
|
||||
sx: {
|
||||
maxWidth: maxWidth
|
||||
return ({
|
||||
slotProps: {
|
||||
tooltip: {
|
||||
sx: {
|
||||
maxWidth: maxWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
}})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
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 && (
|
||||
<Menu
|
||||
anchorEl={savedViewsMenu}
|
||||
@ -443,75 +463,101 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
}
|
||||
{
|
||||
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.</>}>
|
||||
<MenuItem onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>
|
||||
<ListItemIcon><Icon>save</Icon></ListItemIcon>
|
||||
{currentSavedView ? "Save..." : "Save As..."}
|
||||
</MenuItem>
|
||||
<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.</>}>
|
||||
<span>
|
||||
<MenuItem disabled={disabledBecauseNotOwner} onClick={() => handleDropdownOptionClick(SAVE_OPTION)}>
|
||||
<ListItemIcon><Icon>save</Icon></ListItemIcon>
|
||||
{currentSavedView ? "Save..." : "Save As..."}
|
||||
</MenuItem>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen && hasStorePermission && currentSavedView != null &&
|
||||
<Tooltip {...menuTooltipAttribs} title="Change the name for this saved view.">
|
||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(RENAME_OPTION)}>
|
||||
<ListItemIcon><Icon>edit</Icon></ListItemIcon>
|
||||
Rename...
|
||||
</MenuItem>
|
||||
<Tooltip {...menuTooltipAttribs} title={notOwnerTooltipText ?? "Change the name for this saved view."}>
|
||||
<span>
|
||||
<MenuItem disabled={currentSavedView === null || disabledBecauseNotOwner} onClick={() => handleDropdownOptionClick(RENAME_OPTION)}>
|
||||
<ListItemIcon><Icon>edit</Icon></ListItemIcon>
|
||||
Rename...
|
||||
</MenuItem>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen && hasStorePermission && currentSavedView != null &&
|
||||
<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)}>
|
||||
<ListItemIcon><Icon>content_copy</Icon></ListItemIcon>
|
||||
Save As...
|
||||
</MenuItem>
|
||||
<span>
|
||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DUPLICATE_OPTION)}>
|
||||
<ListItemIcon><Icon>content_copy</Icon></ListItemIcon>
|
||||
Save As...
|
||||
</MenuItem>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen && hasDeletePermission && currentSavedView != null &&
|
||||
<Tooltip {...menuTooltipAttribs} title="Delete this saved view.">
|
||||
<MenuItem disabled={currentSavedView === null} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}>
|
||||
<ListItemIcon><Icon>delete</Icon></ListItemIcon>
|
||||
Delete...
|
||||
</MenuItem>
|
||||
<Tooltip {...menuTooltipAttribs} title={notOwnerTooltipText ?? "Delete this saved view."}>
|
||||
<span>
|
||||
<MenuItem disabled={currentSavedView === null || disabledBecauseNotOwner} onClick={() => handleDropdownOptionClick(DELETE_OPTION)}>
|
||||
<ListItemIcon><Icon>delete</Icon></ListItemIcon>
|
||||
Delete...
|
||||
</MenuItem>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen &&
|
||||
<Tooltip {...menuTooltipAttribs} title="Create a new view of this table, resetting the filters and columns to their defaults.">
|
||||
<MenuItem onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>
|
||||
<ListItemIcon><Icon>monitor</Icon></ListItemIcon>
|
||||
New View
|
||||
</MenuItem>
|
||||
<span>
|
||||
<MenuItem onClick={() => handleDropdownOptionClick(CLEAR_OPTION)}>
|
||||
<ListItemIcon><Icon>monitor</Icon></ListItemIcon>
|
||||
New View
|
||||
</MenuItem>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen && hasSavedReportsPermission &&
|
||||
<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)}>
|
||||
<ListItemIcon><Icon>article</Icon></ListItemIcon>
|
||||
Create Report from Current View
|
||||
</MenuItem>
|
||||
<span>
|
||||
<MenuItem onClick={() => handleDropdownOptionClick(NEW_REPORT_OPTION)}>
|
||||
<ListItemIcon><Icon>article</Icon></ListItemIcon>
|
||||
Create Report from Current View
|
||||
</MenuItem>
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
{
|
||||
isQueryScreen && <Divider/>
|
||||
isQueryScreen && <Divider />
|
||||
}
|
||||
<MenuItem disabled style={{"opacity": "initial"}}><b>Your Saved Views</b></MenuItem>
|
||||
{
|
||||
savedViews && savedViews.length > 0 ? (
|
||||
savedViews.map((record: QRecord, index: number) =>
|
||||
yourSavedViews && yourSavedViews.length > 0 ? (
|
||||
yourSavedViews.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 saved views for this table.</i>
|
||||
</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>
|
||||
);
|
||||
|
||||
@ -520,7 +566,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
let buttonBorder = colors.grayLines.main;
|
||||
let buttonColor = colors.gray.main;
|
||||
|
||||
if(currentSavedView)
|
||||
if (currentSavedView)
|
||||
{
|
||||
if (viewIsModified)
|
||||
{
|
||||
@ -548,23 +594,23 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
color: buttonColor,
|
||||
backgroundColor: buttonBackground,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
function isSaveButtonDisabled(): boolean
|
||||
{
|
||||
if(isSubmitting)
|
||||
if (isSubmitting)
|
||||
{
|
||||
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);
|
||||
}
|
||||
@ -593,7 +639,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
fontWeight: 500,
|
||||
fontSize: "0.875rem",
|
||||
p: "0.5rem",
|
||||
... buttonStyles
|
||||
...buttonStyles
|
||||
}}
|
||||
>
|
||||
<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>)
|
||||
}
|
||||
</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>
|
||||
</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 */}
|
||||
<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>)
|
||||
}
|
||||
</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>
|
||||
</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 */}
|
||||
<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>
|
||||
@ -702,15 +753,15 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
) : (
|
||||
isSaveFilterAs ? (
|
||||
<DialogTitle id="alert-dialog-title">Save View As</DialogTitle>
|
||||
):(
|
||||
) : (
|
||||
isRenameFilter ? (
|
||||
<DialogTitle id="alert-dialog-title">Rename View</DialogTitle>
|
||||
):(
|
||||
) : (
|
||||
<DialogTitle id="alert-dialog-title">Update Existing View</DialogTitle>
|
||||
)
|
||||
)
|
||||
)
|
||||
):(
|
||||
) : (
|
||||
<DialogTitle id="alert-dialog-title">Save New View</DialogTitle>
|
||||
)
|
||||
}
|
||||
@ -721,12 +772,12 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
</Box>
|
||||
) : ("")}
|
||||
{
|
||||
(! currentSavedView || isSaveFilterAs || isRenameFilter) && ! isDeleteFilter ? (
|
||||
(!currentSavedView || isSaveFilterAs || isRenameFilter) && !isDeleteFilter ? (
|
||||
<Box>
|
||||
{
|
||||
isSaveFilterAs ? (
|
||||
<Box mb={3}>Enter a name for this new 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>
|
||||
):(
|
||||
) : (
|
||||
isDeleteFilter ? (
|
||||
<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>
|
||||
)
|
||||
)
|
||||
@ -759,7 +810,7 @@ function SavedViews({qController, metaData, tableMetaData, currentSavedView, tab
|
||||
isDeleteFilter ?
|
||||
<QDeleteButton onClickHandler={handleFilterDialogButtonOnClick} disabled={isSubmitting} />
|
||||
:
|
||||
<QSaveButton label="Save" onClickHandler={handleFilterDialogButtonOnClick} disabled={isSaveButtonDisabled()}/>
|
||||
<QSaveButton label="Save" onClickHandler={handleFilterDialogButtonOnClick} disabled={isSaveButtonDisabled()} />
|
||||
}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
@ -24,9 +24,8 @@ import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QT
|
||||
import {QJobComplete} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobComplete";
|
||||
import {QJobError} from "@kingsrook/qqq-frontend-core/lib/model/processes/QJobError";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import {Alert} from "@mui/material";
|
||||
import {Alert, Box} from "@mui/material";
|
||||
import Autocomplete from "@mui/material/Autocomplete";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Card from "@mui/material/Card";
|
||||
import Icon from "@mui/material/Icon";
|
||||
@ -370,15 +369,15 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
||||
<Card sx={{my: 5, mx: "auto", p: 3}}>
|
||||
|
||||
{/* header */}
|
||||
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="flex-start">
|
||||
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="flex-start" maxWidth="590px">
|
||||
<Typography variant="h4" pb={1} fontWeight="600">
|
||||
Share {tableMetaData.label}: {record?.recordLabel ?? record?.values?.get(tableMetaData.primaryKeyField) ?? "Unknown"}
|
||||
<Box color={colors.gray.main} pb={"0.5rem"} fontSize={"0.875rem"} fontWeight="400" maxWidth="590px">
|
||||
<Box color={colors.gray.main} pb={"0.5rem"} fontSize={"0.875rem"} fontWeight="400">
|
||||
{/* todo move to helpContent (what do we attach the meta-data too??) */}
|
||||
Select a user or a group to share this record with.
|
||||
You can choose if they should only be able to Read the record, or also make Edits to it.
|
||||
{/*You can choose if they should only be able to Read the record, or also make Edits to it.*/}
|
||||
</Box>
|
||||
<Box fontSize={14} maxWidth="590px" pb={1} fontWeight="300">
|
||||
<Box fontSize={14} pb={1} fontWeight="300">
|
||||
{alert && <Alert color="error" onClose={() => setAlert(null)}>{alert}</Alert>}
|
||||
{statusString}
|
||||
{!alert && !statusString && (<> </>)}
|
||||
@ -390,7 +389,7 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
||||
<Box pb={3} display="flex" flexDirection="column">
|
||||
{/* row for adding a new share */}
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Box width="350px" pr={2} mb={-1.5}>
|
||||
<Box width="550px" pr={2} mb={-1.5}>
|
||||
<DynamicSelect
|
||||
possibleValueSourceName={shareableTableMetaData.audiencePossibleValueSourceName}
|
||||
fieldLabel="User or Group" // todo should come from shareableTableMetaData
|
||||
@ -400,9 +399,12 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
||||
onChange={handleAudienceChange}
|
||||
/>
|
||||
</Box>
|
||||
<Box width="180px" pr={2}>
|
||||
{renderScopeDropdown("new-share-scope", defaultScope, handleScopeChange)}
|
||||
</Box>
|
||||
{/*
|
||||
when turning scope back on, change width of audience box to 350px
|
||||
<Box width="180px" pr={2}>
|
||||
{renderScopeDropdown("new-share-scope", defaultScope, handleScopeChange)}
|
||||
</Box>
|
||||
*/}
|
||||
<Box>
|
||||
<Tooltip title={selectedAudienceId == null ? "Select a user or group to share with." : null}>
|
||||
<span>
|
||||
@ -429,8 +431,11 @@ export default function ShareModal({open, onClose, tableMetaData, record}: Share
|
||||
currentShares.map((share) => (
|
||||
<Box key={share.shareId} display="flex" justifyContent="space-between" alignItems="center" p="0.25rem" pb="0.75rem" fontSize="1rem">
|
||||
<Box display="flex" alignItems="center">
|
||||
<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 width="490px" pl="1rem">{share.audienceLabel}</Box>
|
||||
{/*
|
||||
when turning scope back on, change width of audience box to 310px
|
||||
<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>
|
||||
|
Reference in New Issue
Block a user