feat: enhance entity row actions with primary action support
- Added primary action support to EntityRowActions component, allowing for a main action button with an icon and label. - Updated various components to utilize the new primary action feature, replacing existing action buttons with the new structure. - Introduced EyeIcon for view actions and PencilIcon for edit actions across multiple entity lists. - Improved accessibility by ensuring primary actions have appropriate aria-labels. - Refactored CatalogRowActions to remove direct view action and integrate with the new structure. - Adjusted tests to cover new primary action functionality and ensure proper rendering and behavior.
This commit is contained in:
parent
68980d9131
commit
ceb4eb88c6
@ -1010,3 +1010,19 @@ registros que presenta cada listado, incluidos los inactivos, sin aplicar sus fi
|
|||||||
Los inventarios y perfiles usan `useListQuery` con las claves existentes por compañía:
|
Los inventarios y perfiles usan `useListQuery` con las claves existentes por compañía:
|
||||||
comparten caché e invalidaciones de altas, cambios y bajas, sin nuevas rutas HTTP ni
|
comparten caché e invalidaciones de altas, cambios y bajas, sin nuevas rutas HTTP ni
|
||||||
estado remoto duplicado. Antes de cargar la compañía, esas consultas están deshabilitadas.
|
estado remoto duplicado. Antes de cargar la compañía, esas consultas están deshabilitadas.
|
||||||
|
|
||||||
|
|
||||||
|
### Acción principal de fila
|
||||||
|
|
||||||
|
`EntityRowActions` dispone de `primaryAction` con `label`, `icon`, `onClick` y
|
||||||
|
`disabled` opcional. Presenta un botón compacto de icono con tooltip y nombre
|
||||||
|
accesible antes del desplegable de acciones secundarias. El consumidor aporta
|
||||||
|
textos traducidos y callbacks; el componente no conoce entidades ni endpoints.
|
||||||
|
La operación principal no debe repetirse entre los `EntityAction` secundarios.
|
||||||
|
|
||||||
|
Omitir `children` cuando solo exista la principal. Si las acciones secundarias
|
||||||
|
dependen de condiciones, declarar `menuVisible` con la misma condición para
|
||||||
|
evitar un menú vacío. Mantener el lápiz para editar y el ojo para abrir o ver.
|
||||||
|
Los listados de configuraciones conservan deshabilitada la edición de registros
|
||||||
|
retirados o nunca configurados. Las acciones de baja o eliminación siguen en
|
||||||
|
el desplegable. Véase el [patrón de listados](patron-de-listados-de-entidades.md).
|
||||||
|
|||||||
@ -40,7 +40,7 @@ El pie opcional está fuera de la tabla y solo aparece cuando existe contenido c
|
|||||||
- El identificador principal abre la ficha existente si hay una. Usar enlace si existe una ruta real; botón si abre diálogo, detalle en memoria o panel. No inventar rutas.
|
- El identificador principal abre la ficha existente si hay una. Usar enlace si existe una ruta real; botón si abre diálogo, detalle en memoria o panel. No inventar rutas.
|
||||||
- Representar estados mediante indicador visual y texto, nunca solo color. Mantener los avisos de revisión e información contextual.
|
- Representar estados mediante indicador visual y texto, nunca solo color. Mantener los avisos de revisión e información contextual.
|
||||||
- Las relaciones son navegables cuando el destino ya ofrece navegación adecuada; no introducir enlaces ficticios ni nuevas rutas.
|
- Las relaciones son navegables cuando el destino ya ofrece navegación adecuada; no introducir enlaces ficticios ni nuevas rutas.
|
||||||
- Acciones de fila siempre al final, con cabecera y celdas alineadas a la derecha y columna anclada al borde derecho durante el desplazamiento horizontal. La columna actions no se oculta ni se ordena. El disparador muestra únicamente el icono de tres puntos; conserva etiqueta accesible y título, sin repetir «Acciones» en las filas. El menú contextual mantiene las restricciones existentes. No convertir acciones de negocio en acciones masivas.
|
- Acciones de fila siempre al final, con cabecera y celdas alineadas a la derecha y columna anclada al borde derecho durante el desplazamiento horizontal. La columna actions no se oculta ni se ordena. La acción principal se presenta como botón de icono con tooltip y nombre accesible, antes del menú de tres puntos. Usar el lápiz para Editar y el ojo para Abrir o Ver; elegir la operación según la finalidad de cada listado. La principal no se repite en el menú. Las acciones secundarias, incluidas las destructivas, permanecen en el desplegable, cuyo disparador muestra únicamente los tres puntos y conserva etiqueta accesible y título. Si no hay secundarias, no mostrar el desplegable. Conservar las restricciones y los estados deshabilitados existentes. Reutilizar `EntityRowActions.primaryAction`; el consumidor aporta el icono, el texto traducido y el callback. No convertir acciones de negocio en acciones masivas.
|
||||||
- Selección múltiple solo cuando una operación real la necesita. No añadir casillas sin consumidor. Mantener separada la selección del activo para su panel lateral.
|
- Selección múltiple solo cuando una operación real la necesita. No añadir casillas sin consumidor. Mantener separada la selección del activo para su panel lateral.
|
||||||
- Los diálogos `PickerList` mantienen una altura estable mediante `FormDialog layout="picker"` y llenan el área de resultados con `EntityTable layout="fill"`. Buscar, paginar, cargar o quedarse sin resultados no cambia la altura del diálogo. Búsqueda, filtros, paginación y acciones permanecen fuera del desplazamiento de resultados; el cuerpo permite desplazamiento adicional si la ventana es demasiado pequeña.
|
- Los diálogos `PickerList` mantienen una altura estable mediante `FormDialog layout="picker"` y llenan el área de resultados con `EntityTable layout="fill"`. Buscar, paginar, cargar o quedarse sin resultados no cambia la altura del diálogo. Búsqueda, filtros, paginación y acciones permanecen fuera del desplazamiento de resultados; el cuerpo permite desplazamiento adicional si la ventana es demasiado pequeña.
|
||||||
- En diálogos `PickerList`, la selección ocupa siempre la primera columna, con identificador `selection`, sin ordenación ni ocultación y sin anclaje a la derecha. Toda fila disponible permite activar/desactivar su checkbox o seleccionar su radio mediante `onRowClick`. Las filas deshabilitadas no cambian la selección; los controles internos conservan su interacción y el teclado, sin un segundo cambio por propagación del clic.
|
- En diálogos `PickerList`, la selección ocupa siempre la primera columna, con identificador `selection`, sin ordenación ni ocultación y sin anclaje a la derecha. Toda fila disponible permite activar/desactivar su checkbox o seleccionar su radio mediante `onRowClick`. Las filas deshabilitadas no cambian la selección; los controles internos conservan su interacción y el teclado, sin un segundo cambio por propagación del clic.
|
||||||
@ -139,7 +139,9 @@ La primera columna tiene anchura mínima para evitar nombres ilegibles. El despl
|
|||||||
- No se han creado ni alterado registros de negocio durante la revisión del navegador.
|
- No se han creado ni alterado registros de negocio durante la revisión del navegador.
|
||||||
|
|
||||||
|
|
||||||
### Ajuste de acciones de fila (7 de octubre de 2026)
|
### Ajuste inicial de acciones de fila (7 de octubre de 2026)
|
||||||
|
|
||||||
|
La regla vigente de acción principal visible figura en Reglas de interacción; este apartado registra la verificación del ajuste inicial.
|
||||||
|
|
||||||
Disparador solo con icono de tres puntos, etiqueta accesible y título conservados. Columna actions siempre al final, compacta, alineada y anclada a la derecha; no admite ocultación, búsqueda u ordenación. El fondo opaco respeta hover, menú abierto y selección del inventario.
|
Disparador solo con icono de tres puntos, etiqueta accesible y título conservados. Columna actions siempre al final, compacta, alineada y anclada a la derecha; no admite ocultación, búsqueda u ordenación. El fondo opaco respeta hover, menú abierto y selección del inventario.
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import {
|
|||||||
EntityListHeader,
|
EntityListHeader,
|
||||||
EntityRowActions,
|
EntityRowActions,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { BoxIcon } from "@repo/rdx-ui/lib/icons";
|
import { BoxIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -131,7 +131,15 @@ export function AssetInventoryPage(props: AssetInventoryPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (asset) => (
|
render: (asset) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(asset.id),
|
||||||
|
disabled: !catalog.data,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction
|
<EntityAction
|
||||||
@ -141,9 +149,6 @@ export function AssetInventoryPage(props: AssetInventoryPageProps) {
|
|||||||
{t("viewAsset")}
|
{t("viewAsset")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
|
|
||||||
<EntityAction disabled={!catalog.data} onClick={() => setEditor(asset.id)}>
|
|
||||||
{t("edit")}
|
|
||||||
</EntityAction>
|
|
||||||
<EntityAction
|
<EntityAction
|
||||||
className="text-destructive data-highlighted:text-destructive"
|
className="text-destructive data-highlighted:text-destructive"
|
||||||
onClick={() => setDeleteId(asset.id)}
|
onClick={() => setDeleteId(asset.id)}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
import { SelectWithAction } from "@repo/rdx-ui/components/select-with-action";
|
||||||
import { Building2Icon } from "@repo/rdx-ui/lib/icons";
|
import { Building2Icon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useId } from "react";
|
import { useId } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -90,15 +90,18 @@ export function BrandCatalogPage() {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (brand) => (
|
render: (brand) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
<EntityAction
|
label={t("actions")}
|
||||||
onClick={() => {
|
menuVisible={!brand.builtin}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => {
|
||||||
setEdit(brand);
|
setEdit(brand);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t("edit")}
|
|
||||||
</EntityAction>
|
|
||||||
{!brand.builtin && (
|
{!brand.builtin && (
|
||||||
<EntityAction onClick={() => setDeleting(brand)}>
|
<EntityAction onClick={() => setDeleting(brand)}>
|
||||||
{t("brands.delete")}
|
{t("brands.delete")}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { EntityRowActions, EntityStatus } from "@repo/rdx-ui/components/entity-list";
|
import { EntityRowActions, EntityStatus } from "@repo/rdx-ui/components/entity-list";
|
||||||
|
import { EyeIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { CatalogItemDTO } from "../../../../common";
|
import type { CatalogItemDTO } from "../../../../common";
|
||||||
@ -63,7 +64,14 @@ export function CatalogCell({
|
|||||||
case "actions":
|
case "actions":
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("view"),
|
||||||
|
icon: <EyeIcon aria-hidden="true" />,
|
||||||
|
onClick: onView,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<CatalogRowActions
|
<CatalogRowActions
|
||||||
deletable={scope === "global" && !item.builtin && item.companyId === null}
|
deletable={scope === "global" && !item.builtin && item.companyId === null}
|
||||||
editable={editable}
|
editable={editable}
|
||||||
@ -72,7 +80,6 @@ export function CatalogCell({
|
|||||||
onDelete={onDelete}
|
onDelete={onDelete}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
onStatus={onStatus}
|
onStatus={onStatus}
|
||||||
onView={onView}
|
|
||||||
pending={pending}
|
pending={pending}
|
||||||
/>
|
/>
|
||||||
</EntityRowActions>
|
</EntityRowActions>
|
||||||
|
|||||||
@ -10,7 +10,6 @@ export function CatalogRowActions({
|
|||||||
pending,
|
pending,
|
||||||
onEdit,
|
onEdit,
|
||||||
onCopy,
|
onCopy,
|
||||||
onView,
|
|
||||||
onStatus,
|
onStatus,
|
||||||
onDelete,
|
onDelete,
|
||||||
}: {
|
}: {
|
||||||
@ -20,7 +19,6 @@ export function CatalogRowActions({
|
|||||||
pending: boolean;
|
pending: boolean;
|
||||||
onEdit: () => void;
|
onEdit: () => void;
|
||||||
onCopy: () => void;
|
onCopy: () => void;
|
||||||
onView: () => void;
|
|
||||||
onStatus: (active: boolean | null) => void;
|
onStatus: (active: boolean | null) => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
}) {
|
}) {
|
||||||
@ -28,7 +26,6 @@ export function CatalogRowActions({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={onView}>{t("view")}</EntityAction>
|
|
||||||
{editable && <EntityAction onClick={onEdit}>{t("edit")}</EntityAction>}
|
{editable && <EntityAction onClick={onEdit}>{t("edit")}</EntityAction>}
|
||||||
{item.values.kind === "template" && <EntityAction onClick={onCopy}>{t("copy")}</EntityAction>}
|
{item.values.kind === "template" && <EntityAction onClick={onCopy}>{t("copy")}</EntityAction>}
|
||||||
<EntityAction disabled={pending} onClick={() => onStatus(!item.effectiveActive)}>
|
<EntityAction disabled={pending} onClick={() => onStatus(!item.effectiveActive)}>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { MonitorIcon } from "@repo/rdx-ui/lib/icons";
|
import { MonitorIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -159,13 +159,20 @@ export function WorkstationsPage(props: WorkstationsPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("workstationHistory")}
|
{t("workstationHistory")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
{(item.values.active ||
|
{(item.values.active ||
|
||||||
item.values.assetIds.length > 0 ||
|
item.values.assetIds.length > 0 ||
|
||||||
item.values.personId) && (
|
item.values.personId) && (
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { EntityAction, EntityRowActions } from "@repo/rdx-ui/components/entity-list";
|
import { EntityAction, EntityRowActions } from "@repo/rdx-ui/components/entity-list";
|
||||||
|
import { PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { AccessItemDTO, AccessResponseDTO, PersonDTO } from "../../../../common";
|
import type { AccessItemDTO, AccessResponseDTO, PersonDTO } from "../../../../common";
|
||||||
@ -74,12 +75,17 @@ export function AccessCell({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{" "}
|
{" "}
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: onEdit,
|
||||||
|
disabled: !own,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction disabled={!own} onClick={onEdit}>
|
|
||||||
{t("edit")}
|
|
||||||
</EntityAction>
|
|
||||||
<EntityAction onClick={onHistory}>{t("history")}</EntityAction>
|
<EntityAction onClick={onHistory}>{t("history")}</EntityAction>
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
</EntityRowActions>{" "}
|
</EntityRowActions>{" "}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { LayoutTemplateIcon } from "@repo/rdx-ui/lib/icons";
|
import { LayoutTemplateIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -142,15 +142,20 @@ export function ApplicationConfigurationsPage(props: ApplicationConfigurationsPa
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: ["removed", "never_configured"].includes(item.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("mail.history")}
|
{t("mail.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
{!["removed", "never_configured"].includes(item.values.state) && (
|
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
)}
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
||||||
{t("deleteRecord.title")}
|
{t("deleteRecord.title")}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { FileKeyIcon } from "@repo/rdx-ui/lib/icons";
|
import { FileKeyIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -149,15 +149,20 @@ export function CertificateConfigurationsPage(props: CertificateConfigurationsPa
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: ["removed", "never_configured"].includes(item.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("mail.history")}
|
{t("mail.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
{!["removed", "never_configured"].includes(item.values.state) && (
|
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
)}
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
||||||
{t("deleteRecord.title")}
|
{t("deleteRecord.title")}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import { BrowserLink } from "@metis/core/client";
|
import { BrowserLink } from "@metis/core/client";
|
||||||
import {
|
import {
|
||||||
EntityAction,
|
|
||||||
EntityListHeader,
|
EntityListHeader,
|
||||||
EntityRowActions,
|
EntityRowActions,
|
||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { Building2Icon } from "@repo/rdx-ui/lib/icons";
|
import { Building2Icon, EyeIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -113,9 +112,14 @@ export function CompaniesPage(props: CompaniesPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (company) => (
|
render: (company) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
<EntityAction onClick={() => selectCompany(company.id)}>{t("open")}</EntityAction>
|
label={t("actions")}
|
||||||
</EntityRowActions>
|
primaryAction={{
|
||||||
|
label: t("open"),
|
||||||
|
icon: <EyeIcon aria-hidden="true" />,
|
||||||
|
onClick: () => selectCompany(company.id),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
EntityAction,
|
|
||||||
EntityListHeader,
|
EntityListHeader,
|
||||||
EntityRowActions,
|
EntityRowActions,
|
||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { BriefcaseIcon } from "@repo/rdx-ui/lib/icons";
|
import { BriefcaseIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -78,9 +77,14 @@ export function DepartmentSection({ company }: { company: CompanyDTO }) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
label={t("actions")}
|
||||||
</EntityRowActions>
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { FolderIcon } from "@repo/rdx-ui/lib/icons";
|
import { FolderIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -148,15 +148,20 @@ export function FolderMappingsPage(props: FolderMappingsPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: ["removed", "never_configured"].includes(item.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("mail.history")}
|
{t("mail.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
{!["removed", "never_configured"].includes(item.values.state) && (
|
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
)}
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
||||||
{t("deleteRecord.title")}
|
{t("deleteRecord.title")}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { MailIcon } from "@repo/rdx-ui/lib/icons";
|
import { MailIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -139,15 +139,20 @@ export function MailConfigurationsPage(props: MailConfigurationsPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: ["removed", "never_configured"].includes(item.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("mail.history")}
|
{t("mail.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
{!["removed", "never_configured"].includes(item.values.state) && (
|
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
)}
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
||||||
{t("deleteRecord.title")}
|
{t("deleteRecord.title")}
|
||||||
|
|||||||
@ -5,7 +5,13 @@ import {
|
|||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { PhoneLink } from "@repo/rdx-ui/components/phone-link";
|
import { PhoneLink } from "@repo/rdx-ui/components/phone-link";
|
||||||
import { BriefcaseIcon, ChevronLeftIcon, MailIcon, PhoneIcon } from "@repo/rdx-ui/lib/icons";
|
import {
|
||||||
|
BriefcaseIcon,
|
||||||
|
ChevronLeftIcon,
|
||||||
|
MailIcon,
|
||||||
|
PencilIcon,
|
||||||
|
PhoneIcon,
|
||||||
|
} from "@repo/rdx-ui/lib/icons";
|
||||||
import { AvatarBadge } from "@repo/shadcn-ui/components/avatar";
|
import { AvatarBadge } from "@repo/shadcn-ui/components/avatar";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { Card, CardContent } from "@repo/shadcn-ui/components/card";
|
import { Card, CardContent } from "@repo/shadcn-ui/components/card";
|
||||||
@ -195,8 +201,14 @@ export function PersonDetails({
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EntityAction onClick={() => setHistoryId(item.id)}>
|
<EntityAction onClick={() => setHistoryId(item.id)}>
|
||||||
{t("history")}
|
{t("history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { FormLabel } from "@repo/rdx-ui/components/form-label";
|
import { FormLabel } from "@repo/rdx-ui/components/form-label";
|
||||||
import { PhoneLink } from "@repo/rdx-ui/components/phone-link";
|
import { PhoneLink } from "@repo/rdx-ui/components/phone-link";
|
||||||
import { PersonIcon } from "@repo/rdx-ui/lib/icons";
|
import { EyeIcon, PersonIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||||
import { useId, useState } from "react";
|
import { useId, useState } from "react";
|
||||||
@ -246,7 +246,17 @@ export function PeopleSection({
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (person) => (
|
render: (person) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("open"),
|
||||||
|
icon: <EyeIcon aria-hidden="true" />,
|
||||||
|
onClick: () => {
|
||||||
|
setSelection({ mode: "detail", id: person.id });
|
||||||
|
selectEntity(person.id);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EntityAction
|
<EntityAction
|
||||||
disabled={!person.active}
|
disabled={!person.active}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@ -258,14 +268,6 @@ export function PeopleSection({
|
|||||||
>
|
>
|
||||||
{assetTranslation("newWorkstation")}
|
{assetTranslation("newWorkstation")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
<EntityAction
|
|
||||||
onClick={() => {
|
|
||||||
setSelection({ mode: "detail", id: person.id });
|
|
||||||
selectEntity(person.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("open")}
|
|
||||||
</EntityAction>
|
|
||||||
</EntityRowActions>
|
</EntityRowActions>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { PrinterIcon } from "@repo/rdx-ui/lib/icons";
|
import { PencilIcon, PrinterIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -134,15 +134,20 @@ export function PrinterConfigurationsPage(props: PrinterConfigurationsPageProps)
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: ["removed", "never_configured"].includes(item.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("mail.history")}
|
{t("mail.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
{!["removed", "never_configured"].includes(item.values.state) && (
|
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
)}
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
<EntityAction className="text-destructive" onClick={() => setDeleteId(item.id)}>
|
||||||
{t("deleteRecord.title")}
|
{t("deleteRecord.title")}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { LayoutTemplateIcon } from "@repo/rdx-ui/lib/icons";
|
import { LayoutTemplateIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -120,10 +120,16 @@ export function ProcessTemplatesPage() {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (template) => (
|
render: (template) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(template.id),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setEditor(template.id)}>{t("edit")}</EntityAction>
|
|
||||||
<EntityAction
|
<EntityAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCopy(template.id);
|
setCopy(template.id);
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { ChevronLeftIcon, LayoutTemplateIcon } from "@repo/rdx-ui/lib/icons";
|
import { ChevronLeftIcon, EyeIcon, LayoutTemplateIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -155,10 +155,14 @@ export function ProcessesPage(props: ProcessesPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (p) => (
|
render: (p) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
<EntityAction onClick={() => setSelected(p.id)}>
|
label={t("actions")}
|
||||||
{t("processes.openDetail")}
|
primaryAction={{
|
||||||
</EntityAction>
|
label: t("processes.openDetail"),
|
||||||
|
icon: <EyeIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setSelected(p.id),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EntityAction
|
<EntityAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { KeyRoundIcon } from "@repo/rdx-ui/lib/icons";
|
import { KeyRoundIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -122,16 +122,18 @@ export function RemoteConnectionsPage(props: RemoteConnectionsPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (c) => (
|
render: (c) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(c.id),
|
||||||
|
disabled: isClosed(c.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(c.id)}>{t("remote.history")}</EntityAction>
|
<EntityAction onClick={() => setView(c.id)}>{t("remote.history")}</EntityAction>
|
||||||
<EntityAction
|
|
||||||
disabled={isClosed(c.values.state)}
|
|
||||||
onClick={() => setEditor(c.id)}
|
|
||||||
>
|
|
||||||
{t("edit")}
|
|
||||||
</EntityAction>
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
</EntityRowActions>
|
</EntityRowActions>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { FormLabel } from "@repo/rdx-ui/components/form-label";
|
import { FormLabel } from "@repo/rdx-ui/components/form-label";
|
||||||
import { MapPinIcon } from "@repo/rdx-ui/lib/icons";
|
import { MapPinIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||||
import { useId } from "react";
|
import { useId } from "react";
|
||||||
@ -85,10 +85,15 @@ export function SiteSection({ company }: { company: CompanyDTO }) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (site) => (
|
render: (site) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
<EntityAction disabled={mutation.isPending} onClick={() => setSelectedId(site.id)}>
|
label={t("actions")}
|
||||||
{t("edit")}
|
primaryAction={{
|
||||||
</EntityAction>
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setSelectedId(site.id),
|
||||||
|
disabled: mutation.isPending,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EntityAction disabled={mutation.isPending} onClick={() => mutation.mutate(site)}>
|
<EntityAction disabled={mutation.isPending} onClick={() => mutation.mutate(site)}>
|
||||||
{t(site.active ? "siteLifecycle.archive" : "siteLifecycle.restore")}
|
{t(site.active ? "siteLifecycle.archive" : "siteLifecycle.restore")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { LayersIcon } from "@repo/rdx-ui/lib/icons";
|
import { LayersIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
|
||||||
import { useId, useState } from "react";
|
import { useId, useState } from "react";
|
||||||
@ -223,11 +223,17 @@ export function SoftwareInstallationsPanel({
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: ["removed", "never_configured"].includes(item.values.state),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EntityAction onClick={() => setView(item.id)}>{t("mail.history")}</EntityAction>
|
<EntityAction onClick={() => setView(item.id)}>{t("mail.history")}</EntityAction>
|
||||||
{!["removed", "never_configured"].includes(item.values.state) && (
|
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
)}
|
|
||||||
<EntityAction className="text-destructive" onClick={() => setDeleting(item.id)}>
|
<EntityAction className="text-destructive" onClick={() => setDeleting(item.id)}>
|
||||||
{t("deleteRecord.title")}
|
{t("deleteRecord.title")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { KeyRoundIcon } from "@repo/rdx-ui/lib/icons";
|
import { KeyRoundIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -121,13 +121,19 @@ export function TeleworkAuthorizationsPage(props: TeleworkAuthorizationsPageProp
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("telework.history")}
|
{t("telework.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
</EntityRowActions>
|
</EntityRowActions>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { WorkProfileIcon } from "@repo/rdx-ui/lib/icons";
|
import { PencilIcon, WorkProfileIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -152,7 +152,14 @@ export function WorkProfilesPage(props: WorkProfilesPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction
|
<EntityAction
|
||||||
@ -188,7 +195,7 @@ export function WorkProfilesPage(props: WorkProfilesPageProps) {
|
|||||||
<EntityAction onClick={() => setView(item.id)}>
|
<EntityAction onClick={() => setView(item.id)}>
|
||||||
{t("profiles.history")}
|
{t("profiles.history")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
<EntityAction onClick={() => setEditor(item.id)}>{t("edit")}</EntityAction>
|
|
||||||
<EntityAction
|
<EntityAction
|
||||||
disabled={!item.values.active}
|
disabled={!item.values.active}
|
||||||
onClick={() => setLifecycle({ id: item.id, action: "deactivate" })}
|
onClick={() => setLifecycle({ id: item.id, action: "deactivate" })}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import {
|
|||||||
EntityStatus,
|
EntityStatus,
|
||||||
EntityTable,
|
EntityTable,
|
||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { UsersIcon } from "@repo/rdx-ui/lib/icons";
|
import { PencilIcon, UsersIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
@ -85,11 +85,16 @@ export function UserManagement() {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (user) => (
|
render: (user) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
menuVisible={!user.isInitialAdmin}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setSelection({ id: user.id, action: "edit" }),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setSelection({ id: user.id, action: "edit" })}>
|
|
||||||
{t("edit")}
|
|
||||||
</EntityAction>
|
|
||||||
{!user.isInitialAdmin && (
|
{!user.isInitialAdmin && (
|
||||||
<EntityAction
|
<EntityAction
|
||||||
onClick={() => setSelection({ id: user.id, action: "password" })}
|
onClick={() => setSelection({ id: user.id, action: "password" })}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { EntityRowActions, EntityStatus } from "@repo/rdx-ui/components/entity-list";
|
import { EntityRowActions, EntityStatus } from "@repo/rdx-ui/components/entity-list";
|
||||||
|
import { EyeIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { CatalogItemDTO } from "../../../../common";
|
import type { CatalogItemDTO } from "../../../../common";
|
||||||
@ -65,14 +66,20 @@ export function CatalogCell({
|
|||||||
case "actions":
|
case "actions":
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("view"),
|
||||||
|
icon: <EyeIcon aria-hidden="true" />,
|
||||||
|
onClick: onView,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<CatalogRowActions
|
<CatalogRowActions
|
||||||
editable={editable}
|
editable={editable}
|
||||||
item={item}
|
item={item}
|
||||||
onCopy={onCopy}
|
onCopy={onCopy}
|
||||||
onEdit={onEdit}
|
onEdit={onEdit}
|
||||||
onStatus={onStatus}
|
onStatus={onStatus}
|
||||||
onView={onView}
|
|
||||||
pending={pending}
|
pending={pending}
|
||||||
/>
|
/>
|
||||||
</EntityRowActions>
|
</EntityRowActions>
|
||||||
|
|||||||
@ -9,7 +9,6 @@ export function CatalogRowActions({
|
|||||||
pending,
|
pending,
|
||||||
onEdit,
|
onEdit,
|
||||||
onCopy,
|
onCopy,
|
||||||
onView,
|
|
||||||
onStatus,
|
onStatus,
|
||||||
}: {
|
}: {
|
||||||
item: CatalogItemDTO;
|
item: CatalogItemDTO;
|
||||||
@ -17,14 +16,12 @@ export function CatalogRowActions({
|
|||||||
pending: boolean;
|
pending: boolean;
|
||||||
onEdit: () => void;
|
onEdit: () => void;
|
||||||
onCopy: () => void;
|
onCopy: () => void;
|
||||||
onView: () => void;
|
|
||||||
onStatus: (active: boolean | null) => void;
|
onStatus: (active: boolean | null) => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation("resources");
|
const { t } = useTranslation("resources");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={onView}>{t("view")}</EntityAction>
|
|
||||||
{editable && <EntityAction onClick={onEdit}>{t("edit")}</EntityAction>}
|
{editable && <EntityAction onClick={onEdit}>{t("edit")}</EntityAction>}
|
||||||
{item.values.kind === "template" && <EntityAction onClick={onCopy}>{t("copy")}</EntityAction>}
|
{item.values.kind === "template" && <EntityAction onClick={onCopy}>{t("copy")}</EntityAction>}
|
||||||
<EntityAction disabled={pending} onClick={() => onStatus(!item.effectiveActive)}>
|
<EntityAction disabled={pending} onClick={() => onStatus(!item.effectiveActive)}>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import {
|
|||||||
} from "@repo/rdx-ui/components/entity-list";
|
} from "@repo/rdx-ui/components/entity-list";
|
||||||
import { ScrollableTabsList } from "@repo/rdx-ui/components/scrollable-tabs-list";
|
import { ScrollableTabsList } from "@repo/rdx-ui/components/scrollable-tabs-list";
|
||||||
import { characteristicsSummary } from "@repo/rdx-ui/lib/characteristics-summary";
|
import { characteristicsSummary } from "@repo/rdx-ui/lib/characteristics-summary";
|
||||||
import { LayersIcon } from "@repo/rdx-ui/lib/icons";
|
import { LayersIcon, PencilIcon } from "@repo/rdx-ui/lib/icons";
|
||||||
import { Button } from "@repo/shadcn-ui/components/button";
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
import { Tabs, TabsContent } from "@repo/shadcn-ui/components/tabs";
|
import { Tabs, TabsContent } from "@repo/shadcn-ui/components/tabs";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@ -176,15 +176,20 @@ export function ResourcesPage(props: ResourcesPageProps) {
|
|||||||
label: t("actions"),
|
label: t("actions"),
|
||||||
hideable: false,
|
hideable: false,
|
||||||
render: (item) => (
|
render: (item) => (
|
||||||
<EntityRowActions label={t("actions")}>
|
<EntityRowActions
|
||||||
|
label={t("actions")}
|
||||||
|
primaryAction={{
|
||||||
|
label: t("edit"),
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => setEditor(item.id),
|
||||||
|
disabled: !catalog.data,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{" "}
|
{" "}
|
||||||
<div className="contents">
|
<div className="contents">
|
||||||
<EntityAction onClick={() => setViewId(item.id)}>
|
<EntityAction onClick={() => setViewId(item.id)}>
|
||||||
{t("viewResource")}
|
{t("viewResource")}
|
||||||
</EntityAction>
|
</EntityAction>
|
||||||
<EntityAction disabled={!catalog.data} onClick={() => setEditor(item.id)}>
|
|
||||||
{t("edit")}
|
|
||||||
</EntityAction>
|
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
</EntityRowActions>
|
</EntityRowActions>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export function CountBadge({
|
|||||||
aria-label={count === undefined ? status : undefined}
|
aria-label={count === undefined ? status : undefined}
|
||||||
className={`min-w-6 px-1.5 tabular-nums ${className ?? ""}`}
|
className={`min-w-6 px-1.5 tabular-nums ${className ?? ""}`}
|
||||||
title={status}
|
title={status}
|
||||||
variant="secondary"
|
variant="link"
|
||||||
>
|
>
|
||||||
{count === undefined ? "—" : new Intl.NumberFormat(locale).format(count)}
|
{count === undefined ? "—" : new Intl.NumberFormat(locale).format(count)}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|||||||
@ -3,31 +3,11 @@ import { Button } from "@repo/shadcn-ui/components/button";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { ENTITY_PAGE_SIZES, entityListLabels, entityListPages } from "../lib/entity-list";
|
import { ENTITY_PAGE_SIZES, entityListLabels, entityListPages } from "../lib/entity-list";
|
||||||
import { EllipsisIcon } from "../lib/icons";
|
|
||||||
|
export { EntityRowActions } from "./entity-row-actions";
|
||||||
|
|
||||||
import { SelectWithAction } from "./select-with-action";
|
import { SelectWithAction } from "./select-with-action";
|
||||||
|
|
||||||
export function EntityRowActions({ children, label }: { children: ReactNode; label: string }) {
|
|
||||||
return (
|
|
||||||
<Menu.Root>
|
|
||||||
<Menu.Trigger
|
|
||||||
aria-label={label}
|
|
||||||
render={<Button size="icon-sm" variant="ghost" />}
|
|
||||||
title={label}
|
|
||||||
>
|
|
||||||
<EllipsisIcon aria-hidden="true" />
|
|
||||||
</Menu.Trigger>
|
|
||||||
<Menu.Portal>
|
|
||||||
<Menu.Positioner align="end" className="z-50" sideOffset={4}>
|
|
||||||
<Menu.Popup className="min-w-44 rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none">
|
|
||||||
{children}
|
|
||||||
</Menu.Popup>
|
|
||||||
</Menu.Positioner>
|
|
||||||
</Menu.Portal>
|
|
||||||
</Menu.Root>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EntityAction({
|
export function EntityAction({
|
||||||
children,
|
children,
|
||||||
onClick,
|
onClick,
|
||||||
|
|||||||
72
packages/rdx-ui/src/components/entity-row-actions.tsx
Normal file
72
packages/rdx-ui/src/components/entity-row-actions.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { Menu } from "@base-ui/react/menu";
|
||||||
|
import { Button } from "@repo/shadcn-ui/components/button";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@repo/shadcn-ui/components/tooltip";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
import { EllipsisIcon } from "../lib/icons";
|
||||||
|
|
||||||
|
export function EntityRowActions({
|
||||||
|
children,
|
||||||
|
label,
|
||||||
|
primaryAction,
|
||||||
|
menuVisible = children !== undefined,
|
||||||
|
}: {
|
||||||
|
children?: ReactNode;
|
||||||
|
label: string;
|
||||||
|
primaryAction?: {
|
||||||
|
label: string;
|
||||||
|
icon: ReactNode;
|
||||||
|
onClick: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
menuVisible?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="inline-flex items-center justify-end gap-1 whitespace-nowrap">
|
||||||
|
{primaryAction && (
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger
|
||||||
|
render={
|
||||||
|
<Button
|
||||||
|
aria-label={primaryAction.label}
|
||||||
|
disabled={primaryAction.disabled}
|
||||||
|
onClick={primaryAction.onClick}
|
||||||
|
size="icon-sm"
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{primaryAction.icon}
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>{primaryAction.label}</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
)}
|
||||||
|
{menuVisible && (
|
||||||
|
<Menu.Root>
|
||||||
|
<Menu.Trigger
|
||||||
|
aria-label={label}
|
||||||
|
render={<Button size="icon-sm" type="button" variant="ghost" />}
|
||||||
|
title={label}
|
||||||
|
>
|
||||||
|
<EllipsisIcon aria-hidden="true" />
|
||||||
|
</Menu.Trigger>
|
||||||
|
<Menu.Portal>
|
||||||
|
<Menu.Positioner align="end" className="z-50" sideOffset={4}>
|
||||||
|
<Menu.Popup className="min-w-44 rounded-md border bg-popover p-1 text-popover-foreground shadow-md outline-none">
|
||||||
|
{children}
|
||||||
|
</Menu.Popup>
|
||||||
|
</Menu.Positioner>
|
||||||
|
</Menu.Portal>
|
||||||
|
</Menu.Root>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import {
|
|||||||
EntityTable,
|
EntityTable,
|
||||||
} from "./components/entity-list";
|
} from "./components/entity-list";
|
||||||
import { entityListLabels, entityListPages, entityListRange } from "./lib/entity-list";
|
import { entityListLabels, entityListPages, entityListRange } from "./lib/entity-list";
|
||||||
|
import { PencilIcon } from "./lib/icons";
|
||||||
|
|
||||||
const items = Array.from({ length: 21 }, (_, index) => ({
|
const items = Array.from({ length: 21 }, (_, index) => ({
|
||||||
id: String(index),
|
id: String(index),
|
||||||
@ -219,6 +220,48 @@ test("acciones: disparador de solo icono con etiqueta accesible y menú contextu
|
|||||||
assert.equal(html.includes(">Acciones de Ana"), false);
|
assert.equal(html.includes(">Acciones de Ana"), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("acciones: operación principal visible con nombre accesible y secundarias en el menú", () => {
|
||||||
|
for (const label of ["Editar", "Edit"]) {
|
||||||
|
const html = renderToStaticMarkup(
|
||||||
|
<EntityRowActions
|
||||||
|
label="Acciones"
|
||||||
|
primaryAction={{ label, icon: <PencilIcon aria-hidden="true" />, onClick: () => undefined }}
|
||||||
|
>
|
||||||
|
<EntityAction onClick={() => undefined}>Eliminar</EntityAction>
|
||||||
|
</EntityRowActions>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.ok(html.includes(`aria-label="${label}"`));
|
||||||
|
assert.ok(html.includes('data-slot="tooltip-trigger"'));
|
||||||
|
assert.ok(html.includes('aria-haspopup="menu"'));
|
||||||
|
assert.ok(html.indexOf(`aria-label="${label}"`) < html.indexOf('aria-label="Acciones"'));
|
||||||
|
assert.equal(html.includes(`>${label}</button>`), false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("acciones: conserva la restricción de la principal y evita menús sin secundarias", () => {
|
||||||
|
const primaryAction = {
|
||||||
|
label: "Editar",
|
||||||
|
icon: <PencilIcon aria-hidden="true" />,
|
||||||
|
onClick: () => undefined,
|
||||||
|
disabled: true,
|
||||||
|
};
|
||||||
|
const html = renderToStaticMarkup(
|
||||||
|
<EntityRowActions label="Acciones" primaryAction={primaryAction} />
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.match(html, /<button[^>]*disabled=""/);
|
||||||
|
assert.equal(html.includes('aria-haspopup="menu"'), false);
|
||||||
|
|
||||||
|
const hidden = renderToStaticMarkup(
|
||||||
|
<EntityRowActions label="Acciones" menuVisible={false} primaryAction={primaryAction}>
|
||||||
|
{false}
|
||||||
|
</EntityRowActions>
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(hidden.includes('aria-haspopup="menu"'), false);
|
||||||
|
});
|
||||||
|
|
||||||
test("el estado activo se distingue en verde del inactivo", () => {
|
test("el estado activo se distingue en verde del inactivo", () => {
|
||||||
const active = renderToStaticMarkup(<EntityStatus active>Activa</EntityStatus>);
|
const active = renderToStaticMarkup(<EntityStatus active>Activa</EntityStatus>);
|
||||||
const inactive = renderToStaticMarkup(<EntityStatus active={false}>Inactiva</EntityStatus>);
|
const inactive = renderToStaticMarkup(<EntityStatus active={false}>Inactiva</EntityStatus>);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export {
|
|||||||
IconDeviceMobile as SmartphoneIcon,
|
IconDeviceMobile as SmartphoneIcon,
|
||||||
IconDeviceTablet as TabletIcon,
|
IconDeviceTablet as TabletIcon,
|
||||||
IconDots as EllipsisIcon,
|
IconDots as EllipsisIcon,
|
||||||
|
IconEye as EyeIcon,
|
||||||
IconFileCertificate as FileKeyIcon,
|
IconFileCertificate as FileKeyIcon,
|
||||||
IconFolder as FolderIcon,
|
IconFolder as FolderIcon,
|
||||||
IconGripVertical as GripVerticalIcon,
|
IconGripVertical as GripVerticalIcon,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user