Merge pull request #102 from Hexastack/fix/align-datagrid-switch

fix(frontend): align data grid toggle action
This commit is contained in:
Mohamed Marrouchi 2024-09-30 11:21:57 +01:00 committed by GitHub
commit 04ce4f2c87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 125 additions and 82 deletions

View File

@ -42,7 +42,6 @@ export class UserStub extends BaseSchema {
@Prop({
type: String,
unique: true,
required: true,
})
password: string;

View File

@ -6,11 +6,12 @@
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
*/
import { styled } from "@mui/material";
import { useTheme } from "@mui/material";
import {
DataGridProps,
gridClasses,
GridColDef,
GridValidRowModel,
DataGrid as MuiDataGrid,
} from "@mui/x-data-grid";
@ -18,22 +19,34 @@ import { renderHeader } from "./columns/renderHeader";
import { styledPaginationSlots } from "./DataGridStyledPagination";
import { NoDataOverlay } from "./NoDataOverlay";
const StyledDataGrid = styled(MuiDataGrid)(({ theme }) => ({
"& .MuiDataGrid-overlayWrapper": {
height: "fit-content",
},
export const StyledDataGrid = <T extends GridValidRowModel = any>(
props: DataGridProps<T>,
) => {
const theme = useTheme();
const { sx, ...otherProps } = props;
[`& .${gridClasses.row}`]: {
"&:hover": {
backgroundColor: theme.palette.background.default,
"@media (hover: none)": {
backgroundColor: theme.palette.background.default,
},
},
},
}));
return (
<MuiDataGrid
{...otherProps}
sx={{
"& .MuiDataGrid-overlayWrapper": {
height: "fit-content",
},
[`& .${gridClasses.row}`]: {
"&:hover": {
backgroundColor: theme.palette.background.default,
"@media (hover: none)": {
backgroundColor: theme.palette.background.default,
},
},
},
...sx,
}}
/>
);
};
export const DataGrid = ({
export const DataGrid = <T extends GridValidRowModel = any>({
columns,
rows = [],
autoHeight = true,
@ -46,8 +59,8 @@ export const DataGrid = ({
showColumnVerticalBorder = false,
sx = { border: "none" },
...rest
}: DataGridProps) => {
const styledColumns: GridColDef[] = columns.map((col) => ({
}: DataGridProps<T>) => {
const styledColumns: GridColDef<T>[] = columns.map((col) => ({
disableColumnMenu: true,
renderHeader,
headerAlign: "left",
@ -56,7 +69,7 @@ export const DataGrid = ({
}));
return (
<StyledDataGrid
<StyledDataGrid<T>
autoHeight={autoHeight}
disableRowSelectionOnClick={disableRowSelectionOnClick}
slots={slots}

View File

@ -9,7 +9,7 @@
import { faAlignLeft } from "@fortawesome/free-solid-svg-icons";
import AddIcon from "@mui/icons-material/Add";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import { Button, Chip, Grid, Paper, Typography } from "@mui/material";
import { Button, Chip, Grid, Paper, Switch, Typography } from "@mui/material";
import Link from "next/link";
import { useRouter } from "next/router";
import { useTranslation } from "react-i18next";
@ -24,13 +24,14 @@ import { renderHeader } from "@/app-components/tables/columns/renderHeader";
import { DataGrid } from "@/app-components/tables/DataGrid";
import { useDelete } from "@/hooks/crud/useDelete";
import { useFind } from "@/hooks/crud/useFind";
import { useGet } from "@/hooks/crud/useGet";
import { useGet, useGetFromCache } from "@/hooks/crud/useGet";
import { useUpdate } from "@/hooks/crud/useUpdate";
import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
import { useHasPermission } from "@/hooks/useHasPermission";
import { useSearch } from "@/hooks/useSearch";
import { useToast } from "@/hooks/useToast";
import { PageHeader } from "@/layout/content/PageHeader";
import { EntityType } from "@/services/types";
import { EntityType, Format } from "@/services/types";
import { IContentType } from "@/types/content-type.types";
import { IContent } from "@/types/content.types";
import { PermissionAction } from "@/types/permission.types";
@ -62,17 +63,26 @@ export const Contents = () => {
entity: EntityType.CONTENT_TYPE,
});
const { dataGridProps } = useFind(
{ entity: EntityType.CONTENT },
{ entity: EntityType.CONTENT, format: Format.FULL },
{
params: searchPayload,
},
);
const { mutateAsync: updateContent } = useUpdate(EntityType.CONTENT, {
onError: (error) => {
toast.error(error.message || t("message.internal_server_error"));
},
onSuccess() {
toast.success(t("message.success_save"));
},
});
const { mutateAsync: deleteContent } = useDelete(EntityType.CONTENT, {
onSuccess: () => {
deleteDialogCtl.closeDialog();
toast.success(t("message.item_delete_success"));
},
});
const getEntityFromCache = useGetFromCache(EntityType.CONTENT_TYPE);
const actionColumns = useActionColumns<IContent>(
EntityType.CONTENT,
[
@ -108,7 +118,7 @@ export const Contents = () => {
<PageHeader
icon={faAlignLeft}
chip={<Chip label={data?.name} variant="title" />}
chip={<Chip label={data?.name} size="medium" variant="title" />}
title={t("title.content")}
>
<Grid justifyContent="flex-end" gap={1} container alignItems="center">
@ -143,7 +153,7 @@ export const Contents = () => {
<Grid padding={2} container>
<Grid item width="100%">
<DataGrid
<DataGrid<IContent>
{...dataGridProps}
disableColumnFilter
showCellVerticalBorder={false}
@ -155,25 +165,39 @@ export const Contents = () => {
field: "entity",
headerName: t("label.entity"),
flex: 1,
valueGetter: (row) => row["name"],
valueGetter: (row: IContent) => {
const contentType = getEntityFromCache(row.id);
return contentType?.name;
},
},
{
maxWidth: 120,
field: "status",
headerName: t("label.status"),
resizable: false,
disableColumnMenu: true,
renderHeader,
headerAlign: "left",
renderCell: (params) => (
<Grid container>
<Grid item xs={12}>
<Chip
label={t(
params.row.status
? "label.enabled"
: "label.disabled",
)}
variant={params.row.status ? "enabled" : "disabled"}
/>
</Grid>
</Grid>
<Switch
checked={params.value}
color="primary"
inputProps={{ "aria-label": "primary checkbox" }}
disabled={
!hasPermission(
EntityType.CONTENT,
PermissionAction.UPDATE,
)
}
onChange={() => {
updateContent({
id: params.row.id,
params: {
status: !params.row.status,
},
});
}}
/>
),
},
{

View File

@ -107,6 +107,9 @@ export const ContextVars = () => {
checked={params.value}
color="primary"
inputProps={{ "aria-label": "primary checkbox" }}
disabled={
!hasPermission(EntityType.CONTEXT_VAR, PermissionAction.UPDATE)
}
onChange={() => {
updateContextVar({
id: params.row.id,

View File

@ -8,7 +8,7 @@
import { Flag } from "@mui/icons-material";
import AddIcon from "@mui/icons-material/Add";
import { Button, Grid, Paper } from "@mui/material";
import { Button, Grid, Paper, Switch } from "@mui/material";
import { GridColDef } from "@mui/x-data-grid";
import { useTranslation } from "react-i18next";
import { useQueryClient } from "react-query";
@ -92,13 +92,6 @@ export const Languages = () => {
const actionColumns = useActionColumns<ILanguage>(
EntityType.LANGUAGE,
[
{
label: ActionColumnLabel.Toggle,
action: (row) => toggleDefault(row),
requires: [PermissionAction.UPDATE],
getState: (row) => row.isDefault,
helperText: t("button.mark_as_default"),
},
{
label: ActionColumnLabel.Edit,
action: (row) => editDialogCtl.openDialog(row),
@ -131,15 +124,6 @@ export const Languages = () => {
renderHeader,
headerAlign: "left",
},
{
flex: 1,
field: "isDefault",
headerName: t("label.is_default"),
disableColumnMenu: true,
renderHeader,
headerAlign: "left",
valueGetter: (value) => (value ? t("label.yes") : t("label.no")),
},
{
flex: 1,
field: "isRTL",
@ -149,6 +133,28 @@ export const Languages = () => {
headerAlign: "left",
valueGetter: (value) => (value ? t("label.yes") : t("label.no")),
},
{
maxWidth: 120,
field: "isDefault",
headerName: t("label.is_default"),
disableColumnMenu: true,
renderHeader,
headerAlign: "left",
renderCell: (params) => (
<Switch
checked={params.value}
color="primary"
inputProps={{ "aria-label": "primary checkbox" }}
disabled={
params.value ||
!hasPermission(EntityType.LANGUAGE, PermissionAction.UPDATE)
}
onChange={() => {
toggleDefault(params.row);
}}
/>
),
},
{
minWidth: 140,
field: "createdAt",

View File

@ -8,7 +8,7 @@
import { faUsers } from "@fortawesome/free-solid-svg-icons";
import PersonAddAlt1Icon from "@mui/icons-material/PersonAddAlt1";
import { Button, Grid, Paper } from "@mui/material";
import { Button, Grid, Paper, Switch } from "@mui/material";
import { GridColDef } from "@mui/x-data-grid";
import { useTranslation } from "react-i18next";
@ -23,6 +23,7 @@ import { buildRenderPicture } from "@/app-components/tables/columns/renderPictur
import { DataGrid } from "@/app-components/tables/DataGrid";
import { useFind } from "@/hooks/crud/useFind";
import { useUpdate } from "@/hooks/crud/useUpdate";
import { useAuth } from "@/hooks/useAuth";
import { useConfig } from "@/hooks/useConfig";
import { getDisplayDialogs, useDialog } from "@/hooks/useDialog";
import { useHasPermission } from "@/hooks/useHasPermission";
@ -42,6 +43,7 @@ export const Users = () => {
const { ssoEnabled } = useConfig();
const { t } = useTranslation();
const { toast } = useToast();
const { user } = useAuth();
const { mutateAsync: updateUser } = useUpdate(EntityType.USER, {
onError: (error) => {
toast.error(error.message || t("message.internal_server_error"));
@ -136,33 +138,29 @@ export const Users = () => {
maxWidth: 120,
field: "state",
headerName: t("label.status"),
resizable: false,
disableColumnMenu: true,
renderCell: (params) => (
<Grid justifyContent="center" alignItems="center">
<Button
variant="contained"
color={params.row.state ? "success" : "error"}
sx={{
paddingX: 2,
paddingY: 1,
}}
onClick={() => {
updateUser({
id: params.row.id,
params: {
state: !params.row.state,
},
});
}}
disabled={ssoEnabled}
>
{t(params.row.state ? "label.enabled" : "label.disabled")}
</Button>
</Grid>
),
headerAlign: "center",
renderHeader,
headerAlign: "left",
renderCell: (params) => (
<Switch
checked={params.value}
color="primary"
inputProps={{ "aria-label": "primary checkbox" }}
disabled={
params.row.id === user?.id ||
ssoEnabled ||
!hasPermission(EntityType.USER, PermissionAction.UPDATE)
}
onChange={() => {
updateUser({
id: params.row.id,
params: {
state: !params.row.state,
},
});
}}
/>
),
},
{
minWidth: 140,