Spaces:
Running
Running
feat: project card more info (#28)
Browse files<img width="1904" alt="image"
src="https://github.com/landing-ai/vision-agent-ui/assets/5669963/8a2ed136-fab5-4a44-9fb9-0ffad32cbc15">
- app/project/layout.tsx +8 -0
- components/project-sidebar/ProjectCard.tsx +32 -11
- components/ui/Chip.tsx +6 -8
- lib/fetch/index.ts +3 -4
- lib/hooks/useVisionAgent.tsx +14 -5
app/project/layout.tsx
CHANGED
|
@@ -1,12 +1,20 @@
|
|
| 1 |
import ProjectListSideBar from '@/components/project-sidebar/ProjectListSideBar';
|
| 2 |
import { Suspense } from 'react';
|
| 3 |
import Loading from '@/components/ui/Loading';
|
|
|
|
|
|
|
| 4 |
|
| 5 |
interface ChatLayoutProps {
|
| 6 |
children: React.ReactNode;
|
| 7 |
}
|
| 8 |
|
| 9 |
export default async function Layout({ children }: ChatLayoutProps) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
return (
|
| 11 |
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
|
| 12 |
<div
|
|
|
|
| 1 |
import ProjectListSideBar from '@/components/project-sidebar/ProjectListSideBar';
|
| 2 |
import { Suspense } from 'react';
|
| 3 |
import Loading from '@/components/ui/Loading';
|
| 4 |
+
import { authEmail } from '@/auth';
|
| 5 |
+
import { redirect } from 'next/navigation';
|
| 6 |
|
| 7 |
interface ChatLayoutProps {
|
| 8 |
children: React.ReactNode;
|
| 9 |
}
|
| 10 |
|
| 11 |
export default async function Layout({ children }: ChatLayoutProps) {
|
| 12 |
+
const { isAdmin } = await authEmail();
|
| 13 |
+
|
| 14 |
+
if (!isAdmin) {
|
| 15 |
+
redirect('/');
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
return (
|
| 19 |
<div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
|
| 20 |
<div
|
components/project-sidebar/ProjectCard.tsx
CHANGED
|
@@ -11,14 +11,25 @@ export interface ProjectCardProps {
|
|
| 11 |
projectInfo: ProjectBaseInfo;
|
| 12 |
}
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
const ProjectCard: React.FC<ProjectCardProps> = ({ projectInfo }) => {
|
| 15 |
-
const {
|
| 16 |
-
|
| 17 |
-
name,
|
| 18 |
-
created_at,
|
| 19 |
-
label_type,
|
| 20 |
-
organization: { name: orgName },
|
| 21 |
-
} = projectInfo;
|
| 22 |
|
| 23 |
const { projectId: projectIdFromParam } = useParams();
|
| 24 |
|
|
@@ -31,10 +42,20 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ projectInfo }) => {
|
|
| 31 |
)}
|
| 32 |
href={`/project/${id}`}
|
| 33 |
>
|
| 34 |
-
<div className="overflow-hidden">
|
| 35 |
-
<
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
</div>
|
| 39 |
</Link>
|
| 40 |
);
|
|
|
|
| 11 |
projectInfo: ProjectBaseInfo;
|
| 12 |
}
|
| 13 |
|
| 14 |
+
export enum LabelType {
|
| 15 |
+
BoundingBox = 'bounding_box',
|
| 16 |
+
Segmentation = 'segmentation',
|
| 17 |
+
Classification = 'classification',
|
| 18 |
+
AnomalyDetection = 'anomaly_detection',
|
| 19 |
+
SegmentationInstantLearning = 'segmentation_instant_learning',
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
const LabelTypeDisplayText: { [key: string]: string } = {
|
| 23 |
+
[LabelType.BoundingBox]: 'detection',
|
| 24 |
+
[LabelType.Segmentation]: 'segmentation',
|
| 25 |
+
[LabelType.Classification]: 'classification',
|
| 26 |
+
[LabelType.AnomalyDetection]: 'anomaly',
|
| 27 |
+
[LabelType.SegmentationInstantLearning]: 'vp',
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
const ProjectCard: React.FC<ProjectCardProps> = ({ projectInfo }) => {
|
| 31 |
+
const { id, name, created_at, label_type, orgName, subscription } =
|
| 32 |
+
projectInfo;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
const { projectId: projectIdFromParam } = useParams();
|
| 35 |
|
|
|
|
| 42 |
)}
|
| 43 |
href={`/project/${id}`}
|
| 44 |
>
|
| 45 |
+
<div className="overflow-hidden w-full">
|
| 46 |
+
<div className="flex items-center justify-between w-full">
|
| 47 |
+
<p className="text-xs text-gray-500 truncate mr-2 truncate">
|
| 48 |
+
{orgName}
|
| 49 |
+
</p>
|
| 50 |
+
<p className="text-xs text-gray-500 italic">{subscription}</p>
|
| 51 |
+
</div>
|
| 52 |
+
<div className="flex mb-1 items-center">
|
| 53 |
+
<p className="text-sm font-medium text-gray mr-2 truncate">{name}</p>
|
| 54 |
+
<Chip value={LabelTypeDisplayText[label_type]} />
|
| 55 |
+
</div>
|
| 56 |
+
<div className="flex items-center truncate">
|
| 57 |
+
<p className="text-xs text-gray-500">{formattedDate}</p>
|
| 58 |
+
</div>
|
| 59 |
</div>
|
| 60 |
</Link>
|
| 61 |
);
|
components/ui/Chip.tsx
CHANGED
|
@@ -3,20 +3,18 @@ import { cn } from '@/lib/utils';
|
|
| 3 |
export interface ChipProps {
|
| 4 |
label?: string;
|
| 5 |
value: string;
|
| 6 |
-
color?: 'gray' | 'blue';
|
| 7 |
className?: string;
|
| 8 |
}
|
| 9 |
|
| 10 |
-
const Chip: React.FC<ChipProps> = ({
|
| 11 |
-
label,
|
| 12 |
-
value,
|
| 13 |
-
color = 'gray',
|
| 14 |
-
className,
|
| 15 |
-
}) => {
|
| 16 |
return (
|
| 17 |
<div
|
| 18 |
className={cn(
|
| 19 |
-
`inline-flex items-center px-
|
|
|
|
|
|
|
|
|
|
| 20 |
className,
|
| 21 |
)}
|
| 22 |
>
|
|
|
|
| 3 |
export interface ChipProps {
|
| 4 |
label?: string;
|
| 5 |
value: string;
|
| 6 |
+
color?: 'gray' | 'blue' | 'yellow' | 'purple';
|
| 7 |
className?: string;
|
| 8 |
}
|
| 9 |
|
| 10 |
+
const Chip: React.FC<ChipProps> = ({ label, value, className, color }) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
return (
|
| 12 |
<div
|
| 13 |
className={cn(
|
| 14 |
+
`inline-flex items-center px-1.5 rounded-full text-xs bg-gray-100 text-gray-500 mr-2`,
|
| 15 |
+
color === 'blue' && 'bg-blue-100 text-blue-500',
|
| 16 |
+
color === 'yellow' && 'bg-yellow-100 text-yellow-500',
|
| 17 |
+
color === 'purple' && 'bg-purple-100 text-purple-500',
|
| 18 |
className,
|
| 19 |
)}
|
| 20 |
>
|
lib/fetch/index.ts
CHANGED
|
@@ -83,10 +83,9 @@ export type ProjectBaseInfo = {
|
|
| 83 |
name: string;
|
| 84 |
created_at: Date;
|
| 85 |
label_type: string;
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
};
|
| 90 |
};
|
| 91 |
/**
|
| 92 |
* Fetch recent projects from all organizations from past 30 days, excluding
|
|
|
|
| 83 |
name: string;
|
| 84 |
created_at: Date;
|
| 85 |
label_type: string;
|
| 86 |
+
orgName: string;
|
| 87 |
+
orgId: number;
|
| 88 |
+
subscription: string;
|
|
|
|
| 89 |
};
|
| 90 |
/**
|
| 91 |
* Fetch recent projects from all organizations from past 30 days, excluding
|
lib/hooks/useVisionAgent.tsx
CHANGED
|
@@ -82,13 +82,22 @@ const useVisionAgent = (chat: ChatEntity) => {
|
|
| 82 |
...message,
|
| 83 |
content: logs + CLEANED_SEPARATOR + newContent,
|
| 84 |
};
|
| 85 |
-
/**
|
| 86 |
-
* A workaround to fix the issue of the message not being appended to the chat
|
| 87 |
-
* https://github.com/vercel/ai/issues/550#issuecomment-1712693371
|
| 88 |
-
*/
|
| 89 |
setMessages([
|
| 90 |
...messages,
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
newMessage,
|
| 93 |
]);
|
| 94 |
if (id) {
|
|
|
|
| 82 |
...message,
|
| 83 |
content: logs + CLEANED_SEPARATOR + newContent,
|
| 84 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
setMessages([
|
| 86 |
...messages,
|
| 87 |
+
/**
|
| 88 |
+
* A workaround to fix the issue of the messages been stale state when appending a new message
|
| 89 |
+
* https://github.com/vercel/ai/issues/550#issuecomment-1712693371
|
| 90 |
+
*/
|
| 91 |
+
...(input
|
| 92 |
+
? [
|
| 93 |
+
{
|
| 94 |
+
id: nanoid(),
|
| 95 |
+
role: 'user',
|
| 96 |
+
content: input,
|
| 97 |
+
createdAt: new Date(),
|
| 98 |
+
} satisfies Message,
|
| 99 |
+
]
|
| 100 |
+
: []),
|
| 101 |
newMessage,
|
| 102 |
]);
|
| 103 |
if (id) {
|