Skip to content

Commit 5bcb8fb

Browse files
authored
Merge pull request #502 from MytsV/feature-details_views
Reimplement detailed views of RSEs, DIDs and Rules
2 parents e9c5233 + 8dc69fa commit 5bcb8fb

File tree

98 files changed

+2555
-2070
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+2555
-2070
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,15 @@
11
'use client';
2-
import { PageDID as PageDIDStory } from '@/component-library/pages/legacy/DID/PageDID';
3-
import useComDOM from '@/lib/infrastructure/hooks/useComDOM';
4-
import { useEffect, useState } from 'react';
5-
import { HTTPRequest } from '@/lib/sdk/http';
6-
import {
7-
DIDDatasetReplicasViewModel,
8-
DIDKeyValuePairsDataViewModel,
9-
DIDMetaViewModel,
10-
DIDRulesViewModel,
11-
DIDViewModel,
12-
FileReplicaStateViewModel,
13-
} from '@/lib/infrastructure/data/view-model/did';
14-
import { didKeyValuePairsDataQuery, didMetaQueryBase } from '@/app/(rucio)/did/queries';
15-
import { Loading } from '@/component-library/pages/legacy/Helpers/Loading';
2+
3+
import { DetailsDID } from '@/component-library/pages/DID/details/DetailsDID';
4+
import { useEffect } from 'react';
165

176
export default function Page({ params }: { params: { scope: string; name: string } }) {
187
const decodedScope = decodeURIComponent(params.scope);
198
const decodedName = decodeURIComponent(params.name);
209

21-
const [didMeta, setDIDMeta] = useState<DIDMetaViewModel>({ status: 'pending' } as DIDMetaViewModel);
22-
const [didKeyValuePairsData, setDIDKeyValuePairsData] = useState({ status: 'pending' } as DIDKeyValuePairsDataViewModel);
23-
const [fromDidList, setFromDidList] = useState<string>('yosearch');
24-
useEffect(() => {
25-
didMetaQueryBase(decodedScope, decodedName).then(setDIDMeta);
26-
}, []);
2710
useEffect(() => {
28-
didKeyValuePairsDataQuery(decodedScope, decodedName).then(setDIDKeyValuePairsData);
11+
document.title = `${decodedScope}:${decodedName} - Rucio`;
2912
}, []);
3013

31-
const didParentsComDOM = useComDOM<DIDViewModel>('page-did-parents-query', [], false, Infinity, 200, true);
32-
const didContentsComDOM = useComDOM<DIDViewModel>('page-did-contents-query', [], false, Infinity, 200, true);
33-
const didFileReplicasComDOM = useComDOM<FileReplicaStateViewModel>('page-did-filereplicas-query', [], false, Infinity, 200, true);
34-
const didFileReplicasDOnChange = (scope: string, name: string) => {
35-
didFileReplicasComDOM.setRequest({
36-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/list-file-replicas`),
37-
method: 'GET',
38-
params: {
39-
scope: scope,
40-
name: name,
41-
},
42-
headers: new Headers({
43-
'Content-Type': 'application/json',
44-
} as HeadersInit),
45-
body: null,
46-
} as HTTPRequest);
47-
didFileReplicasComDOM.start();
48-
};
49-
const didRulesComDOM = useComDOM<DIDRulesViewModel>('page-did-rules-query', [], false, Infinity, 200, true);
50-
const didDatasetReplicasComDOM = useComDOM<DIDDatasetReplicasViewModel>('page-did-datasetreplicas-query', [], false, Infinity, 200, true);
51-
useEffect(() => {
52-
const setRequests = async () => {
53-
await didContentsComDOM.setRequest({
54-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/list-did-contents`),
55-
method: 'GET',
56-
params: {
57-
scope: decodedScope,
58-
name: decodedName,
59-
},
60-
headers: new Headers({
61-
'Content-Type': 'application/json',
62-
} as HeadersInit),
63-
body: null,
64-
} as HTTPRequest);
65-
await didParentsComDOM.setRequest({
66-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/list-did-parents`),
67-
method: 'GET',
68-
params: {
69-
scope: decodedScope,
70-
name: decodedName,
71-
},
72-
headers: new Headers({
73-
'Content-Type': 'application/json',
74-
} as HeadersInit),
75-
body: null,
76-
} as HTTPRequest);
77-
await didFileReplicasComDOM.setRequest({
78-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/list-file-replicas`),
79-
method: 'GET',
80-
params: {
81-
scope: decodedScope,
82-
name: decodedName,
83-
},
84-
headers: new Headers({
85-
'Content-Type': 'application/json',
86-
} as HeadersInit),
87-
body: null,
88-
} as HTTPRequest);
89-
await didRulesComDOM.setRequest({
90-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/list-did-rules`),
91-
method: 'GET',
92-
params: {
93-
scope: decodedScope,
94-
name: decodedName,
95-
},
96-
headers: new Headers({
97-
'Content-Type': 'application/json',
98-
} as HeadersInit),
99-
body: null,
100-
} as HTTPRequest);
101-
await didDatasetReplicasComDOM.setRequest({
102-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/list-dataset-replicas`),
103-
method: 'GET',
104-
params: {
105-
scope: decodedScope,
106-
name: decodedName,
107-
},
108-
headers: new Headers({
109-
'Content-Type': 'application/json',
110-
} as HeadersInit),
111-
body: null,
112-
} as HTTPRequest);
113-
};
114-
setRequests();
115-
}, []);
116-
if (didMeta.status === 'pending') {
117-
return <Loading title="View DID" subtitle={`For DID ${decodedScope}:${decodedName}`} />;
118-
}
119-
return (
120-
<PageDIDStory
121-
didMeta={didMeta}
122-
fromDidList={fromDidList}
123-
didParentsComDOM={didParentsComDOM}
124-
didKeyValuePairsData={didKeyValuePairsData}
125-
didFileReplicasComDOM={didFileReplicasComDOM}
126-
didFileReplicasDOnChange={didFileReplicasDOnChange}
127-
didRulesComDOM={didRulesComDOM}
128-
didContentsComDOM={didContentsComDOM}
129-
didDatasetReplicasComDOM={didDatasetReplicasComDOM}
130-
/>
131-
);
14+
return <DetailsDID scope={decodedScope} name={decodedName} />;
13215
}
+6-40
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,9 @@
1-
'use client';
2-
import { Loading } from '@/component-library/pages/legacy/Helpers/Loading';
3-
import { PageRSE as PageRSEStory } from '@/component-library/pages/legacy/RSE/PageRSE';
4-
import { RSEBlockState } from '@/lib/core/entity/rucio';
5-
import { RSEAttributeViewModel, RSEProtocolViewModel, RSEViewModel } from '@/lib/infrastructure/data/view-model/rse';
6-
import { useEffect, useState } from 'react';
7-
8-
async function getRSE(rseName: string): Promise<RSEViewModel> {
9-
const url = `${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/get-rse?` + new URLSearchParams({ rseName });
10-
const res = await fetch(url);
11-
return await res.json();
12-
}
13-
14-
async function getProtocols(rseName: string): Promise<RSEProtocolViewModel> {
15-
const url = `${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/get-rse-protocols?` + new URLSearchParams({ rseName });
16-
const res = await fetch(url);
17-
return await res.json();
18-
}
19-
20-
async function getAttributes(rseName: string): Promise<RSEAttributeViewModel> {
21-
const url = `${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/get-rse-attributes?` + new URLSearchParams({ rseName });
22-
const res = await fetch(url);
23-
return await res.json();
24-
}
1+
import { DetailsRSE } from '@/component-library/pages/RSE/details/DetailsRSE';
252

263
export default function Page({ params }: { params: { name: string } }) {
27-
const [rse, setRSE] = useState<RSEViewModel>({ status: 'pending' } as RSEViewModel);
28-
const [protocols, setProtocols] = useState<RSEProtocolViewModel>({ status: 'pending' } as RSEProtocolViewModel);
29-
const [attributes, setAttributes] = useState<RSEAttributeViewModel>({ status: 'pending' } as RSEAttributeViewModel);
30-
useEffect(() => {
31-
getRSE(params.name).then(setRSE);
32-
}, [params.name]);
33-
useEffect(() => {
34-
getProtocols(params.name).then(setProtocols);
35-
}, [params.name]);
36-
useEffect(() => {
37-
getAttributes(params.name).then(setAttributes);
38-
}, [params.name]);
39-
if (rse.status === 'pending' || protocols.status === 'pending' || attributes.status === 'pending') {
40-
return <Loading title="View RSE" subtitle={`For RSE ${params.name}`} />;
41-
}
42-
return <PageRSEStory rse={rse} rseblockstate={0 as RSEBlockState} protocols={protocols} attributes={attributes} />;
4+
return <DetailsRSE name={params.name} />;
435
}
6+
7+
export const metadata = {
8+
title: 'RSE - Rucio',
9+
};
+5-54
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,12 @@
11
'use client';
2-
import { PageRule as PageRuleStory } from '@/component-library/pages/legacy/Rule/PageRule';
3-
import { fixtureRuleMetaViewModel } from 'test/fixtures/table-fixtures';
4-
import { useState, useEffect } from 'react';
5-
import useComDOM from '@/lib/infrastructure/hooks/useComDOM';
6-
import { HTTPRequest } from '@/lib/sdk/http';
7-
import { RuleMetaViewModel, RulePageLockEntryViewModel } from '@/lib/infrastructure/data/view-model/rule';
8-
import { getSiteHeader } from '@/app/(rucio)/queries';
9-
import { SiteHeaderViewModel } from '@/lib/infrastructure/data/view-model/site-header';
102

11-
export default function PageRule({ params }: { params: { id: string } }) {
12-
const comDOM = useComDOM<RulePageLockEntryViewModel>('rule-page-lock-query', [], false, Infinity, 50, true);
13-
const [isAdmin, setIsAdmin] = useState<boolean>(false);
14-
useEffect(() => {
15-
getSiteHeader().then((vm: SiteHeaderViewModel) => setIsAdmin(vm.activeAccount?.role === 'admin'));
16-
}, []);
17-
const [meta, setMeta] = useState<RuleMetaViewModel>({} as RuleMetaViewModel);
3+
import { DetailsRule } from '@/component-library/pages/Rule/details/DetailsRule';
4+
import { useEffect } from 'react';
185

6+
export default function PageRule({ params }: { params: { id: string } }) {
197
useEffect(() => {
20-
// TODO get from mock endpoint
21-
fetch(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/mock-get-rule-meta`)
22-
.then(res => {
23-
if (res.ok) {
24-
return res.json();
25-
}
26-
throw new Error(res.statusText);
27-
})
28-
.then(data => {
29-
setMeta({ ...data, id: params.id });
30-
})
31-
.catch(err => {
32-
console.error(err);
33-
});
34-
// setMeta({ ...fixtureRuleMetaViewModel(), id: params.id })
8+
document.title = 'Rule - Rucio';
359
}, []);
3610

37-
useEffect(() => {
38-
const runQuery = async () => {
39-
const request: HTTPRequest = {
40-
url: new URL(`${process.env.NEXT_PUBLIC_WEBUI_HOST}/api/feature/mock-list-rule-page-lock`),
41-
method: 'GET',
42-
headers: new Headers({
43-
'Content-Type': 'application/json',
44-
} as HeadersInit),
45-
body: null,
46-
};
47-
await comDOM.setRequest(request);
48-
};
49-
runQuery();
50-
}, []);
51-
return (
52-
<PageRuleStory
53-
ruleMeta={meta}
54-
ruleLocks={comDOM}
55-
ruleBoostFunc={() => {
56-
console.log('boost not implemented');
57-
}}
58-
ruleBoostShow={isAdmin}
59-
/>
60-
);
11+
return <DetailsRule id={params.id} />;
6112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { ReplicaState } from '@/lib/core/entity/rucio';
2+
import React from 'react';
3+
import { Badge } from '@/component-library/atoms/misc/Badge';
4+
import { cn } from '@/component-library/utils';
5+
6+
const stateString: Record<ReplicaState, string> = {
7+
Available: 'Available',
8+
Bad: 'Bad',
9+
Being_Deleted: 'Being Deleted',
10+
Copying: 'Copying',
11+
Temporary_Unavailable: 'Temporary Unavailable',
12+
Unavailable: 'Unavailable',
13+
Unknown: 'Unknown',
14+
};
15+
16+
const stateColorClasses: Record<ReplicaState, string> = {
17+
Available: 'bg-base-success-500',
18+
Bad: 'bg-base-error-500',
19+
Being_Deleted: 'bg-base-error-300',
20+
Copying: 'bg-base-info-500',
21+
Temporary_Unavailable: 'bg-base-warning-400',
22+
Unavailable: 'bg-neutral-0 dark:bg-neutral-800',
23+
Unknown: 'bg-neutral-0 dark:bg-neutral-800',
24+
};
25+
26+
export const ReplicaStateBadge = (props: { value: ReplicaState; className?: string }) => {
27+
const classes = cn(stateColorClasses[props.value], props.className);
28+
return <Badge value={stateString[props.value]} className={classes} />;
29+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { cn } from '@/component-library/utils';
2+
import { Badge } from '@/component-library/atoms/misc/Badge';
3+
import React from 'react';
4+
5+
export const NullBadge = (props: { className?: string }) => {
6+
const classes = cn('bg-neutral-200 text-neutral-400 dark:bg-neutral-800 dark:text-neutral-600', props.className);
7+
return <Badge value="null" className={classes} />;
8+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import { Badge } from '@/component-library/atoms/misc/Badge';
3+
import { cn } from '@/component-library/utils';
4+
5+
export const RSEAvailabilityBadge = (props: { operation: string; className?: string }) => {
6+
const classes = cn('bg-neutral-200 dark:bg-neutral-800', props.className);
7+
return <Badge value={props.operation} className={classes} />;
8+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { RuleGrouping } from '@/lib/core/entity/rucio';
2+
import React from 'react';
3+
import { Badge } from '@/component-library/atoms/misc/Badge';
4+
import { cn } from '@/component-library/utils';
5+
6+
const groupingString: Record<RuleGrouping, string> = {
7+
A: 'All',
8+
D: 'Dataset',
9+
N: 'None',
10+
};
11+
12+
const groupingColorClasses: Record<RuleGrouping, string> = {
13+
A: 'bg-base-success-500',
14+
D: 'bg-base-warning-400',
15+
N: 'bg-base-error-500',
16+
};
17+
18+
export const RuleGroupingBadge = (props: { value: RuleGrouping; className?: string }) => {
19+
const classes = cn(groupingColorClasses[props.value], props.className);
20+
return <Badge value={groupingString[props.value]} className={classes} />;
21+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { RuleNotification } from '@/lib/core/entity/rucio';
2+
import React from 'react';
3+
import { Badge } from '@/component-library/atoms/misc/Badge';
4+
import { cn } from '@/component-library/utils';
5+
6+
const notificationString: Record<RuleNotification, string> = {
7+
C: 'Close',
8+
N: 'No',
9+
P: 'Progress',
10+
Y: 'Yes',
11+
};
12+
13+
const notificationColorClasses: Record<RuleNotification, string> = {
14+
Y: 'bg-base-success-500',
15+
P: 'bg-base-info-500',
16+
C: 'bg-base-warning-400',
17+
N: 'bg-base-error-500',
18+
};
19+
20+
export const RuleNotificationBadge = (props: { value: RuleNotification; className?: string }) => {
21+
const classes = cn(notificationColorClasses[props.value], props.className);
22+
return <Badge value={notificationString[props.value]} className={classes}></Badge>;
23+
};

src/component-library/features/key-value/KeyValueRow.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const KeyValueRow = (props: { name: string; children: ReactNode }) => {
55
return (
66
<div className="flex w-full items-center h-12">
77
<span className={cn('text-neutral-700 dark:text-neutral-300', 'min-w-[10rem] pr-3', 'font-medium text-right text-sm')}>{props.name}</span>
8-
<span className="flex items-center">{props.children}</span>
8+
<span className="flex items-center space-x-2">{props.children}</span>
99
</div>
1010
);
1111
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { DateISO } from '@/lib/core/entity/rucio';
2+
import Checkbox from '@/component-library/atoms/form/Checkbox';
3+
import React from 'react';
4+
import { formatDate } from '@/component-library/features/utils/text-formatters';
5+
import { NullBadge } from '@/component-library/features/badges/NullBadge';
6+
7+
const isDateISO = (value: unknown): value is DateISO => {
8+
if (typeof value !== 'string') return false;
9+
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|([+-]\d{2}:\d{2}))$/;
10+
return isoDateRegex.test(value);
11+
};
12+
13+
export const AttributeCell = ({ value }: { value: string | DateISO | number | boolean | null }) => {
14+
if (value === null) {
15+
return <NullBadge />;
16+
} else if (typeof value === 'boolean') {
17+
return <Checkbox checked={value} />;
18+
} else if (isDateISO(value)) {
19+
return formatDate(value);
20+
} else {
21+
return value;
22+
}
23+
};

0 commit comments

Comments
 (0)