Skip to content

Commit

Permalink
Validation warnings (#2019)
Browse files Browse the repository at this point in the history
* Add margin-top to StyledDetailWarnings

* Fix conditional rendering in EntityDetailProtocol component

* Classifications should be valid only for Concepts

* First version of the new warning messages

* Add parentT and the T lineage to the entities list

* Extend warning dictionaries

* Implement first version of the tbased warning logic

* Add message texts for T-based validation warnings

* Fix label text in warning messages

* Add some commented tbased validation code

* Typo in file name

* Add entity tags to Warning messages

* Fix searching props in response

* Improve validation messages

* Append territory lineage to loaded entities
  • Loading branch information
adammertel authored Apr 15, 2024
1 parent dceafc0 commit bdf93ac
Show file tree
Hide file tree
Showing 12 changed files with 529 additions and 28 deletions.
197 changes: 193 additions & 4 deletions packages/client/src/components/basic/Message/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { WarningTypeEnums } from "@shared/enums";
import { IEntity, IWarning } from "@shared/types";
import api from "api";
import { EntityTag } from "components/advanced";
import React, { useEffect, useState } from "react";
import { TiWarningOutline } from "react-icons/ti";
import { StyledMessage } from "./MessateStyles";
import { EntityColors } from "types";
import { getShortLabelByLetterCount } from "utils/utils";
import {
StyledMessage,
StyledMessageTValidationContent,
} from "./MessageStyles";
import theme from "Theme/theme";

interface Message {
warning: IWarning;
Expand All @@ -19,18 +26,126 @@ export const Message: React.FC<Message> = ({ warning, entities }) => {
pa: "Pseudo-Actant",
};

const [entity, setEntity] = useState<IEntity | false>(false);
const [extendedEntities, setExtendedEntities] = useState<
Record<string, IEntity>
>(entities ?? {});

useEffect((): void => {
const entitiesOut = [];
const newEntityIds: string[] = [];

async function getEntities(eids: string[]) {
const extractedEntities: Record<string, IEntity> = { ...entities };
for (const eid of eids) {
const entityRes = await api.entitiesGet(eid);
if (entityRes?.data && !entities?.[eid]) {
extractedEntities[eid] = entityRes.data;
}
}
setExtendedEntities(extractedEntities);
}

const isInEntities = (eid: string) => {
return entities && entities[eid] ? true : false;
};

warning?.validation?.propType?.forEach((eid) => {
if (eid && !isInEntities(eid)) {
newEntityIds.push(eid);
}
});

warning?.validation?.allowedEntities?.forEach((eid) => {
if (eid && !isInEntities(eid)) {
newEntityIds.push(eid);
}
});

if (newEntityIds.length > 0) {
getEntities(newEntityIds);
}
}, [warning, entities]);

const [entity, setEntity] = useState<IEntity | undefined>(undefined);

useEffect(() => {
if (warning.position?.entityId && entities) {
const entity = entities[warning.position.entityId];
if (entity) {
setEntity(entity);
} else {
setEntity(undefined);
}
}
}, [warning, entities]);

function getWarningMessage({ type, position }: IWarning): JSX.Element {
function renderEntityTags(entityIds: (string | undefined)[]): JSX.Element {
return (
<>
{entityIds.map((eid) => {
if (eid) {
const entity = extendedEntities?.[eid];
if (entity) {
return (
<div style={{ marginRight: "2px", display: "inline-flex" }}>
<EntityTag key={entity.id} entity={entity} />
</div>
);
}
}
return <>{eid}</>;
})}
</>
);
}
function renderValidationLabel(warning: IWarning): JSX.Element {
if (warning.validation?.detail) {
return (
<span>
{" "}
[<i>{warning.validation?.detail}</i>]
</span>
);
} else {
return <></>;
}
}

function renderEntityClasses(
entityClasses: string[] | undefined
): JSX.Element {
if (entityClasses) {
return (
<>
{entityClasses.map((entityClass, index) => {
const classItem = EntityColors[entityClass];
const colorName = classItem?.color ?? "transparent";
const color = theme.color[colorName] as string;

return (
<span key={index}>
<span
style={{
backgroundColor: color,
paddingLeft: "2px",
paddingRight: "2px",
}}
>
{classItem.entityClass}
</span>
{index < entityClasses.length - 1 ? ", " : ""}
</span>
);
})}
</>
);
} else {
return <></>;
}
}

function getWarningMessage(): JSX.Element {
const { type, position } = warning;
const positionName = position?.subSection
? ` - ${positionObject[position.subSection]}`
: "";
Expand Down Expand Up @@ -132,10 +247,84 @@ export const Message: React.FC<Message> = ({ warning, entities }) => {
);
case WarningTypeEnums.MAEE:
return <b>Missing Action/event equivalent</b>;

case WarningTypeEnums.LM:
return <b>Missing label language attribute</b>;

case WarningTypeEnums.PSM:
return <b>Missing part of speech attribute</b>;

// T-based validations
case WarningTypeEnums.TVEP:
return (
<StyledMessageTValidationContent>
{renderEntityTags([warning?.position?.entityId])} should have a
property {renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
case WarningTypeEnums.TVEPT:
return (
<StyledMessageTValidationContent>
T-based validations:
{renderEntityTags([warning?.position?.entityId])} is missing a
required property with type{" "}
{renderEntityTags(warning.validation?.propType ?? [])}
{renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
case WarningTypeEnums.TVEPV:
const classAllowed =
warning.validation?.allowedClasses &&
warning.validation?.allowedClasses.length > 0;
return (
<StyledMessageTValidationContent>
{renderEntityTags([warning?.position?.entityId])} has a wrong
property type {renderEntityTags(warning.validation?.propType ?? [])}
{classAllowed && (
<>
- should be of type{" "}
{renderEntityClasses(warning.validation?.allowedClasses)}
</>
)}
{!classAllowed && (
<>
- should be of values{" "}
{renderEntityTags(warning.validation?.allowedEntities ?? [])}
</>
)}
{renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
case WarningTypeEnums.TVEC:
return (
<StyledMessageTValidationContent>
{renderEntityTags([warning?.position?.entityId])} should have a
classification {renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
case WarningTypeEnums.TVECE:
return (
<StyledMessageTValidationContent>
{renderEntityTags([warning?.position?.entityId])} is not classified
with valid entity{" "}
{renderEntityClasses(warning.validation?.allowedClasses)}{" "}
{renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
case WarningTypeEnums.TVER:
return (
<StyledMessageTValidationContent>
{renderEntityTags([warning?.position?.entityId])} should have a
reference {renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
case WarningTypeEnums.TVERE:
return (
<StyledMessageTValidationContent>
{renderEntityTags([warning?.position?.entityId])} is not referenced
to a valid entity {renderValidationLabel(warning)}
</StyledMessageTValidationContent>
);
default:
return <></>;
}
Expand All @@ -146,7 +335,7 @@ export const Message: React.FC<Message> = ({ warning, entities }) => {
<div style={{ width: "3rem" }}>
<TiWarningOutline size={20} style={{ marginRight: "0.5rem" }} />
</div>
{getWarningMessage(warning)}
{getWarningMessage()}
</StyledMessage>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ export const StyledMessage = styled.div<StyledMessage>`
font-size: ${({ theme }) => theme.fontSize["xs"]};
border: 1.5px solid ${({ theme }) => theme.color["warningBorder"]};
`;

export const StyledMessageTValidationContent = styled.div`
display: inline;
items-align: center;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export const EntityDetailProtocol: React.FC<EntityDetailProtocol> = ({

<StyledLabel>Guidelines resource</StyledLabel>
<StyledValue>
{guidelinesResource.length > 0 ? (
{guidelinesResource && guidelinesResource.length > 0 ? (
<StyledTagWrap>
<EntityTag
entity={entities[guidelinesResource]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const StyledDetailWarnings = styled.div`
display: grid;
grid-gap: ${({ theme }) => theme.space["1"]};
grid-auto-flow: row;
margin-top: ${({ theme }) => theme.space[4]};
`;

export const StyledDetailContentRowValueID = styled.div`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ export const EntityDetailValidationRule: React.FC<
return allowedEntities !== undefined && allowedEntities.length > 0;
}, [allowedEntities]);

const onlyResourceAllowed = tieType === EProtocolTieType.Reference;
const allowedEntitiesClasses = useMemo<EntityEnums.Class[]>(() => {
if (tieType === EProtocolTieType.Classification) {
return [EntityEnums.Class.Concept];
}
if (tieType === EProtocolTieType.Reference) {
return [EntityEnums.Class.Resource];
}
return classesAll;
}, [tieType]);

return (
<StyledBorderLeft>
Expand Down Expand Up @@ -138,6 +146,8 @@ export const EntityDetailValidationRule: React.FC<
updateValidationRule({
tieType: EProtocolTieType.Classification,
propType: [],
allowedClasses: [],
allowedEntities: [],
}),
selected: tieType === EProtocolTieType.Classification,
},
Expand Down Expand Up @@ -195,7 +205,7 @@ export const EntityDetailValidationRule: React.FC<
)}

{/* Allowed classes */}
{!onlyResourceAllowed && (
{tieType === EProtocolTieType.Property && (
<>
<StyledLabel>Allowed E types</StyledLabel>
<Dropdown.Multi.Entity
Expand All @@ -212,7 +222,9 @@ export const EntityDetailValidationRule: React.FC<

{/* Allowed entities */}
<StyledLabel>
{!onlyResourceAllowed ? "Allowed E values" : "Allowed Resources"}
{tieType === EProtocolTieType.Classification && "Allowed Concepts"}
{tieType === EProtocolTieType.Reference && "Allowed Resources"}
{tieType === EProtocolTieType.Property && "Allowed E values"}
</StyledLabel>
<StyledFlexList>
{allowedEntities?.map((entityId, key) => (
Expand All @@ -234,9 +246,7 @@ export const EntityDetailValidationRule: React.FC<
))}
{!(!userCanEdit && allowedEntities && allowedEntities.length > 0) && (
<EntitySuggester
categoryTypes={
!onlyResourceAllowed ? classesAll : [EntityEnums.Class.Resource]
}
categoryTypes={allowedEntitiesClasses}
excludedActantIds={allowedEntities}
onPicked={(entity) => {
updateValidationRule({
Expand All @@ -250,6 +260,7 @@ export const EntityDetailValidationRule: React.FC<
/>
)}
</StyledFlexList>

{/* Detail */}
<StyledLabel>Detail / Notes</StyledLabel>
<Input
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const EntityDetailValidationText: React.FC<
const renderEntityClassList = useCallback(
(classList: EntityEnums.ExtendedClass[]) => {
if (classList.includes(EntityEnums.Extension.Any)) {
return <StyledSentenceEntity key={"any"}>Any</StyledSentenceEntity>;
return <StyledSentenceEntity key={"any"}></StyledSentenceEntity>;
}
if (
classList.includes(EntityEnums.Extension.Empty) ||
Expand Down
5 changes: 3 additions & 2 deletions packages/server/src/models/entity/entity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Db } from "@service/rethink";
import Entity from "./entity";
import Statement, {
StatementActant,
StatementAction,
StatementTerritory,
} from "@models/statement/statement";
import { clean } from "@modules/common.test";
Expand Down Expand Up @@ -101,7 +102,7 @@ describe("test Entity.beforeSave", () => {
props: [
new Prop({
type: new PropSpec({
entityId: notTpl.id
entityId: notTpl.id,
}),
}),
],
Expand All @@ -128,7 +129,7 @@ describe("test Entity.beforeSave", () => {
props: [
new Prop({
type: new PropSpec({
entityId: tpl.id
entityId: tpl.id,
}),
}),
],
Expand Down
Loading

0 comments on commit bdf93ac

Please sign in to comment.