Skip to content

Commit

Permalink
Merge pull request #114 from basemind-ai/112
Browse files Browse the repository at this point in the history
feat: add applications list API, update nav rail UI
  • Loading branch information
Goldziher authored Oct 19, 2023
2 parents bf69221 + 149960e commit c666d90
Show file tree
Hide file tree
Showing 27 changed files with 567 additions and 115 deletions.
3 changes: 2 additions & 1 deletion frontend/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"abTesting": "A/B Testing",
"bannerTitle": "Need Help?",
"bannerBody": "Our customer success team is \nhere for you",
"bannerCTA": "Get Support"
"bannerCTA": "Get Support",
"application": "Application"
},
"signin": {
"authHeader": "Welcome to BaseMind",
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/api/applications-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ export async function handleCreateApplication({
});
}

export async function handleRetrieveApplications(
projectId: string,
): Promise<Application[]> {
return await fetcher<Application[]>({
url: `projects/${projectId}/applications`,
method: HttpMethod.Get,
});
}

export async function handleRetrieveApplication({
applicationId,
projectId,
Expand Down
14 changes: 0 additions & 14 deletions frontend/src/app/application/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client';

import { useRouter } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { useEffect } from 'react';

import { Navigation } from '@/constants';
import { useAuthenticatedUser } from '@/hooks/use-authenticated-user';
import { useGetApplication } from '@/stores/project-store';

export default function Application({
params: { projectId, applicationId },
}: {
params: { projectId: string; applicationId: string };
}) {
useAuthenticatedUser();
const t = useTranslations('navrail');
const router = useRouter();
const application = useGetApplication()(projectId, applicationId);

useEffect(() => {
if (!application) {
router.replace(Navigation.Projects);
}
}, []);

if (!application) {
return null;
}

return (
<div data-testid="application-page" className="my-8 mx-32">
<h1 className="text-2xl font-semibold text-base-content">
{t('application')} / {application.name}
</h1>
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/src/app/projects/[projectId]/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

import { Navigation } from '@/constants';
import { useProject } from '@/stores/api-store';
import { useProject } from '@/stores/project-store';

export default function Dashboard({
params: { projectId },
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/app/projects/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useState } from 'react';
import { handleCreateProject } from '@/api';
import { Logo } from '@/components/logo';
import { Navigation } from '@/constants';
import { useAddProject, useProjects } from '@/stores/api-store';
import { useAddProject, useProjects } from '@/stores/project-store';
import { handleChange } from '@/utils/helpers';

export default function CreateProjectPage() {
Expand Down
20 changes: 15 additions & 5 deletions frontend/src/app/projects/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';

import { handleRetrieveProjects } from '@/api';
import { handleRetrieveApplications, handleRetrieveProjects } from '@/api';
import { Navigation } from '@/constants';
import { useAuthenticatedUser } from '@/hooks/use-authenticated-user';
import { useSetProjects } from '@/stores/api-store';
import {
useSetCurrentProject,
useSetProjectApplications,
useSetProjects,
} from '@/stores/project-store';

export default function Projects() {
const router = useRouter();
const setProjects = useSetProjects();
const setCurrentProject = useSetCurrentProject();
const setProjectApplications = useSetProjectApplications();
useAuthenticatedUser();

useEffect(() => {
Expand All @@ -20,10 +26,14 @@ export default function Projects() {
router.replace(Navigation.CreateProject);
return;
}
const [{ id: projectId }] = retrievedProjects;
setProjects(retrievedProjects);
router.replace(
`${Navigation.Projects}/${retrievedProjects[0]?.id}/dashboard`,
);
setCurrentProject(projectId);
router.replace(`${Navigation.Projects}/${projectId}/dashboard`);

const projectApplications =
await handleRetrieveApplications(projectId);
setProjectApplications(projectId, projectApplications);
})();
}, []);

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/app-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function AppLayout({ children }: { children: React.ReactNode }) {
className="bg-base-100 h-full w-full flex"
data-testid="app-layout-container"
>
<div className="w-2/12">
<div className="w-2/12 min-w-min">
<NavRail />
</div>
<div className="w-10/12">{children}</div>
Expand Down
52 changes: 32 additions & 20 deletions frontend/src/components/link-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface LinkMenuProps {
isCurrent?: boolean;
isDisabled?: boolean;
href?: string;
children?: React.ReactNode;
}

export default function LinkMenu({
Expand All @@ -17,30 +18,41 @@ export default function LinkMenu({
isDisabled,
isCurrent,
href,
children,
}: LinkMenuProps) {
return isDisabled ? (
<div className="flex items-center pb-4 opacity-60">
<div className="flex items-center text-base-content">
{icon && <div className="mr-2">{icon}</div>}
{text && <span className="text-sm font-medium">{text}</span>}
</div>
{badge && <span className="ml-2">{badge}</span>}
</div>
) : (
<Link href={href ?? '#'}>
<div className="flex items-center pb-4">
const Wrapper = ({ children }: { children: React.ReactNode }) =>
isDisabled ? (
<>{children}</>
) : (
<Link href={href ?? '#'}>{children}</Link>
);

return (
<>
<Wrapper>
<div
className={`flex items-center text-base-content transition ${
isCurrent ? 'text-primary' : 'hover:text-primary'
className={`flex items-center py-2 px-3.5 ${
isDisabled && 'opacity-60'
}`}
>
{icon && <div className="mr-2">{icon}</div>}
{text && (
<span className="text-sm font-medium">{text}</span>
)}
<div
className={`flex items-center text-base-content transition ${
isCurrent ? 'text-primary' : 'hover:text-primary'
}`}
>
{icon && <div className="mr-2">{icon}</div>}
{text && (
<span className="text-sm font-medium">{text}</span>
)}
</div>
{badge && <span className="ml-2">{badge}</span>}
</div>
</Wrapper>
{children && (
<div className="flex flex-col gap-2.5 border-l-2 border-neutral ml-5 pl-2.5">
{children}
</div>
{badge && <span className="ml-2">{badge}</span>}
</div>
</Link>
)}
</>
);
}
57 changes: 43 additions & 14 deletions frontend/src/components/nav-rail/nav-rail-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,64 @@ import {

import Badge from '@/components/badge';
import LinkMenu from '@/components/link-menu';
import { Navigation } from '@/constants';
import { useCurrentProject } from '@/stores/project-store';
import { contextNavigation, populateApplicationId } from '@/utils/navigation';

const ICON_CLASSES = 'w-3.5 h-3.5';

export default function NavRailList() {
const t = useTranslations('navrail');
const [pathname] = usePathname().split('?');
const currentProject = useCurrentProject()();
const navigation = contextNavigation(currentProject?.id);

return (
<div className="mt-12 ml-2 " data-testid="nav-rail-list">
<div
className="mt-12 ml-2 gap-0.5 flex flex-col "
data-testid="nav-rail-list"
>
<LinkMenu
href={Navigation.Dashboard}
href={navigation.Dashboard}
text={t('overview')}
icon={<HouseDoor className="w-3 h-3" />}
isCurrent={Navigation.Dashboard === pathname}
icon={<HouseDoor className={ICON_CLASSES} />}
isCurrent={navigation.Dashboard === pathname}
/>
<LinkMenu
href={Navigation.Prompt}
href={navigation.Prompt}
text={t('testing')}
icon={<Search className="w-3 h-3" />}
isCurrent={Navigation.Prompt === pathname}
icon={<Search className={ICON_CLASSES} />}
isCurrent={navigation.Prompt === pathname}
/>
{currentProject?.applications?.length && (
<LinkMenu
isDisabled={true}
text={t('application')}
icon={<Boxes className={ICON_CLASSES} />}
>
{currentProject.applications.map((application) => {
const applicationUrl = populateApplicationId(
navigation.Application,
application.id,
);
return (
<LinkMenu
href={applicationUrl}
text={application.name}
isCurrent={applicationUrl === pathname}
/>
);
})}
</LinkMenu>
)}
<LinkMenu
href={Navigation.Api}
href={navigation.Api}
text={t('api')}
icon={<Boxes className="w-3 h-3" />}
isCurrent={Navigation.Api === pathname}
icon={<Boxes className={ICON_CLASSES} />}
isCurrent={navigation.Api === pathname}
/>
<LinkMenu
text={t('persistence')}
icon={<HddStack className="w-3 h-3" />}
icon={<HddStack className={ICON_CLASSES} />}
isDisabled={true}
badge={
<Badge
Expand All @@ -52,7 +81,7 @@ export default function NavRailList() {
/>
<LinkMenu
text={t('middleware')}
icon={<Lightning className="w-3 h-3" />}
icon={<Lightning className={ICON_CLASSES} />}
isDisabled={true}
badge={
<Badge
Expand All @@ -64,7 +93,7 @@ export default function NavRailList() {
/>
<LinkMenu
text={t('abTesting')}
icon={<Speedometer2 className="w-3 h-3" />}
icon={<Speedometer2 className={ICON_CLASSES} />}
isDisabled={true}
badge={
<Badge
Expand Down
1 change: 1 addition & 0 deletions frontend/src/constants/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ export enum Navigation {
Settings = '/projects/:projectId/settings',
Billing = '/projects/:projectId/billing',
Support = '/support',
Application = '/projects/:projectId/application/:applicationId',
}
17 changes: 0 additions & 17 deletions frontend/src/stores/api-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,18 @@ import { UserInfo } from '@firebase/auth';
import { create } from 'zustand';
import { StateCreator } from 'zustand/vanilla';

import { Project } from '@/types';

export interface ApiStore {
user: UserInfo | null;
projects: Project[];
setUser: (user: UserInfo) => void;
setProjects: (projects: Project[]) => void;
addProject: (project: Project) => void;
}

export const apiStoreStateCreator: StateCreator<ApiStore> = (set, _) => ({
user: null,
projects: [],
setUser: (user: UserInfo) => {
set({ user });
},
setProjects: (projects: Project[]) => {
set({ projects });
},
addProject: (project: Project) => {
set((state) => ({ projects: [...state.projects, project] }));
},
});

export const useAPIStore = create(apiStoreStateCreator);
export const useUser = () => useAPIStore((s) => s.user);
export const useSetUser = () => useAPIStore((s) => s.setUser);
export const useSetProjects = () => useAPIStore((s) => s.setProjects);
export const useAddProject = () => useAPIStore((s) => s.addProject);
export const useProject = (projectId: string) =>
useAPIStore((s) => s.projects.find((project) => project.id === projectId));
export const useProjects = () => useAPIStore((s) => s.projects);
Loading

0 comments on commit c666d90

Please sign in to comment.