Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions Website/components/datamodelview/Attributes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { columnMatchesSearch } from "@/lib/columnSearch"
import { EntityType, AttributeType } from "@/lib/Types"
import { useState, useEffect } from "react"
import { AttributeDetails } from "./entity/AttributeDetails"
Expand All @@ -14,7 +15,7 @@ import LookupAttribute from "./attributes/LookupAttribute"
import StatusAttribute from "./attributes/StatusAttribute"
import StringAttribute from "./attributes/StringAttribute"
import React from "react"
import { highlightMatch } from "../datamodelview/List";
import { highlightMatch, highlightTypeMatch } from "@/lib/searchHighlight";
import { Box, Button, FormControl, InputAdornment, InputLabel, MenuItem, Select, Table, TableBody, TableCell, TableHead, TableRow, TextField, Tooltip, Typography, useTheme } from "@mui/material"
import { ClearRounded, SearchRounded, Visibility, VisibilityOff, ArrowUpwardRounded, ArrowDownwardRounded } from "@mui/icons-material"
import { useEntityFiltersDispatch } from "@/contexts/EntityFiltersContext"
Expand Down Expand Up @@ -105,7 +106,7 @@ export const Attributes = ({ entity, search = "", onVisibleCountChange }: IAttri
// Also filter by parent search prop if provided
if (search && search.length >= 3) {
const query = search.toLowerCase()
filteredAttributes = filteredAttributes.filter(attr => attributeMatchesSearch(attr, query))
filteredAttributes = filteredAttributes.filter(attr => columnMatchesSearch(attr, query, searchScope))
}

if (hideStandardFields) filteredAttributes = filteredAttributes.filter(attr => (attr.IsCustomAttribute || attr.IsStandardFieldModified) && !attr.SchemaName.endsWith("Base"));
Expand Down Expand Up @@ -151,6 +152,8 @@ export const Attributes = ({ entity, search = "", onVisibleCountChange }: IAttri
const highlightTerm = searchQuery ||
(search && (searchScope.columnNames || searchScope.columnDescriptions || searchScope.columnDataTypes) ? search : "");

const typeHighlightTerm = searchQuery || (searchScope.columnDataTypes ? search : "");

// Notify parent of visible count changes
useEffect(() => {
onVisibleCountChange?.(sortedAttributes.length);
Expand Down Expand Up @@ -415,7 +418,7 @@ export const Attributes = ({ entity, search = "", onVisibleCountChange }: IAttri
<TableCell className="break-words py-1 md:py-1.5 text-xs md:text-sm">
{highlightMatch(attribute.SchemaName, highlightTerm)}
</TableCell>
<TableCell className="break-words py-1 md:py-1.5">{getAttributeComponent(entity, attribute, highlightMatch, highlightTerm)}</TableCell>
<TableCell className="break-words py-1 md:py-1.5">{getAttributeComponent(entity, attribute, highlightTypeMatch, typeHighlightTerm)}</TableCell>
<TableCell className="py-1 md:py-1.5"><AttributeDetails entityName={entity.SchemaName} attribute={attribute} isEntityAuditEnabled={entity.IsAuditEnabled} /></TableCell>
<TableCell className="break-words py-1 md:py-1.5 text-xs md:text-sm">
{highlightMatch(attribute.Description ?? "", highlightTerm)}
Expand All @@ -441,20 +444,20 @@ function getAttributeComponent(entity: EntityType, attribute: AttributeType, hig
case 'GenericAttribute':
return <GenericAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'IntegerAttribute':
return <IntegerAttribute key={key} attribute={attribute} />;
return <IntegerAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'LookupAttribute':
return <LookupAttribute key={key} attribute={attribute} />;
return <LookupAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'DecimalAttribute':
return <DecimalAttribute key={key} attribute={attribute} />;
return <DecimalAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'StatusAttribute':
return <StatusAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'StringAttribute':
return <StringAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'BooleanAttribute':
return <BooleanAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
case 'FileAttribute':
return <FileAttribute key={key} attribute={attribute} />;
return <FileAttribute key={key} attribute={attribute} highlightMatch={highlightMatch} highlightTerm={highlightTerm} />;
default:
return null;
}
}
}
10 changes: 2 additions & 8 deletions Website/components/datamodelview/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@ interface IListProps {
entityActiveTabs: Map<string, number>;
}

// Helper to highlight search matches
export function highlightMatch(text: string, search: string) {
if (!search || search.length < 3) return text;
const idx = text.toLowerCase().indexOf(search.toLowerCase());
if (idx === -1) return text;
return <>{text.slice(0, idx)}<mark className="bg-yellow-200 text-black px-0.5 rounded">{text.slice(idx, idx + search.length)}</mark>{text.slice(idx + search.length)}</>;
}
export { highlightMatch } from "@/lib/searchHighlight";

export const List = ({ setCurrentIndex, entityActiveTabs }: IListProps) => {
const dispatch = useDatamodelViewDispatch();
Expand Down Expand Up @@ -56,7 +50,7 @@ export const List = ({ setCurrentIndex, entityActiveTabs }: IListProps) => {

// Only recalculate items when filtered or search changes
const flatItems = useMemo(() => {
if (filtered && filtered.length > 0) return filtered.filter(item => item.type !== 'attribute' && item.type !== 'relationship');
if (search.length >= 3) return filtered.filter(item => item.type !== 'attribute' && item.type !== 'relationship');

const lowerSearch = search.trim().toLowerCase();
const items: Array<
Expand Down
14 changes: 7 additions & 7 deletions Website/components/datamodelview/attributes/BooleanAttribute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ import { Box, Typography, Chip } from "@mui/material"
import { CheckRounded, RadioButtonCheckedRounded, RadioButtonUncheckedRounded } from "@mui/icons-material";
import React from "react";

export default function BooleanAttribute({ attribute, highlightMatch, highlightTerm }: { attribute: BooleanAttributeType, highlightMatch?: (text: string, term: string) => string | React.JSX.Element, highlightTerm?: string }) {
export default function BooleanAttribute({ attribute, highlightMatch = text => text, highlightTerm = "" }: { attribute: BooleanAttributeType, highlightMatch?: (text: string, term: string) => string | React.JSX.Element, highlightTerm?: string }) {

const isMobile = useIsMobile();

return (
<Box className="flex flex-col gap-1">
<Box className="flex items-center gap-2">
<Typography className="font-semibold text-xs md:text-sm md:font-bold">{highlightMatch && highlightTerm ? highlightMatch("Boolean", highlightTerm) : "Boolean"}</Typography>
<Typography className="font-semibold text-xs md:text-sm md:font-bold">{highlightTerm ? highlightMatch("Boolean", highlightTerm) : "Boolean"}</Typography>
{attribute.DefaultValue !== null && !isMobile && (
<Chip
icon={<CheckRounded className="w-2 h-2 md:w-3 md:h-3" />}
label={`Default: ${attribute.DefaultValue === true ? attribute.TrueLabel : attribute.FalseLabel}`}
label={highlightMatch(`Default: ${attribute.DefaultValue === true ? attribute.TrueLabel : attribute.FalseLabel}`, highlightTerm)}
size="small"
color="success"
variant="outlined"
Expand All @@ -30,11 +30,11 @@ export default function BooleanAttribute({ attribute, highlightMatch, highlightT
) : (
<RadioButtonUncheckedRounded className="w-2 h-2 md:w-3 md:h-3" sx={{ color: 'text.disabled' }} />
)}
<Typography className="text-xs md:text-sm">{highlightMatch && highlightTerm ? highlightMatch(attribute.TrueLabel, highlightTerm) : attribute.TrueLabel}</Typography>
<Typography className="text-xs md:text-sm">{highlightTerm ? highlightMatch(attribute.TrueLabel, highlightTerm) : attribute.TrueLabel}</Typography>
</Box>
<Box className="flex items-center gap-2">
<Chip
label="True"
label={highlightMatch("True", highlightTerm)}
size="small"
sx={{
fontSize: { xs: '0.625rem', md: '0.875rem' },
Expand All @@ -53,11 +53,11 @@ export default function BooleanAttribute({ attribute, highlightMatch, highlightT
) : (
<RadioButtonUncheckedRounded className="w-2 h-2 md:w-3 md:h-3" sx={{ color: 'text.disabled' }} />
)}
<Typography className="text-xs md:text-sm">{highlightMatch && highlightTerm ? highlightMatch(attribute.FalseLabel, highlightTerm) : attribute.FalseLabel}</Typography>
<Typography className="text-xs md:text-sm">{highlightTerm ? highlightMatch(attribute.FalseLabel, highlightTerm) : attribute.FalseLabel}</Typography>
</Box>
<Box className="flex items-center gap-2">
<Chip
label="False"
label={highlightMatch("False", highlightTerm)}
size="small"
sx={{
fontSize: { xs: '0.625rem', md: '0.875rem' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ export default function ChoiceAttribute({ attribute, highlightMatch, highlightTe
<Box className="flex flex-col gap-1">
<Box className="flex items-center gap-2">
<OptionSetScopeIndicator globalOptionSetName={attribute.GlobalOptionSetName} />
<Typography className="font-semibold text-xs md:text-sm md:font-bold">{attribute.Type}-select</Typography>
<Typography className="font-semibold text-xs md:text-sm md:font-bold">{highlightMatch(`${attribute.Type}-select`, highlightTerm)}</Typography>
{attribute.DefaultValue !== null && attribute.DefaultValue !== -1 && !isMobile && (
<Chip
icon={<CheckRounded className="w-2 h-2 md:w-3 md:h-3" />}
label={`Default: ${attribute.Options.find(o => o.Value === attribute.DefaultValue)?.Name}`}
label={highlightMatch(`Default: ${attribute.Options.find(o => o.Value === attribute.DefaultValue)?.Name}`, highlightTerm)}
size="small"
color="success"
variant="outlined"
Expand Down Expand Up @@ -61,7 +61,7 @@ export default function ChoiceAttribute({ attribute, highlightMatch, highlightTe
</Box>
<Box className="flex items-center gap-2">
<Chip
label={formatNumberSeperator(option.Value)}
label={highlightMatch(formatNumberSeperator(option.Value), highlightTerm)}
size="small"
sx={{
fontSize: { xs: '0.625rem', md: '0.875rem' },
Expand All @@ -78,7 +78,7 @@ export default function ChoiceAttribute({ attribute, highlightMatch, highlightTe
className="text-xs italic pl-4 break-words md:pl-6"
sx={{ color: 'text.secondary' }}
>
{option.Description}
{highlightMatch(option.Description, highlightTerm)}
</Typography>
)}
</Box>
Expand Down
31 changes: 5 additions & 26 deletions Website/components/datamodelview/attributes/DecimalAttribute.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
import { DecimalAttributeType } from "@/lib/Types"
import { formatNumberSeperator } from "@/lib/utils"
import { decimalRange, precisionText } from "@/lib/columnTypeText"
import { Typography } from "@mui/material"

export default function MoneyAttribute({ attribute }: { attribute: DecimalAttributeType }) {
const formatNumber =
attribute.Type === "Money"
? FormatMoney
: FormatDecimal

export default function MoneyAttribute({ attribute, highlightMatch = text => text, highlightTerm = "" }: { attribute: DecimalAttributeType, highlightMatch?: (text: string, term: string) => string | React.JSX.Element, highlightTerm?: string }) {
return (
<>
<Typography component="p">
<Typography component="span" className="font-semibold text-xs md:font-bold md:text-sm">{attribute.Type}</Typography>
<Typography component="span" className="font-semibold text-xs md:font-bold md:text-sm">{highlightMatch(attribute.Type, highlightTerm)}</Typography>
{" "}
<Typography component="span" className="text-xs md:text-sm">({formatNumber(attribute.MinValue)} to {formatNumber(attribute.MaxValue)})</Typography>
<Typography component="span" className="text-xs md:text-sm">{highlightMatch(decimalRange(attribute), highlightTerm)}</Typography>
</Typography>
<Typography component="p" className="text-xs md:text-sm">Precision: {attribute.Precision}</Typography>
<Typography component="p" className="text-xs md:text-sm">{highlightMatch(precisionText(attribute.Precision), highlightTerm)}</Typography>
</>
)
}

function FormatMoney(number: number) {
if (number === 922337203685477)
return "Max"
if (number === -922337203685477)
return "Min"
return formatNumberSeperator(number)
}

function FormatDecimal(number: number) {
if (number === 100000000000)
return "Max"
if (number === -100000000000)
return "Min"
return formatNumberSeperator(number)
}
8 changes: 4 additions & 4 deletions Website/components/datamodelview/attributes/FileAttribute.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { FileAttributeType } from "@/lib/Types";
import { formatNumberSeperator } from "@/lib/utils";
import { fileSize } from "@/lib/columnTypeText";
import { Typography } from "@mui/material";

export default function FileAttribute({ attribute } : { attribute: FileAttributeType }) {
export default function FileAttribute({ attribute, highlightMatch = text => text, highlightTerm = "" }: { attribute: FileAttributeType, highlightMatch?: (text: string, term: string) => string | React.JSX.Element, highlightTerm?: string }) {
return (
<>
<Typography component="span" className="font-semibold text-xs md:font-bold md:text-sm">File</Typography>
<Typography component="span" className="font-semibold text-xs md:font-bold md:text-sm">{highlightMatch("File", highlightTerm)}</Typography>
{" "}
<Typography component="span" className="text-xs md:text-sm">(Max {formatNumberSeperator(attribute.MaxSize)}KB)</Typography>
<Typography component="span" className="text-xs md:text-sm">{highlightMatch(fileSize(attribute.MaxSize), highlightTerm)}</Typography>
</>
)
}
16 changes: 4 additions & 12 deletions Website/components/datamodelview/attributes/IntegerAttribute.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
import { IntegerAttributeType } from "@/lib/Types"
import { formatNumberSeperator } from "@/lib/utils"
import { integerRange } from "@/lib/columnTypeText"
import { Typography } from "@mui/material"

export default function IntegerAttribute({ attribute } : { attribute: IntegerAttributeType }) {
export default function IntegerAttribute({ attribute, highlightMatch = text => text, highlightTerm = "" }: { attribute: IntegerAttributeType, highlightMatch?: (text: string, term: string) => string | React.JSX.Element, highlightTerm?: string }) {
return (
<>
<Typography component="span" className="font-semibold text-xs md:font-bold md:text-sm">{attribute.Format}</Typography>
<Typography component="span" className="font-semibold text-xs md:font-bold md:text-sm">{highlightMatch(attribute.Format, highlightTerm)}</Typography>
{" "}
<Typography component="span" className="text-xs md:text-sm">({FormatNumber(attribute.MinValue)} to {FormatNumber(attribute.MaxValue)})</Typography>
<Typography component="span" className="text-xs md:text-sm">{highlightMatch(integerRange(attribute.MinValue, attribute.MaxValue), highlightTerm)}</Typography>
</>
)
}

function FormatNumber(number: number) {
if (number === 2147483647)
return "Max"
if (number === -2147483648)
return "Min"
return formatNumberSeperator(number)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { useDatamodelView, useDatamodelViewDispatch } from "@/contexts/Datamodel
import { Box, Typography, Button, Chip } from "@mui/material"
import { ContentPasteOffRounded, ContentPasteSearchRounded } from "@mui/icons-material";

export default function LookupAttribute({ attribute }: { attribute: LookupAttributeType }) {
export default function LookupAttribute({ attribute, highlightMatch = text => text, highlightTerm = "" }: { attribute: LookupAttributeType, highlightMatch?: (text: string, term: string) => string | React.JSX.Element, highlightTerm?: string }) {

const { scrollToSection } = useDatamodelView();
const dispatch = useDatamodelViewDispatch();

return (
<Box className="flex flex-wrap items-center gap-1 md:gap-2">
<Typography className="font-semibold text-xs md:font-bold md:text-sm">Lookup</Typography>
<Typography className="font-semibold text-xs md:font-bold md:text-sm">{highlightMatch("Lookup", highlightTerm)}</Typography>
<Box className="flex flex-wrap gap-1">
{attribute.Targets
.map(target => target.IsInSolution ?
Expand All @@ -35,12 +35,12 @@ export default function LookupAttribute({ attribute }: { attribute: LookupAttrib
}
}}
>
{target.Name}
{highlightMatch(target.Name, highlightTerm)}
</Button> :
<Chip
key={target.Name}
icon={<ContentPasteOffRounded className="w-2 h-2 md:w-3 md:h-3" />}
label={target.Name}
label={highlightMatch(target.Name, highlightTerm)}
size="small"
disabled
sx={{
Expand Down
Loading
Loading