Skip to content

Commit

Permalink
chore: add frontend visual fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
brunotot committed Nov 16, 2024
1 parent b05c997 commit 163eb3c
Show file tree
Hide file tree
Showing 33 changed files with 392 additions and 313 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ export class UserController {

@contract(contracts.User.findAll, withRouteSecured(Role.Enum["avr-admin"]))
async findAll(): RouteOutput<typeof contracts.User.findAll> {
/*return {
status: 500,
body: {
message: "Not implemented",
status: 500,
timestamp: new Date().toISOString(),
},
};*/
return {
status: 200,
body: await this.userService.findAll(),
Expand Down
1 change: 1 addition & 0 deletions packages/mern-sample-app/app-vite-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"material-ui-popup-state": "^5.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.1.2",
"react-hook-form": "^7.53.1",
"react-i18next": "^14.1.0",
"react-router-dom": "^6.22.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { NavigationRouteProtectParam } from "@org/app-vite-react/server/route-typings";

import { LoaderSuspense } from "../LoaderSuspense";
import { Protect } from "../Protect";
import { QueryErrorBoundary } from "../QueryErrorBoundary";

export type BoundarySuspenseGroupProps = {
protect?: NavigationRouteProtectParam;
children: React.ReactNode;
};

export function BoundarySuspenseGroup({ protect, children }: BoundarySuspenseGroupProps) {
return (
<QueryErrorBoundary>
<LoaderSuspense>
<Protect protect={protect}>{children}</Protect>
</LoaderSuspense>
</QueryErrorBoundary>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./BoundarySuspenseGroup";
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
type ServerDatatableProps,
type ClientDatatableProps,
ClientDatatable,
ServerDatatable,
} from "./impl";

export type DatatableProps<T> = ServerDatatableProps<T> | ClientDatatableProps<T>;

export function Datatable<T>(props: DatatableProps<T>) {
if (props.sync) return <ClientDatatable {...props} />;
return <ServerDatatable {...props} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { PropsWithChildren } from "react";
import { Card } from "@mui/material";

export function DatatableContainer({ children }: PropsWithChildren) {
// return <>{children}</>;

//return <Paper>{children}</Paper>;
return <Card elevation={3}>{children}</Card>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,33 @@ import {
TablePagination,
} from "@mui/material";
import { DtSortableCell } from "@org/app-vite-react/app/components/Datatable/components/DtSortableCell/DtSortableCell";
import { DEFAULT_PAGINATION_OPTIONS } from "@org/app-vite-react/app/components/Datatable/types";
import { ClientResponsiveTable } from "@org/app-vite-react/app/pages/admin-settings/manage-users/components";
import { Fragment, useMemo, useState } from "react";

export function ClientDatatable<T>(props: ClientDatatableProps<T>) {
const { data, columns, disablePagination = false, renderMobileRow } = props;
const {
data,
columns,
pagination,
onPaginationChange,
disablePagination = false,
renderMobileRow,
keyMapper,
} = props;
const matchesMobile = mui.useMediaQuery("(max-width:678px)");
const [sortData, setSortData] = useState<DtBaseOrder>([]);
const [paginationOptions, setPaginationOptions] = useState(DEFAULT_PAGINATION_OPTIONS);

const onPageChange = (newPage: number) => {
setPaginationOptions({ ...paginationOptions, page: newPage });
onPaginationChange({ ...pagination, page: newPage });
};

const onRowsPerPageChange = (newRowsPerPage: number) => {
setPaginationOptions({ ...paginationOptions, rowsPerPage: newRowsPerPage });
onPaginationChange({ ...pagination, rowsPerPage: newRowsPerPage });
};

const filteredData = useMemo(() => {
if (disablePagination) return data;
const { page, rowsPerPage } = paginationOptions;
const { page, rowsPerPage } = pagination;
let localData = data;
if (sortData.length > 0) {
localData = [...data].sort((a, b) => {
Expand All @@ -49,7 +55,7 @@ export function ClientDatatable<T>(props: ClientDatatableProps<T>) {
}
return localData.slice(page * rowsPerPage, (page + 1) * rowsPerPage);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, paginationOptions, disablePagination, sortData]);
}, [data, pagination, disablePagination, sortData]);

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const onSortColumnClick = (id: string, _event: MouseEvent<unknown>) => {
Expand Down Expand Up @@ -81,8 +87,8 @@ export function ClientDatatable<T>(props: ClientDatatableProps<T>) {
labelDisplayedRows={({ from, to, count }) => `${from}-${to} to ${count}`}
rowsPerPageOptions={[5, 10, 25, 50, 100]}
count={data.length}
page={paginationOptions.page}
rowsPerPage={paginationOptions.rowsPerPage}
page={pagination.page}
rowsPerPage={pagination.rowsPerPage}
showFirstButton
showLastButton
onPageChange={(_, newPage) => onPageChange(newPage)}
Expand Down Expand Up @@ -134,13 +140,8 @@ export function ClientDatatable<T>(props: ClientDatatableProps<T>) {
</TableRow>
</TableHead>
<TableBody>
{filteredData.map((item, i) => (
<TableRow
hover
key={i + paginationOptions.page * paginationOptions.rowsPerPage}
role="checkbox"
tabIndex={-1}
>
{filteredData.map(item => (
<TableRow hover key={keyMapper(item)} role="checkbox" tabIndex={-1}>
{columns.map(({ id, align, renderBody }) => (
<TableCell key={id} align={align}>
{renderBody(item, { cleanup: () => {} })}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import type { DtBaseColumn } from "@org/app-vite-react/app/components/Datatable/types";
import type { DtBaseProps } from "@org/app-vite-react/app/components/Datatable/types";

export type DtClientColumnSort<T> = (o1: T, o2: T) => number;

export type DtClientColumn<T> = DtBaseColumn<T> & {
sort?: DtClientColumnSort<T>;
};

export type ClientDatatableProps<T> = {
data: T[];
columns: DtClientColumn<T>[];
export type ClientDatatableProps<T> = DtBaseProps<T, (o1: T, o2: T) => number> & {
sync: true;
disablePagination?: boolean;
renderMobileRow: (item: T) => React.ReactNode;
};
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ export function ServerDatatable<T>({
data,
columns,
keyMapper,
paginationOptions,
onPaginationOptionsChange,
pagination,
onPaginationChange,
count,
}: ServerDatatableProps<T>) {
const sortData =
paginationOptions?.order.map((order: TODO) => {
pagination?.order.map((order: TODO) => {
const [id, direction] = order.split(" ");
return { id, direction } as DtBaseSortItem;
}) ?? [];

const onPageChange = (newPage: number) => {
onPaginationOptionsChange({ ...paginationOptions, page: newPage });
onPaginationChange({ ...pagination, page: newPage });
};

const onRowsPerPageChange = (newRowsPerPage: number) => {
onPaginationOptionsChange({ ...paginationOptions, rowsPerPage: newRowsPerPage });
onPaginationChange({ ...pagination, rowsPerPage: newRowsPerPage });
};

const onSortColumnClick = useCallback(
Expand All @@ -43,19 +43,19 @@ export function ServerDatatable<T>({
//console.log(_event);
const sortIndex = sortData.findIndex((v: TODO) => v.id === id);
if (sortIndex < 0) {
onPaginationOptionsChange({ ...paginationOptions, order: [`${id} asc`] });
onPaginationChange({ ...pagination, order: [`${id} asc`] });
return;
}
const sortProps = sortData[sortIndex];
const oldDirection = sortProps.direction;
if (oldDirection === "desc") {
onPaginationOptionsChange({ ...paginationOptions, order: [] });
onPaginationChange({ ...pagination, order: [] });
return;
}
onPaginationOptionsChange({ ...paginationOptions, order: [`${id} desc`] });
onPaginationChange({ ...pagination, order: [`${id} desc`] });
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[paginationOptions, sortData],
[pagination, sortData],
);

return (
Expand Down Expand Up @@ -96,7 +96,7 @@ export function ServerDatatable<T>({
<TableRow hover key={keyMapper(item)} role="checkbox" tabIndex={-1}>
{columns.map(({ id, align, renderBody }) => (
<TableCell key={id} align={align}>
{renderBody(item)}
{renderBody(item, { cleanup: () => {} })}
</TableCell>
))}
</TableRow>
Expand All @@ -111,8 +111,8 @@ export function ServerDatatable<T>({
labelDisplayedRows={({ from, to, count }) => `${from}-${to} to ${count}`}
rowsPerPageOptions={[10, 25, 50, 100]}
count={count}
page={paginationOptions?.page ?? 0}
rowsPerPage={paginationOptions?.rowsPerPage ?? 0}
page={pagination?.page ?? 0}
rowsPerPage={pagination?.rowsPerPage ?? 0}
showFirstButton
showLastButton
onPageChange={(_, newPage) => onPageChange(newPage)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
import type { DtBaseColumn } from "@org/app-vite-react/app/components/Datatable/types";
import type { DtBaseProps } from "@org/app-vite-react/app/components/Datatable/types";

import { type PaginationOptions } from "@org/lib-api-client";

export type DtServerColumn<T> = DtBaseColumn<T> & {
sort?: string;
};

export type ServerDatatableProps<T> = {
data: T[];
columns: DtServerColumn<T>[];
keyMapper: (value: T) => string;
export type ServerDatatableProps<T> = DtBaseProps<T, string> & {
sync?: false;
count: number;
paginationOptions: PaginationOptions;
onPaginationOptionsChange: (paginationOptions: PaginationOptions) => void;
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@ export type DtBaseColumnRenderHeader = () => ReactNode;
export type DtBaseColumnRenderBody<T> = (value: T, config: DtBaseColumnConfig) => ReactNode;
export type DtBaseOrder = DtBaseSortItem[];
export type DtBaseSortItem = { id: string; direction: "asc" | "desc" };
export type DtBaseColumn<T> = {
export type DtBaseColumn<T, SORT> = {
id: string;
align?: DtBaseColumnAlign;
renderHeader: DtBaseColumnRenderHeader;
renderBody: DtBaseColumnRenderBody<T>;
sort?: SORT;
};

export type DtBaseProps<T, SORT> = {
data: T[];
columns: DtBaseColumn<T, SORT>[];
renderMobileRow: (item: T) => React.ReactNode;
keyMapper: (value: T) => string;
pagination: PaginationOptions;
onPaginationChange: (paginationOptions: PaginationOptions) => void;
};

export const DEFAULT_PAGINATION_OPTIONS: PaginationOptions = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,35 @@
import type { NavigationRouteHandle } from "@org/app-vite-react/server/route-typings";
import type { TODO } from "@org/lib-commons";

import * as icons from "@mui/icons-material";
import * as mui from "@mui/material";
import { sigDirection } from "@org/app-vite-react/app/signals/sigDirection";
import { useState } from "react";
import { useTranslation } from "@org/app-vite-react/lib/i18next";
import { useMemo, useState } from "react";
import { Link as RouterLink } from "react-router-dom";
import { type UIMatch, useMatches } from "react-router-dom";

type Crumb = {
export type NavigationCrumb = {
match: UIMatch<unknown, unknown>;
label: string;
};

function convertToCrumbs(matches: UIMatch<unknown, unknown>[]): /*Crumb[]*/ TODO[] {
const crumbs: Crumb[] = [];
function useNavigationCrumbs(): NavigationCrumb[] {
const matches: UIMatch<unknown, unknown>[] = useMatches();
const t = useTranslation();

//console.log(matches);

for (const match of matches) {
const handle: TODO = match.handle;
//console.log(match);

if (handle?.crumb) {
crumbs.push({
const crumbs: NavigationCrumb[] = useMemo(() => {
const c: NavigationCrumb[] = [];
for (const match of matches) {
const handle = match.handle as NavigationRouteHandle;
if (!handle?.crumb) continue;
c.push({
match: match,
label: handle?.crumb?.(match.params) || undefined,
label: handle.crumb(t, match.params),
});
}

/*if (
"handle" in match &&
match.handle &&
typeof match.handle === "object" &&
"crumb" in match.handle &&
match.handle.crumb &&
typeof match.handle.crumb === "function"
) {
crumbs.push({
match: match,
label: handle?.crumb?.(match.params) || undefined,
});
}*/
}
return c;
}, [t, matches]);

return crumbs;
}
Expand Down Expand Up @@ -92,9 +80,8 @@ export function LocalBreadcrumbs({
}

export function ComputedBreadcrumbs() {
const crumbs = useNavigationCrumbs();
const matchesDesktop = mui.useMediaQuery("(min-width:678px)");
const matches = useMatches();
const crumbs = convertToCrumbs(matches);
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const open = Boolean(anchorEl);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as mui from "@mui/material";
import { Suspense } from "react";

export function LoaderSuspense({ children }: React.PropsWithChildren) {
return (
<Suspense
fallback={
<mui.Box sx={{ textAlign: "center", padding: 3 }}>
<mui.CircularProgress />
</mui.Box>
}
>
{children}
</Suspense>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./LoaderSuspense";
Loading

0 comments on commit 163eb3c

Please sign in to comment.