first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
import React, {useContext} from 'react';
import {GetDatatableDataParams} from '../requests/paginated-resources';
import {UseQueryResult} from '@tanstack/react-query';
import {PaginatedBackendResponse} from '../../http/backend-response/pagination-response';
export interface DataTableContextValue<T = unknown, A = unknown> {
selectedRows: (string | number)[];
setSelectedRows: (keys: (string | number)[]) => void;
endpoint: string;
params: GetDatatableDataParams;
setParams: (value: GetDatatableDataParams) => void;
query: UseQueryResult<PaginatedBackendResponse<T> & A, unknown>;
}
export const DataTableContext = React.createContext<DataTableContextValue>(
null!,
);
export function useDataTable<T = unknown, A = unknown>() {
return useContext(DataTableContext) as DataTableContextValue<T, A>;
}

View File

@@ -0,0 +1,42 @@
import React, {ReactNode} from 'react';
import {IllustratedMessage} from '../../ui/images/illustrated-message';
import {SvgImage} from '../../ui/images/svg-image/svg-image';
import {Trans} from '../../i18n/trans';
import {useIsMobileMediaQuery} from '../../utils/hooks/is-mobile-media-query';
export interface DataTableEmptyStateMessageProps {
isFiltering?: boolean;
title: ReactNode;
filteringTitle?: ReactNode;
image: string;
size?: 'sm' | 'md';
className?: string;
}
export function DataTableEmptyStateMessage({
isFiltering,
title,
filteringTitle,
image,
size,
className,
}: DataTableEmptyStateMessageProps) {
const isMobile = useIsMobileMediaQuery();
if (!size) {
size = isMobile ? 'sm' : 'md';
}
// allow user to disable filtering message variation by not passing in "filteringTitle"
return (
<IllustratedMessage
className={className}
size={size}
image={<SvgImage src={image} />}
title={isFiltering && filteringTitle ? filteringTitle : title}
description={
isFiltering && filteringTitle ? (
<Trans message="Try another search query or different filters" />
) : undefined
}
/>
);
}

View File

@@ -0,0 +1,53 @@
import React, {ReactElement, ReactNode, useId} from 'react';
import {TableDataItem} from '../../ui/tables/types/table-data-item';
import {DataTable, DataTableProps} from '../data-table';
import {TableProps} from '../../ui/tables/table';
import {StaticPageTitle} from '../../seo/static-page-title';
import {MessageDescriptor} from '../../i18n/message-descriptor';
import clsx from 'clsx';
interface Props<T extends TableDataItem> extends DataTableProps<T> {
title?: ReactElement<MessageDescriptor>;
headerContent?: ReactNode;
headerItemsAlign?: string;
enableSelection?: boolean;
onRowAction?: TableProps<T>['onAction'];
padding?: string;
className?: string;
}
export function DataTablePage<T extends TableDataItem>({
title,
headerContent,
headerItemsAlign = 'items-end',
className,
padding,
...dataTableProps
}: Props<T>) {
const titleId = useId();
return (
<div className={clsx(padding ?? 'p-12 md:p-24', className)}>
{title && (
<div
className={clsx(
'mb-16',
headerContent && `flex ${headerItemsAlign} gap-4`,
)}
>
<StaticPageTitle>{title}</StaticPageTitle>
<h1 className="text-3xl font-light first:capitalize" id={titleId}>
{title}
</h1>
{headerContent}
</div>
)}
<DataTable
{...dataTableProps}
tableDomProps={{
'aria-labelledby': title ? titleId : undefined,
}}
/>
</div>
);
}

View File

@@ -0,0 +1,53 @@
import {Button} from '../../ui/buttons/button';
import {Trans} from '../../i18n/trans';
import {ConfirmationDialog} from '../../ui/overlays/dialog/confirmation-dialog';
import {DialogTrigger} from '../../ui/overlays/dialog/dialog-trigger';
import React from 'react';
import {useDeleteSelectedRows} from '../requests/delete-selected-rows';
import {useDataTable} from './data-table-context';
import {useDialogContext} from '@common/ui/overlays/dialog/dialog-context';
import {errorStatusIs} from '@common/utils/http/error-status-is';
export function DeleteSelectedItemsAction() {
return (
<DialogTrigger type="modal">
<Button variant="flat" color="danger" className="ml-auto">
<Trans message="Delete" />
</Button>
<DeleteItemsDialog />
</DialogTrigger>
);
}
function DeleteItemsDialog() {
const deleteSelectedRows = useDeleteSelectedRows();
const {selectedRows, setSelectedRows} = useDataTable();
const {close} = useDialogContext();
return (
<ConfirmationDialog
isLoading={deleteSelectedRows.isPending}
title={
<Trans
message="Delete [one 1 item|other :count items]?"
values={{count: selectedRows.length}}
/>
}
body={
<Trans message="This will permanently remove the items and cannot be undone." />
}
confirm={<Trans message="Delete" />}
isDanger
onConfirm={() => {
deleteSelectedRows.mutate(undefined, {
onSuccess: () => close(),
onError: err => {
if (errorStatusIs(err, 422)) {
setSelectedRows([]);
close();
}
},
});
}}
/>
);
}