Spaces:
Sleeping
Sleeping
MingruiZhang
commited on
feat: class list in project viewer (#29)
Browse files<img width="1904" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/5669963/ed78e0f0-1bed-4480-aa0d-1d40a3b4d9b2">
- app/project/[projectId]/page.tsx +7 -2
- components/project/ClassBar.tsx +22 -0
- components/project/MediaGrid.tsx +2 -2
- components/ui/Chip.tsx +8 -5
- lib/fetch/index.ts +20 -0
app/project/[projectId]/page.tsx
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
import MediaGrid from '@/components/project/MediaGrid';
|
2 |
-
import { fetchProjectMedia } from '@/lib/fetch';
|
3 |
import ProjectChat from '@/components/project/ProjectChat';
|
|
|
4 |
|
5 |
interface PageProps {
|
6 |
params: {
|
@@ -11,12 +12,16 @@ interface PageProps {
|
|
11 |
export default async function Page({ params }: PageProps) {
|
12 |
const { projectId } = params;
|
13 |
|
14 |
-
const mediaList = await
|
|
|
|
|
|
|
15 |
|
16 |
return (
|
17 |
<div className="pt-4 md:pt-10 h-full">
|
18 |
<div className="flex h-full">
|
19 |
<div className="w-1/2 relative border-r border-gray-300 overflow-auto">
|
|
|
20 |
<MediaGrid mediaList={mediaList} />
|
21 |
</div>
|
22 |
<div className="w-1/2 relative overflow-auto">
|
|
|
1 |
import MediaGrid from '@/components/project/MediaGrid';
|
2 |
+
import { fetchProjectClass, fetchProjectMedia } from '@/lib/fetch';
|
3 |
import ProjectChat from '@/components/project/ProjectChat';
|
4 |
+
import ClassBar from '@/components/project/ClassBar';
|
5 |
|
6 |
interface PageProps {
|
7 |
params: {
|
|
|
12 |
export default async function Page({ params }: PageProps) {
|
13 |
const { projectId } = params;
|
14 |
|
15 |
+
const [mediaList, classList] = await Promise.all([
|
16 |
+
fetchProjectMedia({ projectId: Number(projectId) }),
|
17 |
+
fetchProjectClass({ projectId: Number(projectId) }),
|
18 |
+
]);
|
19 |
|
20 |
return (
|
21 |
<div className="pt-4 md:pt-10 h-full">
|
22 |
<div className="flex h-full">
|
23 |
<div className="w-1/2 relative border-r border-gray-300 overflow-auto">
|
24 |
+
<ClassBar classList={classList} />
|
25 |
<MediaGrid mediaList={mediaList} />
|
26 |
</div>
|
27 |
<div className="w-1/2 relative overflow-auto">
|
components/project/ClassBar.tsx
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ClassDetails } from '@/lib/fetch';
|
2 |
+
import Chip from '../ui/Chip';
|
3 |
+
|
4 |
+
export interface ClassBarProps {
|
5 |
+
classList: ClassDetails[];
|
6 |
+
}
|
7 |
+
|
8 |
+
export default async function ClassBar({ classList }: ClassBarProps) {
|
9 |
+
return (
|
10 |
+
<div className="border-b border-gray-300 px-3 pb-3 max-w-3xl mx-auto">
|
11 |
+
{classList.map(classItem => {
|
12 |
+
return (
|
13 |
+
<Chip
|
14 |
+
key={classItem.id}
|
15 |
+
value={classItem.name}
|
16 |
+
className="px-3 py-1 my-1"
|
17 |
+
/>
|
18 |
+
);
|
19 |
+
})}
|
20 |
+
</div>
|
21 |
+
);
|
22 |
+
}
|
components/project/MediaGrid.tsx
CHANGED
@@ -7,8 +7,8 @@ export default function MediaGrid({
|
|
7 |
mediaList: MediaDetails[];
|
8 |
}) {
|
9 |
return (
|
10 |
-
<div className="relative size-full p-
|
11 |
-
<div className="columns-1 sm:columns-1 md:columns-2 lg:columns-2 xl:columns:3 gap-
|
12 |
{mediaList.map(media => (
|
13 |
<MediaTile key={media.id} media={media} />
|
14 |
))}
|
|
|
7 |
mediaList: MediaDetails[];
|
8 |
}) {
|
9 |
return (
|
10 |
+
<div className="relative size-full p-3 max-w-3xl mx-auto">
|
11 |
+
<div className="columns-1 sm:columns-1 md:columns-2 lg:columns-2 xl:columns:3 gap-3 [&>img:not(:first-child)]:mt-3">
|
12 |
{mediaList.map(media => (
|
13 |
<MediaTile key={media.id} media={media} />
|
14 |
))}
|
components/ui/Chip.tsx
CHANGED
@@ -7,14 +7,17 @@ export interface ChipProps {
|
|
7 |
className?: string;
|
8 |
}
|
9 |
|
10 |
-
const Chip: React.FC<ChipProps> = ({
|
|
|
|
|
|
|
|
|
|
|
11 |
return (
|
12 |
<div
|
13 |
className={cn(
|
14 |
-
|
15 |
-
color
|
16 |
-
color === 'yellow' && 'bg-yellow-100 text-yellow-500',
|
17 |
-
color === 'purple' && 'bg-purple-100 text-purple-500',
|
18 |
className,
|
19 |
)}
|
20 |
>
|
|
|
7 |
className?: string;
|
8 |
}
|
9 |
|
10 |
+
const Chip: React.FC<ChipProps> = ({
|
11 |
+
label,
|
12 |
+
value,
|
13 |
+
className,
|
14 |
+
color = 'gray',
|
15 |
+
}) => {
|
16 |
return (
|
17 |
<div
|
18 |
className={cn(
|
19 |
+
'inline-flex items-center px-1.5 rounded-full text-xs mr-2',
|
20 |
+
`bg-${color}-100 text-${color}-500`,
|
|
|
|
|
21 |
className,
|
22 |
)}
|
23 |
>
|
lib/fetch/index.ts
CHANGED
@@ -121,3 +121,23 @@ export const fetchProjectMedia = clefApiBuilder<
|
|
121 |
{ projectId: number },
|
122 |
MediaDetails[]
|
123 |
>('api/admin/vision-agent/project/media');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
{ projectId: number },
|
122 |
MediaDetails[]
|
123 |
>('api/admin/vision-agent/project/media');
|
124 |
+
|
125 |
+
export type ClassDetails = {
|
126 |
+
id: number;
|
127 |
+
name: string;
|
128 |
+
descriptionText?: string | null;
|
129 |
+
orgId: number;
|
130 |
+
projectId: number;
|
131 |
+
createdAt: string;
|
132 |
+
updatedAt: string | null;
|
133 |
+
color?: string | null;
|
134 |
+
};
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Fetch all active classes of a given project
|
138 |
+
* @author https://github.com/landing-ai/landing-platform/blob/mingrui-04-08-meaningful-project/packages/server-clef/src/main_app/controllers/admin/get_admin_meaningful_project_controller.ts
|
139 |
+
*/
|
140 |
+
export const fetchProjectClass = clefApiBuilder<
|
141 |
+
{ projectId: number },
|
142 |
+
ClassDetails[]
|
143 |
+
>('api/admin/vision-agent/project/class');
|