Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,109 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Loading from "~/components/ui/loaders/Loading";
|
||||
import { EntitiesApi } from "~/utils/api/.server/EntitiesApi";
|
||||
import { RowsApi } from "~/utils/api/.server/RowsApi";
|
||||
import { useAppOrAdminData } from "~/utils/data/useAppOrAdminData";
|
||||
import { EntityWithDetails } from "~/utils/db/entities/entities.db.server";
|
||||
import { RowWithDetails } from "~/utils/db/entities/rows.db.server";
|
||||
import { getEntityPermission, getUserHasPermission } from "~/utils/helpers/PermissionsHelper";
|
||||
import RowForm from "../../../components/entities/rows/RowForm";
|
||||
import RowTags from "../../../components/entities/rows/RowTags";
|
||||
import { useFetcher } from "react-router";
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
onUpdated?: (row: RowWithDetails) => void;
|
||||
onDeleted?: () => void;
|
||||
allEntities: EntityWithDetails[];
|
||||
}
|
||||
export default function RowEditFetcher({ url, onUpdated, allEntities, onDeleted }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const fetcher = useFetcher<{
|
||||
rowData?: RowsApi.GetRowData;
|
||||
routes?: EntitiesApi.Routes;
|
||||
updatedRow?: RowsApi.GetRowData;
|
||||
relationshipRows?: RowsApi.GetRelationshipRowsData;
|
||||
}>();
|
||||
const appOrAdminData = useAppOrAdminData();
|
||||
|
||||
const [data, setData] = useState<{
|
||||
rowData?: RowsApi.GetRowData;
|
||||
routes?: EntitiesApi.Routes;
|
||||
updatedRow?: RowsApi.GetRowData;
|
||||
relationshipRows?: RowsApi.GetRelationshipRowsData;
|
||||
}>();
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
fetcher.load(url);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [url]);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
if (fetcher.data?.rowData && fetcher.data?.routes) {
|
||||
setData(fetcher.data);
|
||||
}
|
||||
if (fetcher.data?.updatedRow?.item && onUpdated) {
|
||||
onUpdated(fetcher.data.updatedRow.item);
|
||||
}
|
||||
// if (fetcher.data?.deleted && onDeleted) {
|
||||
// onDeleted();
|
||||
// }
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fetcher.data]);
|
||||
|
||||
function onSubmit(formData: FormData) {
|
||||
fetcher.submit(formData, {
|
||||
action: url,
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
function onDelete() {
|
||||
const formData = new FormData();
|
||||
formData.set("action", "delete");
|
||||
formData.set("id", data?.rowData?.item.id ?? "");
|
||||
fetcher.submit(formData, {
|
||||
action: url,
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!fetcher.data ? (
|
||||
<Loading small loading />
|
||||
) : !data?.rowData ? (
|
||||
<div>{t("models.row.empty.noData")}</div>
|
||||
) : data ? (
|
||||
<div>
|
||||
{data.routes && (
|
||||
<div className="space-y-2">
|
||||
{data.rowData.item.tags.length > 0 && <RowTags items={data.rowData.item.tags} />}
|
||||
<RowForm
|
||||
allEntities={allEntities}
|
||||
entity={data.rowData.entity}
|
||||
routes={data.routes}
|
||||
item={data.rowData.item}
|
||||
editing={true}
|
||||
canDelete={getUserHasPermission(appOrAdminData, getEntityPermission(data.rowData.entity, "delete")) && data.rowData.rowPermissions.canDelete}
|
||||
canUpdate={getUserHasPermission(appOrAdminData, getEntityPermission(data.rowData.entity, "update")) && data.rowData.rowPermissions.canUpdate}
|
||||
onSubmit={onSubmit}
|
||||
onDelete={onDelete}
|
||||
relationshipRows={data.relationshipRows}
|
||||
state={{
|
||||
loading: fetcher.state === "loading",
|
||||
submitting: fetcher.state === "submitting",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div>{t("shared.unknownError")}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import { useFetcher, useSearchParams } from "react-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import ButtonPrimary from "~/components/ui/buttons/ButtonPrimary";
|
||||
import ButtonSecondary from "~/components/ui/buttons/ButtonSecondary";
|
||||
import InputFilters from "~/components/ui/input/InputFilters";
|
||||
import SlideOverWideEmpty from "~/components/ui/slideOvers/SlideOverWideEmpty";
|
||||
import { EntitiesApi } from "~/utils/api/.server/EntitiesApi";
|
||||
import { RowsApi } from "~/utils/api/.server/RowsApi";
|
||||
import type { EntityWithDetails } from "~/utils/db/entities/entities.db.server";
|
||||
import type { RowWithDetails } from "~/utils/db/entities/rows.db.server";
|
||||
import EntityHelper from "~/utils/helpers/EntityHelper";
|
||||
import RowNewFetcher from "./RowNewFetcher";
|
||||
import RowsList from "~/components/entities/rows/RowsList";
|
||||
import type { EntityViewWithDetails } from "~/utils/db/entities/entityViews.db.server";
|
||||
import { RowDisplayDefaultProperty } from "~/utils/helpers/PropertyHelper";
|
||||
import Loading from "~/components/ui/loaders/Loading";
|
||||
import { useLocaleFormatters } from "~/locale/format";
|
||||
import { translateEntityTitle, translateEntityTitlePlural } from "~/utils/i18n/translateLabel";
|
||||
|
||||
interface Props {
|
||||
currentView: EntityViewWithDetails | null;
|
||||
listUrl: string;
|
||||
newUrl: string;
|
||||
parentEntity?: EntityWithDetails;
|
||||
onSelected: (rows: RowWithDetails[]) => void;
|
||||
multipleSelection?: boolean;
|
||||
allEntities: EntityWithDetails[];
|
||||
}
|
||||
export default function RowListFetcher({ currentView, listUrl, newUrl, parentEntity, onSelected, multipleSelection, allEntities }: Props) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const formatters = useLocaleFormatters();
|
||||
const fetcher = useFetcher<{ rowsData: RowsApi.GetRowsData; routes: EntitiesApi.Routes }>();
|
||||
const [data, setData] = useState<{ rowsData: RowsApi.GetRowsData; routes: EntitiesApi.Routes }>();
|
||||
const [adding, setAdding] = useState(false);
|
||||
const [rows, setRows] = useState<RowWithDetails[]>([]);
|
||||
const [selectedRows, setSelectedRows] = useState<RowWithDetails[]>([]);
|
||||
const [searchParams] = useSearchParams();
|
||||
const createTitle = data?.rowsData?.entity
|
||||
? t("models.row.actions.createForEntity", { entity: translateEntityTitle(t, i18n, data.rowsData.entity) })
|
||||
: t("shared.create");
|
||||
|
||||
useEffect(() => {
|
||||
fetcher.load(listUrl);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [listUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentView) {
|
||||
searchParams.set("v", currentView.name);
|
||||
}
|
||||
fetcher.load(listUrl + "?" + searchParams.toString());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchParams, currentView]);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetcher.data) {
|
||||
const data: { rowsData: RowsApi.GetRowsData } = fetcher.data;
|
||||
setData(fetcher.data);
|
||||
setRows(data.rowsData.items);
|
||||
const selectedRowIds = new Set(selectedRows.map((row) => row.id));
|
||||
setSelectedRows(data.rowsData.items.filter((row) => selectedRowIds.has(row.id)));
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [fetcher.data]);
|
||||
|
||||
function onRowsSelected(rows: RowWithDetails[]) {
|
||||
setSelectedRows(rows);
|
||||
}
|
||||
|
||||
function onCreated(row: RowWithDetails) {
|
||||
setRows([row, ...rows]);
|
||||
setSelectedRows([row, ...selectedRows]);
|
||||
setAdding(false);
|
||||
}
|
||||
|
||||
function onConfirm(rows: RowWithDetails[]) {
|
||||
onSelected(rows);
|
||||
}
|
||||
|
||||
function getSelectionLabel() {
|
||||
const entity = data?.rowsData?.entity;
|
||||
if (!entity) {
|
||||
return t("shared.select");
|
||||
}
|
||||
const entityLabel = selectedRows.length === 1 ? translateEntityTitle(t, i18n, entity) : translateEntityTitlePlural(t, i18n, entity);
|
||||
return t("models.row.selection.selectRows", {
|
||||
count: selectedRows.length,
|
||||
formattedCount: formatters.number(selectedRows.length),
|
||||
entity: entityLabel,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!fetcher.data ? (
|
||||
<Loading small loading />
|
||||
) : !data?.rowsData?.entity ? (
|
||||
<div className="border-border relative block w-full cursor-not-allowed rounded-lg border-2 border-dashed p-4 text-center">
|
||||
{t("models.row.loadingRows")}
|
||||
</div>
|
||||
) : !data?.rowsData ? (
|
||||
<div>{t("models.row.empty.noData")}</div>
|
||||
) : data?.rowsData ? (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between space-x-2">
|
||||
{/* <div className="text-lg font-bold text-foreground">{t(data.rowsData?.entity.titlePlural)}</div> */}
|
||||
<ButtonPrimary type="button" onClick={() => onConfirm(selectedRows)} disabled={selectedRows.length > 1 && !multipleSelection}>
|
||||
{getSelectionLabel()}
|
||||
</ButtonPrimary>
|
||||
<div className="flex space-x-2">
|
||||
<InputFilters filters={EntityHelper.getFilters({ t, entity: data.rowsData.entity })} />
|
||||
<ButtonSecondary type="button" onClick={() => setAdding(true)}>
|
||||
+
|
||||
{/* {t("shared.new")}
|
||||
<span className="ml-1 lowercase">{t(data.rowsData?.entity.title)}</span> */}
|
||||
</ButtonSecondary>
|
||||
</div>
|
||||
</div>
|
||||
<RowsList
|
||||
view={(currentView?.layout ?? "table") as "table" | "board" | "grid" | "card"}
|
||||
currentView={currentView}
|
||||
entity={data.rowsData.entity}
|
||||
items={rows}
|
||||
pagination={data.rowsData.pagination}
|
||||
selectedRows={selectedRows}
|
||||
onSelected={onRowsSelected}
|
||||
readOnly={true}
|
||||
// routes={data.routes}
|
||||
ignoreColumns={[RowDisplayDefaultProperty.FOLIO]}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div>{t("shared.unknownError")}</div>
|
||||
)}
|
||||
<SlideOverWideEmpty
|
||||
title={createTitle}
|
||||
className="max-w-md"
|
||||
open={adding}
|
||||
onClose={() => setAdding(false)}
|
||||
>
|
||||
<RowNewFetcher url={newUrl} parentEntity={parentEntity} onCreated={onCreated} allEntities={allEntities} />
|
||||
</SlideOverWideEmpty>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CheckPlanFeatureLimit from "~/components/core/settings/subscription/CheckPlanFeatureLimit";
|
||||
import Loading from "~/components/ui/loaders/Loading";
|
||||
import { EntitiesApi } from "~/utils/api/.server/EntitiesApi";
|
||||
import { RowsApi } from "~/utils/api/.server/RowsApi";
|
||||
import { EntityWithDetails } from "~/utils/db/entities/entities.db.server";
|
||||
import { RowWithDetails } from "~/utils/db/entities/rows.db.server";
|
||||
import RowForm from "../../../components/entities/rows/RowForm";
|
||||
import { useFetcher } from "react-router";
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
parentEntity?: EntityWithDetails;
|
||||
onCreated?: (row: RowWithDetails) => void;
|
||||
allEntities: EntityWithDetails[];
|
||||
customSearchParams?: URLSearchParams;
|
||||
// onSelected: (entity: EntityWithDetails, item: RowWithDetails) => void;
|
||||
}
|
||||
export default function RowNewFetcher({ url, parentEntity, onCreated, allEntities, customSearchParams }: Props) {
|
||||
const { t } = useTranslation();
|
||||
const fetcher = useFetcher<{
|
||||
newRow?: RowWithDetails;
|
||||
entityData?: EntitiesApi.GetEntityData;
|
||||
routes?: EntitiesApi.Routes;
|
||||
relationshipRows?: RowsApi.GetRelationshipRowsData;
|
||||
}>();
|
||||
// const actionData = useActionData<{ newRow?: RowWithDetails }>();
|
||||
|
||||
// useEffect(() => {
|
||||
// if (actionData?.newRow && onCreated) {
|
||||
// onCreated(actionData.newRow);
|
||||
// }
|
||||
// }, [actionData, onCreated]);
|
||||
|
||||
const [data, setData] = useState<{
|
||||
newRow?: RowWithDetails;
|
||||
entityData?: EntitiesApi.GetEntityData;
|
||||
routes?: EntitiesApi.Routes;
|
||||
relationshipRows?: RowsApi.GetRelationshipRowsData;
|
||||
}>();
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.newRow && onCreated) {
|
||||
onCreated(data.newRow);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
fetcher.load(url);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [url]);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetcher.data) {
|
||||
setData(fetcher.data);
|
||||
}
|
||||
}, [fetcher.data]);
|
||||
|
||||
function onSubmit(formData: FormData) {
|
||||
fetcher.submit(formData, {
|
||||
action: url,
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!fetcher.data ? (
|
||||
<Loading small loading />
|
||||
) : !data?.entityData ? (
|
||||
<div>{t("models.row.empty.noData")}</div>
|
||||
) : data ? (
|
||||
<CheckPlanFeatureLimit item={data.entityData.featureUsageEntity}>
|
||||
{data.routes && (
|
||||
<RowForm
|
||||
entity={data.entityData.entity}
|
||||
routes={data.routes}
|
||||
parentEntity={parentEntity}
|
||||
onSubmit={onSubmit}
|
||||
onCreatedRedirect={undefined}
|
||||
allEntities={allEntities}
|
||||
relationshipRows={data.relationshipRows}
|
||||
state={{
|
||||
loading: fetcher.state === "loading",
|
||||
submitting: fetcher.state === "submitting",
|
||||
}}
|
||||
customSearchParams={customSearchParams}
|
||||
/>
|
||||
)}
|
||||
</CheckPlanFeatureLimit>
|
||||
) : (
|
||||
<div>{t("shared.unknownError")}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import SlideOverWideEmpty from "~/components/ui/slideOvers/SlideOverWideEmpty";
|
||||
import { EntitiesApi } from "~/utils/api/.server/EntitiesApi";
|
||||
import { RowsApi } from "~/utils/api/.server/RowsApi";
|
||||
import { EntityWithDetails } from "~/utils/db/entities/entities.db.server";
|
||||
import { RowWithDetails } from "~/utils/db/entities/rows.db.server";
|
||||
import EntityHelper from "~/utils/helpers/EntityHelper";
|
||||
import RowNewFetcher from "./RowNewFetcher";
|
||||
import InputSelector from "~/components/ui/input/InputSelector";
|
||||
import RowModel from "../repositories/RowModel";
|
||||
import { useFetcher } from "react-router";
|
||||
import { translateEntityTitle } from "~/utils/i18n/translateLabel";
|
||||
|
||||
interface Props {
|
||||
entity: EntityWithDetails;
|
||||
// listUrl: string;
|
||||
// newUrl: string;
|
||||
routes: EntitiesApi.Routes;
|
||||
allEntities: EntityWithDetails[];
|
||||
initial?: string;
|
||||
onSelected: (row: RowWithDetails) => void;
|
||||
onClear?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
export default function RowSelectorFetcher({ entity, routes, allEntities, initial, onSelected, onClear, className }: Props) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const fetcher = useFetcher<{ rowsData: RowsApi.GetRowsData; routes: EntitiesApi.Routes }>();
|
||||
const entityLabel = translateEntityTitle(t, i18n, entity);
|
||||
|
||||
const listUrl = EntityHelper.getRoutes({ routes, entity })?.list ?? "";
|
||||
const newUrl = EntityHelper.getRoutes({ routes, entity })?.new ?? "";
|
||||
|
||||
const [selected, setSelected] = useState<string | null>(initial ?? null);
|
||||
|
||||
const [data, setData] = useState<{ rowsData: RowsApi.GetRowsData; routes: EntitiesApi.Routes }>();
|
||||
const [adding, setAdding] = useState(false);
|
||||
const [rows, setRows] = useState<RowWithDetails[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const item = rows.find((f) => f.id === selected);
|
||||
if (item && item.id !== initial) {
|
||||
onSelected(item);
|
||||
}
|
||||
if (selected === "{new}") {
|
||||
setAdding(true);
|
||||
// setSelected(undefined);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selected]);
|
||||
|
||||
useEffect(() => {
|
||||
fetcher.load(listUrl);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [listUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
fetcher.load(listUrl + `?view=null&pageSize=-1`);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (fetcher.data) {
|
||||
const data: { rowsData: RowsApi.GetRowsData } = fetcher.data;
|
||||
setData(fetcher.data);
|
||||
setRows(data.rowsData.items);
|
||||
}
|
||||
}, [fetcher.data]);
|
||||
|
||||
function onCreated(row: RowWithDetails) {
|
||||
setRows([row, ...rows]);
|
||||
setSelected(row.id);
|
||||
setAdding(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<InputSelector
|
||||
title={entityLabel}
|
||||
disabled={!data}
|
||||
isLoading={!data || fetcher.state === "loading"}
|
||||
options={[
|
||||
{
|
||||
value: "{new}",
|
||||
name: t("models.row.actions.addForEntity", { entity: entityLabel }),
|
||||
},
|
||||
...(rows.map((f) => {
|
||||
const model = new RowModel(f);
|
||||
return {
|
||||
value: model.row.id,
|
||||
name: model.toString(),
|
||||
};
|
||||
}) ?? []),
|
||||
]}
|
||||
value={selected ?? undefined}
|
||||
setValue={(e) => setSelected(e?.toString() ?? "")}
|
||||
hint={
|
||||
<>
|
||||
{onClear && selected && (
|
||||
<button
|
||||
type="button"
|
||||
className="text-muted-foreground text-xs hover:underline"
|
||||
onClick={() => {
|
||||
setSelected(null);
|
||||
onClear();
|
||||
}}
|
||||
>
|
||||
{t("shared.clear")}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<SlideOverWideEmpty
|
||||
title={
|
||||
data?.rowsData?.entity
|
||||
? t("models.row.actions.createForEntity", { entity: translateEntityTitle(t, i18n, data.rowsData.entity) })
|
||||
: t("shared.create")
|
||||
}
|
||||
className="max-w-md"
|
||||
open={adding}
|
||||
onClose={() => {
|
||||
setAdding(false);
|
||||
if (selected === "{new}") {
|
||||
setSelected(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<RowNewFetcher url={newUrl} parentEntity={entity} onCreated={onCreated} allEntities={allEntities} />
|
||||
</SlideOverWideEmpty>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const sourcePath = path.resolve(process.cwd(), "apps/web/app/modules/rows/fetchers/RowListFetcher.tsx");
|
||||
|
||||
describe("RowListFetcher i18n", () => {
|
||||
it("uses a semantic plural key for selected row counts", () => {
|
||||
const source = fs.readFileSync(sourcePath, "utf8");
|
||||
|
||||
expect(source).toContain("models.row.selection.selectRows");
|
||||
expect(source).toContain("models.row.actions.createForEntity");
|
||||
expect(source).toContain("models.row.loadingRows");
|
||||
expect(source).toContain("formatters.number(selectedRows.length)");
|
||||
expect(source).toContain("translateEntityTitle");
|
||||
expect(source).toContain("translateEntityTitlePlural");
|
||||
expect(source).not.toContain("toLocaleLowerCase");
|
||||
expect(source).not.toContain('{t("shared.select")} {selectedRows.length}');
|
||||
expect(source).not.toContain('{t("shared.select")} 1');
|
||||
expect(source).not.toContain('{t("shared.loading")}...');
|
||||
expect(source).not.toContain("ui.autogenerated");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const sourcePath = path.resolve(process.cwd(), "apps/web/app/modules/rows/fetchers/RowSelectorFetcher.tsx");
|
||||
|
||||
describe("RowSelectorFetcher i18n", () => {
|
||||
it("uses entity translation helpers for dynamic labels", () => {
|
||||
const source = fs.readFileSync(sourcePath, "utf8");
|
||||
|
||||
expect(source).toContain("translateEntityTitle");
|
||||
expect(source).toContain("const { t, i18n } = useTranslation()");
|
||||
expect(source).toContain("entityLabel");
|
||||
expect(source).toContain("models.row.actions.addForEntity");
|
||||
expect(source).toContain("models.row.actions.createForEntity");
|
||||
expect(source).not.toContain("t(entity.title)");
|
||||
expect(source).not.toContain("t(data?.rowsData?.entity.title");
|
||||
expect(source).not.toContain("` - ${t(\"shared.add\")} ${entityLabel} - `");
|
||||
expect(source).not.toContain('t("shared.create") + " "');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user