Skip to content

Commit 63ab68c

Browse files
committed
Generalized overview pages, added actions to overview pages, harmonized hover behaviour and click indicators on overviews
1 parent 10af3d6 commit 63ab68c

File tree

10 files changed

+361
-165
lines changed

10 files changed

+361
-165
lines changed

app/base-repo/(overview)/page.tsx

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,55 @@
11
import RepositoryStats from "@/app/base-repo/components/Dashboard/RepositoryStats";
22
import LatestActivities from "@/app/base-repo/components/Dashboard/LatestActivities";
33
import * as React from 'react';
4-
import {Suspense} from 'react';
5-
import Breadcrumbs from "@/components/Breadcrumbs/Breadcrumbs";
64
import LatestActivitiesSkeleton from "@/app/base-repo/components/Dashboard/LatestActivitiesSkeleton";
75
import RepositoryStatsSkeleton from "@/app/base-repo/components/Dashboard/RepositoryStatsSkeleton";
8-
import SectionCaption from "@/components/SectionCaption/SectionCaption";
6+
import {OverviewPage} from "@/components/OverviewPage/OverviewPage";
7+
import {getServerSession, Session} from "next-auth";
8+
import {authOptions} from "@/pages/api/auth/[...nextauth]";
99

1010
export default async function Page() {
11+
const basePath: string = ((withBasePath && process.env.NEXT_PUBLIC_BASE_PATH) ? process.env.NEXT_PUBLIC_BASE_PATH : "");
12+
let session:Session | undefined = await getServerSession(authOptions) as Session;
1113

12-
return (
13-
<main>
14-
<Breadcrumbs
15-
breadcrumbs={[
16-
{label: "Overview", href: '/base-repo', active: true},
17-
]}
18-
/>
19-
<SectionCaption caption={"Overview"}/>
20-
21-
<div className="grid gap-6 mt-6 sm:grid-cols-2 lg:grid-cols-2">
22-
<div className="flex w-full flex-col">
23-
<SectionCaption caption={"Usage Statistics"} level={"h2"}/>
14+
const actions = [
15+
{
16+
icon: "add",
17+
title: "Create Document",
18+
subtitle: "Start a new document",
19+
href: `${basePath}/base-repo/resources/create`,
20+
requiresAuth: true,
21+
},
22+
{
23+
icon: "list",
24+
title: "List Documents",
25+
subtitle: "View all documents",
26+
href: `${basePath}/base-repo/resources`,
27+
requiresAuth: false,
28+
},
29+
{
30+
icon: "search",
31+
title: "Search",
32+
subtitle: "Search using the site search",
33+
href: `${basePath}/search`,
34+
requiresAuth: false,
35+
},
36+
];
2437

25-
<div className="grid grid-cols-2 gap-4 px-4 py-8">
26-
<Suspense fallback={<RepositoryStatsSkeleton/>}>
27-
<RepositoryStats/>
28-
</Suspense>
29-
</div>
30-
</div>
38+
const availableActions = actions.filter(action => {
39+
return !(action.requiresAuth && !session);
40+
});
3141

32-
<div className="flex w-full flex-col">
33-
<SectionCaption caption={"Latest Activities"} level={"h2"}/>
34-
35-
<div className="flex gap-4 px-4 py-8 grow flex-col justify-between rounded-xl ">
36-
<Suspense fallback={<LatestActivitiesSkeleton/>}>
37-
<LatestActivities/>
38-
</Suspense>
39-
</div>
40-
</div>
41-
</div>
42-
</main>
42+
return (
43+
<OverviewPage
44+
title="Overview"
45+
breadcrumbs={[
46+
{label: "Overview", href: '/base-repo', active: true}
47+
]}
48+
stats={<RepositoryStats />}
49+
statsFallback={<RepositoryStatsSkeleton />}
50+
activities={<LatestActivities />}
51+
activitiesFallback={<LatestActivitiesSkeleton />}
52+
actions={availableActions}
53+
/>
4354
);
4455
}

app/base-repo/components/Dashboard/RepositoryStats.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default async function RepositoryStats() {
2222
{
2323
"text": "Resources",
2424
"value": formatNumber(resources),
25-
"icon": "fluent-emoji-high-contrast:card-index-dividers"
25+
"icon": "grommet-icons:unordered-list"
2626
},
2727
{
2828
"text": "Files",
@@ -47,12 +47,12 @@ export default async function RepositoryStats() {
4747
]
4848

4949
return (
50-
<>
51-
{stats.map((stat, i) => {
50+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-3">
51+
{stats.map((stat, i) => {
5252
return (
5353
<InfoCard key={i} icon={stat.icon} value={stat.value} text={stat.text}/>
5454
);
5555
})}
56-
</>
56+
</div>
5757
);
5858
}

app/mapping/(overview)/page.tsx

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,41 @@ import Breadcrumbs from "@/components/Breadcrumbs/Breadcrumbs";
44
import SectionCaption from "@/components/SectionCaption/SectionCaption";
55
import MappingServiceStats from "@/app/mapping/components/Dashboard/MappingServiceStats";
66
import MappingServiceStatsSkeleton from "@/app/mapping/components/Dashboard/MappingServiceStatsSkeleton";
7+
import {getServerSession, Session} from "next-auth";
8+
import {authOptions} from "@/pages/api/auth/[...nextauth]";
9+
import {OverviewPage} from "@/components/OverviewPage/OverviewPage";
10+
import RepositoryStats from "@/app/base-repo/components/Dashboard/RepositoryStats";
11+
import RepositoryStatsSkeleton from "@/app/base-repo/components/Dashboard/RepositoryStatsSkeleton";
12+
import LatestActivities from "@/app/base-repo/components/Dashboard/LatestActivities";
13+
import LatestActivitiesSkeleton from "@/app/base-repo/components/Dashboard/LatestActivitiesSkeleton";
714

815
export default async function Page() {
9-
return (
10-
<main>
11-
<Breadcrumbs
12-
breadcrumbs={[
13-
{label: "Overview", href: '/mapping', active: true},
14-
]}
15-
/>
16-
<SectionCaption caption={"Overview"}/>
16+
const basePath: string = ((withBasePath && process.env.NEXT_PUBLIC_BASE_PATH) ? process.env.NEXT_PUBLIC_BASE_PATH : "");
17+
let session:Session | undefined = await getServerSession(authOptions) as Session;
18+
19+
const actions = [
20+
{
21+
icon: "add",
22+
title: "Create Mapping",
23+
subtitle: "Start a new mapping",
24+
href: `${basePath}/mapping/map`,
25+
requiresAuth: true,
26+
}
27+
];
1728

18-
<div className="grid gap-6 mt-6 sm:grid-cols-2 lg:grid-cols-2">
19-
<div className="flex w-full flex-col">
20-
<SectionCaption caption={"Usage Statistics"} level={"h2"}/>
29+
const availableActions = actions.filter(action => {
30+
return !(action.requiresAuth && !session);
31+
});
2132

22-
<div className="grid grid-cols-3 gap-4 px-4 py-8">
23-
<Suspense fallback={<MappingServiceStatsSkeleton/>}>
24-
<MappingServiceStats/>
25-
</Suspense>
26-
</div>
27-
</div>
28-
</div>
29-
</main>
33+
return (
34+
<OverviewPage
35+
title="Overview"
36+
breadcrumbs={[
37+
{label: "Overview", href: '/mapping', active: true}
38+
]}
39+
stats={<RepositoryStats />}
40+
statsFallback={<RepositoryStatsSkeleton />}
41+
actions={availableActions}
42+
/>
3043
);
3144
}

app/metastore/(overview)/page.tsx

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,50 @@
11
import LatestActivities from "@/app/base-repo/components/Dashboard/LatestActivities";
22
import * as React from 'react';
3-
import {Suspense} from 'react';
4-
import Breadcrumbs from "@/components/Breadcrumbs/Breadcrumbs";
53
import LatestActivitiesSkeleton from "@/app/base-repo/components/Dashboard/LatestActivitiesSkeleton";
64
import RepositoryStatsSkeleton from "@/app/base-repo/components/Dashboard/RepositoryStatsSkeleton";
7-
import SectionCaption from "@/components/SectionCaption/SectionCaption";
85
import MetastoreStats from "@/app/metastore/components/Dashboard/MetastoreStats";
6+
import {OverviewPage} from "@/components/OverviewPage/OverviewPage";
97

108
export default async function Page() {
11-
return (
12-
<main>
13-
<Breadcrumbs
14-
breadcrumbs={[
15-
{label: "Overview", href: '/metastore', active: true},
16-
]}
17-
/>
18-
<SectionCaption caption={"Overview"}/>
19-
20-
<div className="grid gap-6 mt-6 sm:grid-cols-2 lg:grid-cols-2">
21-
<div className="flex w-full flex-col">
22-
<SectionCaption caption={"Usage Statistics"} level={"h2"}/>
9+
const actions = [
10+
{
11+
icon: "add",
12+
title: "Create Schema",
13+
subtitle: "Start a new schema",
14+
href: `${basePath}/metastore/schemas/create`,
15+
requiresAuth: true,
16+
},
17+
{
18+
icon: "list",
19+
title: "List Documents",
20+
subtitle: "View all documents",
21+
href: `${basePath}/metastore/metadata`,
22+
requiresAuth: false,
23+
},
24+
{
25+
icon: "search",
26+
title: "Search",
27+
subtitle: "Search using the site search",
28+
href: `${basePath}/search`,
29+
requiresAuth: false,
30+
},
31+
];
2332

24-
<div className="grid grid-cols-2 gap-4 px-4 py-8">
25-
<Suspense fallback={<RepositoryStatsSkeleton/>}>
26-
<MetastoreStats/>
27-
</Suspense>
28-
</div>
29-
</div>
33+
const availableActions = actions.filter(action => {
34+
return !(action.requiresAuth && !session);
35+
});
3036

31-
<div className="flex w-full flex-col">
32-
<SectionCaption caption={"Latest Activities"} level={"h2"}/>
33-
34-
<div className="grid grid-cols-1 gap-4 px-4 py-8">
35-
<Suspense fallback={<LatestActivitiesSkeleton/>}>
36-
<LatestActivities/>
37-
</Suspense>
38-
</div>
39-
</div>
40-
</div>
41-
</main>
37+
return (
38+
<OverviewPage
39+
title="Overview"
40+
breadcrumbs={[
41+
{label: "Overview", href: '/metastore', active: true}
42+
]}
43+
stats={<MetastoreStats />}
44+
statsFallback={<RepositoryStatsSkeleton />}
45+
activities={<LatestActivities />}
46+
activitiesFallback={<LatestActivitiesSkeleton />}
47+
actions={availableActions}
48+
/>
4249
);
4350
}

components/ActivityList/ActivityList.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,37 @@ export function ActivityList ({activities}: {
88
activities: Array<Activity>;
99
}) {
1010
return (
11-
<div className="w-full max-w-3xl mx-auto rounded-lg border bg-card text-card-foreground shadow-sm overflow-y-auto h-[400px]">
11+
<div className="w-full max-w-3xl mx-auto rounded-lg border bg-card shadow-sm overflow-y-auto max-h-[400px]">
1212
{activities.map((activity, index) => (
13-
<div key={index} className="flex items-start border-b last:border-0">
14-
{/* Icon Section */}
15-
<div className="w-1/5 flex items-center bg-card p-3 brightness-150 justify-center h-full">
16-
<Icon icon={activity.icon} width={"30"} height={"30"} className={"mb-4"} />
13+
<div
14+
key={index}
15+
className="flex gap-3 px-3 py-2 border-b last:border-0 items-start sm:items-center">
16+
{/* Icon */}
17+
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted">
18+
<Icon
19+
icon={activity.icon}
20+
className="h-4 w-4"
21+
/>
1722
</div>
1823

19-
{/* Title and Subtitle Section */}
20-
<div className="w-2/5 pl-4 mb-2">
21-
<div className="text-l font-semibold text-card-800 mt-2">{activity.title}</div>
22-
<div className="text-sm text-gray-600">{activity.subtitle}</div>
24+
{/* Text */}
25+
<div className="flex min-w-0 flex-1 flex-col">
26+
<div className="text-sm font-medium text-foreground leading-tight truncate sm:truncate-none">
27+
{activity.title}
28+
</div>
29+
<div className="text-xs text-muted-foreground leading-tight truncate">
30+
{activity.subtitle}
31+
</div>
2332
</div>
2433

25-
{/* Date Section */}
26-
<div className="w-2/5 text-right text-sm text-gray-500 m-2">
34+
{/* Date */}
35+
<div className="hidden sm:block shrink-0 text-xs text-muted-foreground">
2736
{activity.date}
2837
</div>
2938
</div>
3039
))}
3140
</div>
41+
3242
);
3343
};
3444

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"use client";
2+
3+
import Link from "next/link";
4+
import {Icon} from "@iconify-icon/react";
5+
import React from "react";
6+
7+
type ActionCardProps = {
8+
icon: "add" | "list" | "upload" | "search"; // The icon name, e.g., plus
9+
title: string; // main label
10+
subtitle?: string; // optional smaller text
11+
href: string; // ref
12+
className?: string;
13+
};
14+
15+
const iconMap = {
16+
add: "grommet-icons:new",
17+
list: "grommet-icons:unordered-list",
18+
upload: "grommet-icons:upload",
19+
search: "grommet-icons:search"
20+
};
21+
22+
export function ActionCard({ icon, title, subtitle, href, className }: ActionCardProps) {
23+
const iconName = iconMap[icon];
24+
25+
return (
26+
<Link href={href || "#"}>
27+
<div
28+
className={`flex items-center gap-4 rounded-md border bg-accent p-4 shadow-sm hover:shadow-md
29+
hover:scale-105 transition-all duration-150 ${className}`}>
30+
31+
{/* Icon */}
32+
<div className="flex h-12 w-12 items-center justify-center rounded-md bg-muted flex-shrink-0">
33+
<Icon icon={iconName} className="h-4 w-4 text-foreground" />
34+
</div>
35+
36+
{/* Text */}
37+
<div className="flex flex-col justify-center">
38+
<span className="text-base font-semibold text-foreground">{title}</span>
39+
{subtitle && <span className="text-sm text-muted-foreground">{subtitle}</span>}
40+
</div>
41+
</div>
42+
</Link>
43+
);
44+
}

0 commit comments

Comments
 (0)