Skip to content

Commit d2cdaa2

Browse files
committed
UI fixes based on feedback.
1 parent bcc3f9f commit d2cdaa2

File tree

30 files changed

+1521
-373
lines changed

30 files changed

+1521
-373
lines changed

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/app/app.css

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
html,
1313
body {
1414
@apply bg-white dark:bg-gray-950;
15+
overscroll-behavior: none;
1516

1617
@media (prefers-color-scheme: dark) {
1718
color-scheme: dark;
@@ -112,7 +113,7 @@ body {
112113
--card-foreground: oklch(0.985 0 0);
113114
--popover: oklch(0.21 0.006 285.885);
114115
--popover-foreground: oklch(0.985 0 0);
115-
--primary: oklch(0.648 0.2 131.684);
116+
--primary: oklch(0.52 0.18 131.684);
116117
--primary-foreground: oklch(0.986 0.031 120.757);
117118
--secondary: oklch(0.274 0.006 286.033);
118119
--secondary-foreground: oklch(0.985 0 0);
@@ -123,20 +124,20 @@ body {
123124
--destructive: oklch(0.704 0.191 22.216);
124125
--border: oklch(1 0 0 / 10%);
125126
--input: oklch(1 0 0 / 15%);
126-
--ring: oklch(0.405 0.101 131.063);
127+
--ring: oklch(0.33 0.09 131.063);
127128
--chart-1: oklch(0.871 0.15 154.449);
128129
--chart-2: oklch(0.723 0.219 149.579);
129130
--chart-3: oklch(0.627 0.194 149.214);
130131
--chart-4: oklch(0.527 0.154 150.069);
131132
--chart-5: oklch(0.448 0.119 151.328);
132133
--sidebar: oklch(0.21 0.006 285.885);
133134
--sidebar-foreground: oklch(0.985 0 0);
134-
--sidebar-primary: oklch(0.768 0.233 130.85);
135+
--sidebar-primary: oklch(0.6 0.2 130.85);
135136
--sidebar-primary-foreground: oklch(0.986 0.031 120.757);
136137
--sidebar-accent: oklch(0.274 0.006 286.033);
137138
--sidebar-accent-foreground: oklch(0.985 0 0);
138139
--sidebar-border: oklch(1 0 0 / 10%);
139-
--sidebar-ring: oklch(0.405 0.101 131.063);
140+
--sidebar-ring: oklch(0.33 0.09 131.063);
140141
}
141142

142143
@layer base {

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/app/routes/layout.tsx

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ import { GlobalRefreshButton } from '~/components/elements/GlobalRefreshButton';
2727
import { DiagnosticsDialog } from '~/components/elements/DiagnosticsDialog';
2828
import { SidebarProvider, SidebarInset, SidebarTrigger } from '~/components/ui/sidebar';
2929
import { Badge } from '~/components/ui/badge';
30+
import { Button } from '~/components/ui/button';
3031
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui/tooltip';
31-
import { ChevronRight, Lock } from 'lucide-react';
32-
import { LegacyModeDocumentation } from '~/features/queue-management/components/LegacyModeDocumentation';
33-
import { getMergedConfigData } from '~/utils/configUtils';
34-
import { SPECIAL_VALUES } from '~/types';
32+
import { Lock, GitCompareArrows } from 'lucide-react';
3533
import { SearchBar } from '~/components/search/SearchBar';
3634
import { useKeyboardShortcuts } from '~/hooks/useKeyboardShortcuts';
3735
import { toast } from 'sonner';
@@ -41,19 +39,16 @@ export default function Layout() {
4139
const [stagedChangesPanelOpen, setStagedChangesPanelOpen] = useState(false);
4240
const [isApplying, setIsApplying] = useState(false);
4341
const loadInitialData = useSchedulerStore((state) => state.loadInitialData);
44-
const configData = useSchedulerStore((state) => state.configData);
4542
const stagedChanges = useSchedulerStore((state) => state.stagedChanges);
4643
const setSearchContext = useSchedulerStore((state) => state.setSearchContext);
4744
const isReadOnly = useSchedulerStore((state) => state.isReadOnly);
4845
const applyChanges = useSchedulerStore((state) => state.applyChanges);
4946
const clearAllChanges = useSchedulerStore((state) => state.clearAllChanges);
5047
const isPropertyPanelOpen = useSchedulerStore((state) => state.isPropertyPanelOpen);
48+
const isComparisonModeActive = useSchedulerStore((state) => state.isComparisonModeActive);
49+
const toggleComparisonMode = useSchedulerStore((state) => state.toggleComparisonMode);
5150
const location = useLocation();
5251

53-
// Get legacy mode status considering staged changes
54-
const mergedData = getMergedConfigData(configData, stagedChanges);
55-
const legacyModeEnabled = mergedData.get(SPECIAL_VALUES.LEGACY_MODE_PROPERTY) !== 'false';
56-
5752
useEffect(() => {
5853
loadInitialData().catch((err) => {
5954
console.error('Failed to load initial data:', err);
@@ -183,21 +178,7 @@ export default function Layout() {
183178
<header className="flex h-16 items-center gap-4 border-b px-6">
184179
<SidebarTrigger />
185180
<div className="flex-1">
186-
<div className="flex items-center gap-2">
187-
<h1 className="text-xl font-semibold">{pageInfo.title}</h1>
188-
{location.pathname === '/' && (
189-
<LegacyModeDocumentation legacyModeEnabled={legacyModeEnabled}>
190-
<Badge
191-
variant={legacyModeEnabled ? 'warning' : 'success'}
192-
className="cursor-pointer hover:opacity-80 hover:scale-105 transition-all duration-200 animate-pulse"
193-
style={{ animationIterationCount: '3' }}
194-
>
195-
{legacyModeEnabled ? 'Legacy Mode' : 'Flexible Mode'}
196-
<ChevronRight className="h-3 w-3 ml-1" />
197-
</Badge>
198-
</LegacyModeDocumentation>
199-
)}
200-
</div>
181+
<h1 className="text-xl font-semibold">{pageInfo.title}</h1>
201182
<p className="text-sm text-muted-foreground">{pageInfo.description}</p>
202183
</div>
203184
<SearchBar className="w-64" placeholder="Search" />
@@ -217,6 +198,27 @@ export default function Layout() {
217198
</TooltipProvider>
218199
)}
219200
<GlobalRefreshButton />
201+
{location.pathname === '/' && (
202+
<TooltipProvider>
203+
<Tooltip>
204+
<TooltipTrigger asChild>
205+
<Button
206+
variant={isComparisonModeActive ? 'default' : 'ghost'}
207+
size="icon"
208+
onClick={toggleComparisonMode}
209+
aria-label={
210+
isComparisonModeActive ? 'Exit comparison mode' : 'Enter comparison mode'
211+
}
212+
>
213+
<GitCompareArrows className="h-4 w-4" />
214+
</Button>
215+
</TooltipTrigger>
216+
<TooltipContent>
217+
<p>{isComparisonModeActive ? 'Exit comparison mode' : 'Compare queues'}</p>
218+
</TooltipContent>
219+
</Tooltip>
220+
</TooltipProvider>
221+
)}
220222
<DiagnosticsDialog />
221223
<ModeToggle />
222224
</header>

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/config/__tests__/propertyDefinitions.test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,128 @@ describe('propertyDefinitions', () => {
238238
}
239239
});
240240

241+
it('disables legacy auto-creation for queues with children', () => {
242+
const legacyAutoCreate = queuePropertyDefinitions.find(
243+
(p) => p.name === 'auto-create-child-queue.enabled',
244+
);
245+
246+
expect(legacyAutoCreate).toBeDefined();
247+
if (!legacyAutoCreate) {
248+
return;
249+
}
250+
251+
expect(Array.isArray(legacyAutoCreate.enableWhen)).toBe(true);
252+
const enableCondition = legacyAutoCreate.enableWhen?.[0];
253+
expect(enableCondition).toBeInstanceOf(Function);
254+
255+
if (enableCondition) {
256+
// Test parent queue with children - should be disabled
257+
const parentWithChildrenOptions = {
258+
...createConditionOptions({
259+
property: legacyAutoCreate,
260+
capacity: '50',
261+
values: { 'auto-create-child-queue.enabled': 'false' },
262+
}),
263+
queuePath: 'root.parent',
264+
queueInfo: {
265+
queueType: 'parent' as const,
266+
queueName: 'parent',
267+
queuePath: 'root.parent',
268+
capacity: 50,
269+
usedCapacity: 0,
270+
maxCapacity: 100,
271+
absoluteCapacity: 50,
272+
absoluteMaxCapacity: 100,
273+
absoluteUsedCapacity: 0,
274+
numApplications: 0,
275+
numActiveApplications: 0,
276+
numPendingApplications: 0,
277+
state: 'RUNNING' as const,
278+
queues: {
279+
queue: [
280+
{
281+
queueType: 'leaf' as const,
282+
queueName: 'child1',
283+
queuePath: 'root.parent.child1',
284+
capacity: 50,
285+
usedCapacity: 0,
286+
maxCapacity: 100,
287+
absoluteCapacity: 25,
288+
absoluteMaxCapacity: 100,
289+
absoluteUsedCapacity: 0,
290+
numApplications: 0,
291+
numActiveApplications: 0,
292+
numPendingApplications: 0,
293+
state: 'RUNNING' as const,
294+
},
295+
],
296+
},
297+
},
298+
};
299+
expect(enableCondition(parentWithChildrenOptions)).toBe(false);
300+
301+
// Test parent queue without children - should be enabled
302+
const parentNoChildrenOptions = {
303+
...createConditionOptions({
304+
property: legacyAutoCreate,
305+
capacity: '50',
306+
values: { 'auto-create-child-queue.enabled': 'false' },
307+
}),
308+
queuePath: 'root.parent',
309+
queueInfo: {
310+
queueType: 'parent' as const,
311+
queueName: 'parent',
312+
queuePath: 'root.parent',
313+
capacity: 50,
314+
usedCapacity: 0,
315+
maxCapacity: 100,
316+
absoluteCapacity: 50,
317+
absoluteMaxCapacity: 100,
318+
absoluteUsedCapacity: 0,
319+
numApplications: 0,
320+
numActiveApplications: 0,
321+
numPendingApplications: 0,
322+
state: 'RUNNING' as const,
323+
queues: {
324+
queue: [],
325+
},
326+
},
327+
};
328+
expect(enableCondition(parentNoChildrenOptions)).toBe(true);
329+
330+
// Test with null queueInfo - should be enabled (default behavior)
331+
const nullQueueInfoOptions = createConditionOptions({
332+
property: legacyAutoCreate,
333+
capacity: '50',
334+
});
335+
expect(enableCondition(nullQueueInfoOptions)).toBe(true);
336+
337+
// Test with undefined queues property - should be enabled
338+
const undefinedQueuesOptions = {
339+
...createConditionOptions({
340+
property: legacyAutoCreate,
341+
capacity: '50',
342+
}),
343+
queueInfo: {
344+
queueType: 'parent' as const,
345+
queueName: 'parent',
346+
queuePath: 'root.parent',
347+
capacity: 50,
348+
usedCapacity: 0,
349+
maxCapacity: 100,
350+
absoluteCapacity: 50,
351+
absoluteMaxCapacity: 100,
352+
absoluteUsedCapacity: 0,
353+
numApplications: 0,
354+
numActiveApplications: 0,
355+
numPendingApplications: 0,
356+
state: 'RUNNING' as const,
357+
},
358+
};
359+
expect(enableCondition(undefinedQueuesOptions)).toBe(true);
360+
}
361+
});
362+
241363
it('enables flexible auto-creation based on root queue children capacity mode', () => {
242364
const flexibleAutoCreate = queuePropertyDefinitions.find(
243365
(p) => p.name === 'auto-queue-creation-v2.enabled',

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/config/properties/queue-properties.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,12 @@ export const queuePropertyDefinitions: PropertyDescriptor[] = [
508508
defaultValue: '',
509509
required: false,
510510
showWhen: [shouldShowLegacyAutoCreation],
511+
enableWhen: [
512+
({ queueInfo }) => {
513+
// Disable if queue has children
514+
return !queueInfo?.queues?.queue || queueInfo.queues.queue.length === 0;
515+
},
516+
],
511517
},
512518
{
513519
name: 'auto-queue-creation-v2.enabled',

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/features/global-settings/components/LegacyModeToggle.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { HighlightedText } from '~/components/search/HighlightedText';
2525
import {
2626
Dialog,
2727
DialogContent,
28+
DialogDescription,
2829
DialogHeader,
2930
DialogTitle,
3031
DialogTrigger,
@@ -265,15 +266,12 @@ export const LegacyModeToggle: React.FC<LegacyModeToggleProps> = ({
265266
<DialogContent className="max-w-2xl max-h-[80vh]">
266267
<DialogHeader>
267268
<DialogTitle>Validation Preview</DialogTitle>
269+
<DialogDescription>
270+
Changes that would occur when switching to{' '}
271+
{currentEnabled ? 'Flexible' : 'Legacy'} Mode
272+
</DialogDescription>
268273
</DialogHeader>
269274
<div className="space-y-3 overflow-y-auto max-h-[calc(80vh-8rem)]">
270-
<div>
271-
<p className="text-xs text-muted-foreground mt-1">
272-
Changes that would occur when switching to{' '}
273-
{currentEnabled ? 'Flexible' : 'Legacy'} Mode
274-
</p>
275-
</div>
276-
277275
{validationPreview.affectedQueues === 0 ? (
278276
<p className="text-sm text-muted-foreground py-2">
279277
No validation changes would occur.

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/features/property-editor/components/PropertyFormField.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ describe('PropertyFormField', () => {
224224
</FormWrapper>,
225225
);
226226

227-
expect(screen.getByText('Capacity Editor')).toBeInTheDocument();
227+
expect(screen.getByText('Edit Capacity')).toBeInTheDocument();
228228
expect(screen.queryByText('This is the capacity description')).not.toBeInTheDocument();
229229
});
230230

hadoop-yarn-project/hadoop-yarn/hadoop-yarn-capacity-scheduler-ui/src/main/webapp/src/features/property-editor/components/PropertyFormField.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ export const PropertyFormField: React.FC<PropertyFormFieldProps> = ({
523523
onClick={handleOpenCapacityEditor}
524524
disabled={!parentQueuePath || !isEnabled}
525525
>
526-
Capacity Editor
526+
Edit Capacity
527527
</Button>
528528
) : (
529529
<span className="text-xs text-muted-foreground">

0 commit comments

Comments
 (0)