Skip to content

Commit

Permalink
Add projects fetch and set selected project in sidebar on load
Browse files Browse the repository at this point in the history
  • Loading branch information
tomtitherington committed Oct 21, 2023
1 parent 6988333 commit b713f6a
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 24 deletions.
45 changes: 25 additions & 20 deletions frontend/src/components/sidebarMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import CustomButton from 'components/button';
import SidebarButtonPrimary from './sidebarButtonPrimary';
import SidebarButtonSecondary from './sidebarButtonSecondary';
import {
MdSpoke,
MdChevronRight,
MdSupport,
MdOutlineOpenInNew,
Expand All @@ -15,8 +14,9 @@ import {
import { ReactComponent as Flow } from 'assets/icons/Flow Icon.svg';
import { ReactComponent as CrumpetLogo } from 'assets/images/Crumpet Logo Oxford.svg';
import { Fragment, useState } from 'react';
import TextInput from 'components/textInput';
import { CreateProjectModal } from 'features/projects/components/CreateProjectModal';
import { useProjectsStore } from 'features/projects/stores/useProjectsStore';
import { getFirstLetter } from 'utils';

const environments = [
{ id: 1, name: 'Development' },
Expand All @@ -35,9 +35,9 @@ interface SidebarMenuProps {
}

const SidebarMenu = ({ projects }: SidebarMenuProps) => {
//TODO: Use custom modal and encapsulate logic into its own hook
const [projectName, setProjectName] = useState('');
const [isOpen, setIsOpen] = useState(false);
const { selectedProject } = useProjectsStore();
const isLoadingState = ['initial', 'loading', 'hasError'].includes(selectedProject.state);

function closeModal() {
setIsOpen(false);
Expand All @@ -64,27 +64,20 @@ const SidebarMenu = ({ projects }: SidebarMenuProps) => {
console.log('Clicked Button');
},
},
{
label: 'Segments',
icon: () => <MdSpoke />,
onClick: () => {
console.log('Clicked Button');
},
},
];

return (
<>
<div
className="w-64 h-screen pt-4 bg-crumpet-light-100 border-r border-crumpet-light-300 flex-col
className="w-72 h-screen pt-4 bg-crumpet-light-100 border-r border-crumpet-light-300 flex-col
justify-between items-center gap-8 flex">
<div className="self-stretch px-4 py-3 flex-col justify-start gap-8 flex">
<div className="flex items-center gap-2">
<CrumpetLogo style={{ height: '24px', width: '24px' }} />
<div className="text-oxford font-heebo font-black text-2xl">Crumpet</div>
</div>
<Picker widthFill={true} items={environments} initialSelection={environments[0]} />
<div className="flex-col justify-start items-start gap-4 flex w-full">
<div className="flex-col justify-start items-start gap-0.5 flex w-full">
{ButtonList.map((button, index) => (
<SidebarButtonPrimary
key={index}
Expand All @@ -98,7 +91,7 @@ const SidebarMenu = ({ projects }: SidebarMenuProps) => {
</div>
</div>
<div className="self-stretch flex-col justify-end items-center gap-4 flex">
<div className="self-stretch flex flex-col gap-4 px-4 ">
<div className="self-stretch flex flex-col gap-0.5 px-4 ">
<SidebarButtonSecondary
icon={<MdSupport />}
label="Support"
Expand All @@ -116,15 +109,27 @@ const SidebarMenu = ({ projects }: SidebarMenuProps) => {
<Popover className="relative w-full">
{({ open }) => (
<>
<Popover.Button
<Popover.Button disabled={isLoadingState}
className="w-full flex justify-between items-center p-4 border-t
border-crumpet-light-300">
<div className="flex items-center space-x-4">
<div className="w-8 h-8 rounded-full bg-blue-400 text-white flex items-center justify-center">
C
{isLoadingState ? (
// Render pulsing grey rectangle for initial, loading, and hasError states
<div className="flex items-center space-x-4 animate-pulse">
<div className="w-8 h-8 rounded bg-crumpet-light-300"></div>
<div className="w-32 h-6 rounded bg-crumpet-light-300"></div>
</div>
) : (
<div className="flex items-center space-x-4">
<div className="w-8 h-8 rounded bg-blue-400 text-white flex items-center justify-center">
{getFirstLetter(
selectedProject.state === 'hasData' ? selectedProject.data.name : null,
)}
</div>
<span className="text-base font-semibold">
{selectedProject.state === 'hasData' ? selectedProject.data.name : ''}
</span>
</div>
<span className="text-base font-semibold">Crumpet</span>
</div>
)}
<MdChevronRight className="fill-gray-500" />
</Popover.Button>
<Transition
Expand Down
13 changes: 13 additions & 0 deletions frontend/src/features/projects/stores/useProjectsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type ProjectsStore = {
projects: ApiState<Project[]>;
fetchProjects: (config: Configuration) => void;
setSelectedProject: (projectId: number) => void;
fetchAndSelectProject: (config: Configuration) => void;
createProject: (name: string, config: Configuration) => void;
};

Expand All @@ -29,6 +30,18 @@ export const useProjectsStore = create<ProjectsStore>((set, get) => ({
}
}
},
fetchAndSelectProject: (config: Configuration) => {
set(state => ({ projects: ApiState.loading() }));
new ProjectsApi(config)
.listProjects()
.then(res =>
set(state => ({
projects: ApiState.hasData(res.data),
selectedProject: ApiState.hasData(res.data[0]),
})),
)
.catch(err => set(state => ({ projects: ApiState.hasError(err) })));
},
createProject: async (name: string, config: Configuration) => {
const res = await new ProjectsApi(config).createProject({ name: name });
set(state => ({ selectedProject: ApiState.hasData(res.data) }));
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/pages/root/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { Outlet, useNavigate } from 'react-router-dom';

const Root = () => {
const { config } = useApiConfig();
const { projects, fetchProjects, setSelectedProject } = useProjectsStore();
const { projects, fetchAndSelectProject, setSelectedProject } = useProjectsStore();
const navigate = useNavigate();

useEffect(() => {
fetchProjects(config);
}, [fetchProjects, config]);
fetchAndSelectProject(config);
}, [fetchAndSelectProject, config]);

return (
<div className="flex h-screen v-screen">
Expand All @@ -38,7 +38,7 @@ const Root = () => {
<Outlet />
</>
);
//TODO: Handle these cases (probs toast and navigate away?)
//TODO: Handle these cases (probs toast and navigate away?)
case 'hasError':
return <div>Error encountered</div>; // Render the error
case 'hasDataWithError':
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Utility function to get the first letter of a string.
*
* @param str - The input string.
* @returns The first letter of the string, or '' if the string is null or empty.
*/
export const getFirstLetter = (str: string | null | undefined): string => {
return str && str.length > 0 ? str.charAt(0) : '';
}

0 comments on commit b713f6a

Please sign in to comment.