Skip to content

Commit aff8c97

Browse files
authored
Merge pull request #26 from grycap/ui-homogeneity
Improve UI homogeneity
2 parents 9dee368 + c2f8768 commit aff8c97

File tree

8 files changed

+108
-69
lines changed

8 files changed

+108
-69
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import UserInfo from "@/components/UserInfo";
2+
import useServicesContext from "@/pages/ui/services/context/ServicesContext";
3+
import OscarColors, { OscarStyles } from "@/styles";
4+
import { RefreshCcwIcon } from "lucide-react";
5+
import { Link } from "react-router-dom";
6+
7+
interface IntegratedAppProps {
8+
appName: string;
9+
DeployInstancePopover: React.ComponentType;
10+
}
11+
12+
function IntegratedAppTopbar({ appName, DeployInstancePopover }: IntegratedAppProps) {
13+
const { refreshServices } = useServicesContext();
14+
return (
15+
<div
16+
style={{
17+
borderBottom: OscarStyles.border,
18+
}}
19+
className="grid grid-cols-[1fr_auto] h-[64px]"
20+
>
21+
<div
22+
style={{
23+
padding: "0 16px",
24+
}}
25+
className={"grid items-center justify-between gap-2 grid-cols-[auto_auto]"}
26+
>
27+
<div className="flex flex-row items-center gap-3">
28+
<Link
29+
to=""
30+
style={{
31+
color: OscarColors.DarkGrayText,
32+
fontSize: 18,
33+
textDecoration: "none",
34+
}}
35+
>{appName}</Link>
36+
37+
<Link to=""
38+
onClick={() => refreshServices()}
39+
>
40+
<RefreshCcwIcon size={18}
41+
onMouseEnter={(e) => {e.currentTarget.style.transform = 'rotate(90deg)'}}
42+
onMouseLeave={(e) => {e.currentTarget.style.transform = 'rotate(0deg)'}}
43+
/>
44+
</Link>
45+
</div>
46+
<DeployInstancePopover />
47+
</div>
48+
<UserInfo />
49+
</div>
50+
);
51+
}
52+
53+
export default IntegratedAppTopbar;

src/components/IntegratedApp/index.tsx

Lines changed: 30 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import { Service } from "@/pages/ui/services/models/service";
22
import GenericTable from "../Table";
33
import { useNavigate } from "react-router-dom";
4-
import { Copy, Edit, MoreVertical, RefreshCcwIcon, Trash2 } from "lucide-react";
4+
import { Edit, MoreVertical, RefreshCcwIcon, Trash2 } from "lucide-react";
55
import OscarColors from "@/styles";
66
import useServicesContext from "@/pages/ui/services/context/ServicesContext";
77
import { Button } from "../ui/button";
8-
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "../ui/card";
98
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "../ui/dropdown-menu";
109
import { useState } from "react";
1110
import getServicesApi from "@/api/services/getServicesApi";
1211
import deleteServiceApi from "@/api/services/deleteServiceApi";
1312
import { alert } from "@/lib/alert";
14-
import DeleteDialog from "../DeleteDialog";
1513
import updateServiceApi from "@/api/services/updateServiceApi";
16-
import { useAuth } from "@/contexts/AuthContext";
1714
import ServiceRedirectButton from "../ServiceRedirectButton";
15+
import IntegratedAppTopbar from "./components/Topbar";
16+
import DeleteDialog from "../DeleteDialog";
1817

1918
interface IntegratedAppProps {
2019
appName: string;
@@ -28,8 +27,6 @@ function IntegratedApp({ appName, endpoint, filteredServices, additionalExposedP
2827
const { setFormService } = useServicesContext();
2928
const [servicesToDelete, setServicesToDelete] = useState<Service[]>([]);
3029
const { setServices } = useServicesContext();
31-
const authContext = useAuth();
32-
3330

3431
const navigate = useNavigate();
3532

@@ -99,40 +96,23 @@ function IntegratedApp({ appName, endpoint, filteredServices, additionalExposedP
9996
}
10097

10198
return (
102-
<div className="grid grid-cols-1 gap-6 w-[95%] sm:w-[90%] lg:w-[80%] mx-auto mt-[40px] min-w-[300px] max-w-[1600px] content-start">
103-
<div className="text-center sm:text-left flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3 mb-3">
104-
<h1 className="" style={{ fontSize: "24px", fontWeight: "500" }}>{appName}</h1>
105-
<div className="grid grid-cols-1 xl:grid-cols-[auto_auto] text-md text-decoration-underline-hover"
106-
onClick={() => {
107-
navigator.clipboard.writeText(authContext.authData.endpoint);
108-
alert.success("Endpoint copied to clipboard");
109-
}}
110-
style={{
111-
cursor: "pointer",
112-
}}
99+
<div className="">
100+
<IntegratedAppTopbar appName={appName} DeployInstancePopover={DeployInstancePopover} />
101+
<div
102+
style={{
103+
display: "flex",
104+
flexDirection: "column",
105+
flexGrow: 1,
106+
flexBasis: 0,
107+
overflow: "hidden",
108+
}}
113109
>
114-
<div className="truncate">
115-
{`${authContext.authData.user} -\u00A0`}
116-
</div>
117-
<div className="flex flex-row items-center justify-center gap-2 truncate">
118-
{`${authContext.authData.endpoint}`}
119-
<Copy className="h-4 w-4" />
120-
</div>
121-
</div>
122-
</div>
123-
<Card>
124-
<CardHeader>
125-
<CardTitle className="flex flex-row items-center justify-between gap-2">
126-
<div>Deployed {appName} Instances</div>
127-
<DeployInstancePopover />
128-
</CardTitle>
129-
</CardHeader>
130-
<CardContent className="flex flex-col max-h-[65vh]">
131110
<GenericTable<Service>
132111
data={filteredServices}
133112
idKey="name"
134113
columns={[
135114
{ header: "Name", accessor: "name", sortBy: "name" },
115+
{ header: "Image", accessor: "image", sortBy: "image" },
136116
{ header: "CPU", accessor: "cpu", sortBy: "cpu" },
137117
{ header: "Memory", accessor: "memory", sortBy: "memory" },
138118
]}
@@ -142,7 +122,7 @@ function IntegratedApp({ appName, endpoint, filteredServices, additionalExposedP
142122
<DropdownMenu>
143123
<DropdownMenuTrigger asChild title="More actions">
144124
<Button variant={"link"} size="icon" tooltipLabel="More Actions">
145-
<MoreVertical size={20}/>
125+
<MoreVertical />
146126
</Button>
147127
</DropdownMenuTrigger>
148128
<DropdownMenuContent align="end" className="w-[220px]">
@@ -175,11 +155,13 @@ function IntegratedApp({ appName, endpoint, filteredServices, additionalExposedP
175155
},
176156
{
177157
button: (service) => (
178-
<ServiceRedirectButton
179-
service={service}
180-
endpoint={endpoint}
181-
additionalExposedPathArgs={additionalExposedPathArgs}
182-
/>
158+
<div className="p-2">
159+
<ServiceRedirectButton
160+
service={service}
161+
endpoint={endpoint}
162+
additionalExposedPathArgs={additionalExposedPathArgs}
163+
/>
164+
</div>
183165
),
184166
},
185167
{
@@ -190,7 +172,7 @@ function IntegratedApp({ appName, endpoint, filteredServices, additionalExposedP
190172
onClick={() => setServicesToDelete([service])}
191173
tooltipLabel="Delete"
192174
>
193-
<Trash2 color={OscarColors.Red} size={20} />
175+
<Trash2 color={OscarColors.Red} />
194176
</Button>
195177
),
196178
},
@@ -218,17 +200,13 @@ function IntegratedApp({ appName, endpoint, filteredServices, additionalExposedP
218200
},
219201
]}
220202
/>
221-
</CardContent>
222-
<CardFooter className="grid grid-cols-1 hidden">
223-
224-
</CardFooter>
225-
</Card>
226-
<DeleteDialog
227-
isOpen={servicesToDelete.length > 0}
228-
onClose={() => setServicesToDelete([])}
229-
onDelete={handleDeleteService}
230-
itemNames={servicesToDelete.map((service) => service.name)}
231-
/>
203+
<DeleteDialog
204+
isOpen={servicesToDelete.length > 0}
205+
onClose={() => setServicesToDelete([])}
206+
onDelete={handleDeleteService}
207+
itemNames={servicesToDelete.map((service) => service.name)}
208+
/>
209+
</div>
232210
</div>
233211
);
234212
}

src/components/ServiceRedirectButton/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ function ServiceRedirectButton({ service, endpoint, additionalExposedPathArgs }:
5858
}`}
5959
target="_blank"
6060
>
61-
<ExternalLink size={20} />
61+
<ExternalLink />
6262
</Link>
6363
) : (
6464
<Loader2 className="animate-spin" color={OscarColors.DarkGrayText} />

src/pages/ui/flows/components/FlowsFormPopover/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Service } from "@/pages/ui/services/models/service";
1212
import createServiceApi from "@/api/services/createServiceApi";
1313
import useServicesContext from "@/pages/ui/services/context/ServicesContext";
1414
import { useMinio } from "@/contexts/Minio/MinioContext";
15-
import { Info, RefreshCcwIcon } from "lucide-react";
15+
import { Info, Plus, RefreshCcwIcon } from "lucide-react";
1616
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
1717
import RequestButton from "@/components/RequestButton";
1818
import { generateReadableName, genRandomString } from "@/lib/utils";
@@ -182,10 +182,11 @@ function FlowsFormPopover() {
182182
<Dialog open={isOpen} onOpenChange={setIsOpen}>
183183
<DialogTrigger asChild>
184184
<Button
185-
variant="default"
186-
tooltipLabel="New Node-RED Instance"
185+
variant="mainGreen"
186+
tooltipLabel="New Flow Instance"
187187
onClick={() => {setIsOpen(false)}}
188188
>
189+
<Plus size={20} className="mr-2" />
189190
New
190191
</Button>
191192
</DialogTrigger>

src/pages/ui/flows/index.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ function FlowsView() {
99
const { services } = useServicesContext();
1010

1111
const ownerName = authData?.egiSession?.sub ?? authData?.token ?? (authData?.user === "oscar" ? "cluster_admin" : authData?.user);
12-
const flowsService = services.filter((service) => service.owner === ownerName && service.labels["node_red"] === "true");
12+
const flowsService = services.filter((service) => (service.owner === ownerName || ownerName === "cluster_admin") && service.labels["node_red"] === "true");
1313

1414
useEffect(() => {
1515
document.title ="OSCAR - Flows"
1616
});
1717

1818
return (
19-
<>
20-
<IntegratedApp appName="Node-RED" endpoint={authData.endpoint} filteredServices={flowsService} DeployInstancePopover={FlowsFormPopover} />
21-
</>
19+
<div className="flex flex-col h-full w-full">
20+
<IntegratedApp
21+
appName="Flows"
22+
endpoint={authData.endpoint}
23+
filteredServices={flowsService}
24+
DeployInstancePopover={FlowsFormPopover}
25+
/>
26+
</div>
2227
);
2328
}
2429

src/pages/ui/juno/components/JunoFormPopover/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import yamlToServices from "@/pages/ui/services/components/FDL/utils/yamlToServi
1313
import useServicesContext from "@/pages/ui/services/context/ServicesContext";
1414
import { Service } from "@/pages/ui/services/models/service";
1515
import OscarColors from "@/styles";
16-
import { RefreshCcwIcon } from "lucide-react";
16+
import { Plus, RefreshCcwIcon } from "lucide-react";
1717
import { useEffect, useState } from "react";
1818

1919

@@ -163,10 +163,11 @@ return (
163163
<Dialog open={isOpen} onOpenChange={setIsOpen}>
164164
<DialogTrigger asChild>
165165
<Button
166-
variant="default"
167-
tooltipLabel="New Jupyter Notebook Instance"
166+
variant="mainGreen"
167+
tooltipLabel="New Notebook Instance"
168168
onClick={() => {setIsOpen(false)}}
169169
>
170+
<Plus size={20} className="mr-2" />
170171
New
171172
</Button>
172173
</DialogTrigger>

src/pages/ui/juno/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,22 @@ function JunoView() {
1515
const { services } = useServicesContext();
1616

1717
const ownerName = authData?.egiSession?.sub ?? authData?.token ?? (authData?.user === "oscar" ? "cluster_admin" : authData?.user);
18-
const junoService = services.filter((service) => service.owner === ownerName && service.labels["jupyter_notebook"] === "true");
18+
const junoService = services.filter((service) => (service.owner === ownerName || ownerName === "cluster_admin") && service.labels["jupyter_notebook"] === "true");
1919

2020
useEffect(() => {
2121
document.title ="OSCAR - Notebooks"
2222
});
2323

2424
return (
25-
<>
25+
<div className="flex flex-col h-full w-full">
2626
<IntegratedApp
27-
appName="Jupyter Notebook"
27+
appName="Notebook"
2828
endpoint={authData.endpoint}
2929
filteredServices={junoService}
3030
DeployInstancePopover={JunoFormPopover}
3131
additionalExposedPathArgs="?token={{JUPYTER_TOKEN}}"
3232
/>
33-
</>
33+
</div>
3434
);
3535
}
3636

src/pages/ui/minio/components/Topbar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useEffect, useMemo, useState } from "react";
22
import { Link } from "react-router-dom";
33
import { Database, FolderRoot, Slash } from "lucide-react";
4-
import { OscarStyles } from "@/styles";
4+
import OscarColors, { OscarStyles } from "@/styles";
55
import UserInfo from "@/components/UserInfo";
66
import AddBucketButton from "./AddBucketButton";
77
import AddFolderButton from "./AddFolderButton";
@@ -89,6 +89,7 @@ function MinioTopbar() {
8989
>
9090
<div className="grid grid-cols-1 items-center pl-4 text-gray-600 text-lg no-underline">
9191
<Link
92+
style={{ color: OscarColors.DarkGrayText }}
9293
to="/ui/minio"
9394
aria-label="Navigate to Buckets"
9495
className="no-underline hover:text-gray-800 transition-colors duration-200 flex items-center gap-2"
@@ -120,7 +121,7 @@ function MinioTopbar() {
120121
}
121122
</div>
122123

123-
<div className="flex flex-row items-center gap-2 pl-2 pr-2">
124+
<div className="flex flex-row items-center gap-2 pl-2">
124125
{isOnRoot ?
125126
<AddBucketButton bucket={bucket} create={true} />
126127
:

0 commit comments

Comments
 (0)