Skip to content

Commit e991d9b

Browse files
authored
Merge pull request #65 from delegateas/patches/more-patches-to-metadata
More patches from Morten's list
2 parents 1519bf5 + 05bbc5d commit e991d9b

16 files changed

Lines changed: 231 additions & 178 deletions

File tree

Generator/DataverseService.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ public DataverseService(IConfiguration configuration, ILogger<DataverseService>
6464
var entityRootBehaviour = solutionComponents.Where(x => x.ComponentType == 1).ToDictionary(x => x.ObjectId, x => x.RootComponentBehavior);
6565
var attributesInSolution = solutionComponents.Where(x => x.ComponentType == 2).Select(x => x.ObjectId).ToHashSet();
6666
var rolesInSolution = solutionComponents.Where(x => x.ComponentType == 20).Select(x => x.ObjectId).ToList();
67-
var pluginStepsInSolution = solutionComponents.Where(x => x.ComponentType == 92).Select(x => x.ObjectId).ToList();
6867

6968
var entitiesInSolutionMetadata = await GetEntityMetadata(entitiesInSolution);
7069

@@ -140,7 +139,7 @@ public DataverseService(IConfiguration configuration, ILogger<DataverseService>
140139
.ToList(),
141140
RelevantManyToMany =
142141
x.ManyToManyRelationships
143-
.Where(r => entityLogicalNamesInSolution.Contains(r.IntersectEntityName.ToLower()))
142+
.Where(r => entityLogicalNamesInSolution.Contains(r.Entity1LogicalName) && entityLogicalNamesInSolution.Contains(r.Entity2LogicalName))
144143
.ToList(),
145144
})
146145
.Where(x => x.EntityMetadata.DisplayName.UserLocalizedLabel?.Label != null)
@@ -215,16 +214,24 @@ private static Record MakeRecord(
215214

216215
var manyToMany = relevantManyToMany
217216
.Where(x => logicalToSchema.ContainsKey(x.Entity1LogicalName) && logicalToSchema[x.Entity1LogicalName].IsInSolution)
218-
.Select(x => new DTO.Relationship(
219-
x.IsCustomRelationship ?? false,
220-
x.Entity1AssociatedMenuConfiguration.Behavior == AssociatedMenuBehavior.UseLabel
221-
? x.Entity1AssociatedMenuConfiguration.Label.UserLocalizedLabel?.Label ?? x.Entity1NavigationPropertyName
222-
: x.Entity1NavigationPropertyName,
223-
logicalToSchema[x.Entity1LogicalName].Name,
224-
"-",
225-
x.SchemaName,
226-
IsManyToMany: true,
227-
null))
217+
.Select(x =>
218+
{
219+
var useEntity1 = x.Entity1LogicalName == entity.LogicalName;
220+
221+
var label = !useEntity1
222+
? x.Entity1AssociatedMenuConfiguration.Label.UserLocalizedLabel?.Label ?? x.Entity1NavigationPropertyName
223+
: x.Entity2AssociatedMenuConfiguration.Label.UserLocalizedLabel?.Label ?? x.Entity2NavigationPropertyName;
224+
225+
return new DTO.Relationship(
226+
x.IsCustomRelationship ?? false,
227+
label,
228+
logicalToSchema[!useEntity1 ? x.Entity1LogicalName : x.Entity2LogicalName].Name,
229+
"-",
230+
x.SchemaName,
231+
IsManyToMany: true,
232+
null
233+
);
234+
})
228235
.ToList();
229236

230237
Dictionary<string, string> tablegroups = []; // logicalname -> group

Website/app/api/markdown/route.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { NextResponse } from 'next/server'
2-
import { readFileSync } from "fs";
2+
import { existsSync, readFileSync } from "fs";
33
import { join } from "path";
44

55
export async function GET() {
66
const generatedPath = join(process.cwd(), 'generated', 'Introduction.md');
7-
const stubsPath = join(process.cwd(), 'stubs', 'Introduction.md');
8-
let fileContent;
7+
8+
if (!existsSync(generatedPath)) {
9+
console.error(`File not found at path: ${generatedPath}`);
10+
return NextResponse.json({ error: 'File not found' }, { status: 404 });
11+
}
12+
13+
let fileContent: string;
914
try {
1015
fileContent = readFileSync(generatedPath, 'utf-8');
11-
} catch (error) {
12-
fileContent = readFileSync(stubsPath, 'utf-8');
13-
console.error('Error reading generated wiki file, falling back to stubs:', error);
16+
} catch {
17+
return NextResponse.json({ error: 'File not found' }, { status: 404 });
1418
}
1519
return NextResponse.json({ fileContent })
1620
}

Website/components/aboutview/AboutView.tsx

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,19 @@ export const AboutView = ({}: IAboutViewProps) => {
2525
<Box className="max-w-6xl mx-auto py-10 px-4 sm:px-6 lg:px-12">
2626
{/* Logo */}
2727
<Box className="flex justify-center mb-10">
28-
<Box
29-
component="img"
30-
className="h-20 sm:h-28 md:h-32 object-contain"
31-
src="/DMVLOGOHORZ.svg"
32-
alt="Data Model Viewer logo"
33-
/>
28+
<Box className="flex items-center">
29+
<Box
30+
component="img"
31+
className="h-20 sm:h-28 md:h-32 object-contain"
32+
src="/DMVLOGO.svg"
33+
alt="Data Model Viewer logo"
34+
/>
35+
<Box className="flex flex-col ml-4 mt-4 justify-center h-full">
36+
<Typography variant='h4' className='m-0 p-0 leading-8'>DATA MODEL</Typography>
37+
<Typography variant='h2' className='m-0 p-0 font-semibold leading-14'>VIEWER</Typography>
38+
<Typography variant='caption' color='text.secondary'>@ DELEGATE | CONTEXT&</Typography>
39+
</Box>
40+
</Box>
3441
</Box>
3542

3643
{/* What is DMV */}

Website/components/datamodelview/Attributes.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ export const Attributes = ({ entity, onVisibleCountChange, search = "" }: IAttri
388388
{highlightMatch(attribute.SchemaName, highlightTerm)}
389389
</TableCell>
390390
<TableCell className="break-words py-1 md:py-1.5">{getAttributeComponent(entity, attribute, highlightMatch, highlightTerm)}</TableCell>
391-
<TableCell className="py-1 md:py-1.5"><AttributeDetails attribute={attribute} /></TableCell>
391+
<TableCell className="py-1 md:py-1.5"><AttributeDetails entityName={entity.SchemaName} attribute={attribute} /></TableCell>
392392
<TableCell className="break-words py-1 md:py-1.5 text-xs md:text-sm">
393393
{highlightMatch(attribute.Description ?? "", highlightTerm)}
394394
</TableCell>

Website/components/datamodelview/List.tsx

Lines changed: 81 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { AttributeType, EntityType, GroupType } from "@/lib/Types";
88
import { updateURL } from "@/lib/url-utils";
99
import { copyToClipboard, generateGroupLink } from "@/lib/clipboard-utils";
1010
import { useSnackbar } from "@/contexts/SnackbarContext";
11-
import { debounce, Tooltip } from '@mui/material';
11+
import { Box, CircularProgress, debounce, Tooltip } from '@mui/material';
1212

1313
interface IListProps {
1414
setCurrentIndex: (index: number) => void;
@@ -24,16 +24,13 @@ export function highlightMatch(text: string, search: string) {
2424

2525
export const List = ({ setCurrentIndex }: IListProps) => {
2626
const dispatch = useDatamodelViewDispatch();
27-
const { currentSection } = useDatamodelView();
27+
const { currentSection, loadingSection } = useDatamodelView();
2828
const { groups, filtered, search } = useDatamodelData();
2929
const { showSnackbar } = useSnackbar();
3030
const parentRef = useRef<HTMLDivElement | null>(null);
3131
// used to relocate section after search/filter
3232
const [sectionVirtualItem, setSectionVirtualItem] = useState<string | null>(null);
33-
34-
// Track position before search for restoration
35-
const isTabSwitching = useRef(false);
36-
33+
3734
const handleCopyGroupLink = useCallback(async (groupName: string) => {
3835
const link = generateGroupLink(groupName);
3936
const success = await copyToClipboard(link);
@@ -163,8 +160,7 @@ export const List = ({ setCurrentIndex }: IListProps) => {
163160
}
164161

165162
rowVirtualizer.scrollToIndex(sectionIndex, {
166-
align: 'start',
167-
behavior: 'smooth'
163+
align: 'start'
168164
});
169165

170166
}, [flatItems]);
@@ -180,8 +176,7 @@ export const List = ({ setCurrentIndex }: IListProps) => {
180176
}
181177

182178
rowVirtualizer.scrollToIndex(groupIndex, {
183-
align: 'start',
184-
behavior: 'smooth'
179+
align: 'start'
185180
});
186181
}, [flatItems]);
187182

@@ -208,85 +203,85 @@ export const List = ({ setCurrentIndex }: IListProps) => {
208203
}, [rowVirtualizer]);
209204

210205
return (
211-
<div ref={parentRef} style={{ height: 'calc(100vh - var(--layout-header-desktop-height))', overflow: 'auto' }} className="p-6 relative no-scrollbar">
206+
<>
207+
<Box className={`absolute w-full h-full flex items-center justify-center z-[100] transition-opacity duration-300 ${loadingSection ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}>
208+
<CircularProgress />
209+
</Box>
210+
<div ref={parentRef} style={{ height: 'calc(100vh - var(--layout-header-desktop-height))', overflow: 'auto' }} className="relative no-scrollbar">
212211

213-
{/* Show no results message when searching but no items found */}
214-
{flatItems.length === 0 && search && search.length >= 3 && (
215-
<div className="flex flex-col items-center justify-center h-64 text-gray-500">
216-
<div className="text-lg font-medium mb-2">No tables found</div>
217-
<div className="text-sm text-center">
218-
No attributes match your search for &quot;{search}&quot;
212+
{/* Show no results message when searching but no items found */}
213+
{flatItems.length === 0 && search && search.length >= 3 && (
214+
<div className="flex flex-col items-center justify-center h-64 text-gray-500">
215+
<div className="text-lg font-medium mb-2">No tables found</div>
216+
<div className="text-sm text-center">
217+
No attributes match your search for &quot;{search}&quot;
218+
</div>
219219
</div>
220-
</div>
221-
)}
222-
223-
{/* Virtualized list */}
224-
<div
225-
style={{
226-
height: `${rowVirtualizer.getTotalSize()}px`,
227-
width: '100%',
228-
position: 'relative',
229-
visibility: flatItems.length === 0 ? 'hidden' : 'visible'
230-
}}
231-
>
232-
{rowVirtualizer.getVirtualItems().map((virtualItem) => {
233-
const item = flatItems[virtualItem.index];
220+
)}
221+
222+
{/* Virtualized list */}
223+
<div
224+
className={`m-6 transition-opacity duration-300 ${loadingSection ? 'opacity-0 pointer-events-none' : 'opacity-100'}`}
225+
style={{
226+
height: `${rowVirtualizer.getTotalSize()}px`,
227+
width: '100%',
228+
position: 'relative',
229+
visibility: flatItems.length === 0 ? 'hidden' : 'visible'
230+
}}
231+
>
232+
{rowVirtualizer.getVirtualItems().map((virtualItem) => {
233+
const item = flatItems[virtualItem.index];
234234

235-
return (
236-
<div
237-
key={virtualItem.key}
238-
data-index={virtualItem.index}
239-
style={{
240-
position: 'absolute',
241-
top: 0,
242-
left: 0,
243-
width: '100%',
244-
transform: `translateY(${virtualItem.start}px)`,
245-
}}
246-
ref={(el) => {
247-
if (el) {
248-
// trigger remeasurement when content changes and load
249-
requestAnimationFrame(() => {
250-
handleSectionResize(virtualItem.index);
251-
});
252-
}
253-
}}
254-
>
255-
{item.type === 'group' ? (
256-
<div className="flex items-center py-6 my-4">
257-
<div className="flex-1 h-0.5 bg-gray-200" />
258-
<Tooltip title="Copy link to this group">
259-
<div
260-
className="px-4 text-md font-semibold text-gray-700 uppercase tracking-wide whitespace-nowrap cursor-pointer hover:text-blue-600 transition-colors"
261-
onClick={() => handleCopyGroupLink(item.group.Name)}
262-
>
263-
{item.group.Name}
264-
</div>
265-
</Tooltip>
266-
<div className="flex-1 h-0.5 bg-gray-200" />
267-
</div>
268-
) : (
269-
<div className="text-sm">
270-
<Section
271-
entity={item.entity}
272-
group={item.group}
273-
onTabChange={(isChanging: boolean) => {
274-
isTabSwitching.current = isChanging;
275-
if (isChanging) {
276-
// Reset after a short delay to allow for the content change
277-
setTimeout(() => {
278-
isTabSwitching.current = false;
279-
}, 100);
280-
}
281-
}}
282-
search={search}
283-
/>
284-
</div>
285-
)}
286-
</div>
287-
);
288-
})}
235+
return (
236+
<div
237+
key={virtualItem.key}
238+
data-index={virtualItem.index}
239+
style={{
240+
position: 'absolute',
241+
top: 0,
242+
left: 0,
243+
width: '100%',
244+
transform: `translateY(${virtualItem.start}px)`,
245+
}}
246+
ref={(el) => {
247+
if (el) {
248+
// trigger remeasurement when content changes and load
249+
requestAnimationFrame(() => {
250+
handleSectionResize(virtualItem.index);
251+
});
252+
}
253+
}}
254+
>
255+
{item.type === 'group' ? (
256+
<div className="flex items-center py-6 my-4">
257+
<div className="flex-1 h-0.5 bg-gray-200" />
258+
<Tooltip title="Copy link to this group">
259+
<div
260+
className="px-4 text-md font-semibold text-gray-700 uppercase tracking-wide whitespace-nowrap cursor-pointer hover:text-blue-600 transition-colors"
261+
onClick={() => handleCopyGroupLink(item.group.Name)}
262+
>
263+
{item.group.Name}
264+
</div>
265+
</Tooltip>
266+
<div className="flex-1 h-0.5 bg-gray-200" />
267+
</div>
268+
) : (
269+
<div className="text-sm">
270+
<Section
271+
entity={item.entity}
272+
group={item.group}
273+
onTabChange={() => {
274+
275+
}}
276+
search={search}
277+
/>
278+
</div>
279+
)}
280+
</div>
281+
);
282+
})}
283+
</div>
289284
</div>
290-
</div>
285+
</>
291286
);
292287
};

Website/components/datamodelview/Relationships.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ export const Relationships = ({ entity, onVisibleCountChange, search = "" }: IRe
368368
color: 'primary.main'
369369
}}
370370
onClick={() => {
371-
dispatch({ type: "SET_CURRENT_SECTION", payload: relationship.TableSchema })
371+
dispatch({ type: 'SET_LOADING_SECTION', payload: relationship.TableSchema });
372+
dispatch({ type: "SET_CURRENT_SECTION", payload: relationship.TableSchema });
372373
scrollToSection(relationship.TableSchema);
373374
}}
374375
>

Website/components/datamodelview/attributes/ChoiceAttribute.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useIsMobile } from "@/hooks/use-mobile"
22
import { ChoiceAttributeType } from "@/lib/Types"
33
import { formatNumberSeperator } from "@/lib/utils"
44
import { Box, Typography, Chip } from "@mui/material"
5-
import { CheckBoxOutlineBlankRounded, CheckBoxRounded, CheckRounded } from "@mui/icons-material"
5+
import { CheckBoxOutlineBlankRounded, CheckBoxRounded, CheckRounded, RadioButtonCheckedRounded, RadioButtonUncheckedRounded } from "@mui/icons-material"
66

77
export default function ChoiceAttribute({ attribute, highlightMatch, highlightTerm }: { attribute: ChoiceAttributeType, highlightMatch: (text: string, term: string) => string | React.JSX.Element, highlightTerm: string }) {
88

@@ -38,9 +38,9 @@ export default function ChoiceAttribute({ attribute, highlightMatch, highlightTe
3838
) : (
3939
// For single-select, show radio buttons
4040
option.Value === attribute.DefaultValue ? (
41-
<CheckBoxRounded className="w-2 h-2 md:w-3 md:h-3" sx={{ color: 'success.main' }} />
41+
<RadioButtonCheckedRounded className="w-2 h-2 md:w-3 md:h-3" sx={{ color: 'success.main' }} />
4242
) : (
43-
<CheckBoxOutlineBlankRounded className="w-2 h-2 md:w-3 md:h-3" sx={{ color: 'text.disabled' }} />
43+
<RadioButtonUncheckedRounded className="w-2 h-2 md:w-3 md:h-3" sx={{ color: 'text.disabled' }} />
4444
)
4545
)}
4646
<Typography className="text-xs md:text-sm">{highlightMatch(option.Name, highlightTerm)}</Typography>

0 commit comments

Comments
 (0)