Skip to content

Commit

Permalink
Update front-end setup according to review
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorricks committed Dec 30, 2023
1 parent 0ccdf45 commit 4aab22d
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 91 deletions.
4 changes: 3 additions & 1 deletion duty_board/www/js/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ function App() {
<div style={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}>
<AnnouncementBar />
<Navbar />
<Outlet /> {/* Start rendering router matches */}
<main>
<Outlet /> {/* Start rendering router matches */}
</main>
<div style={{ marginTop: "auto" }}>
<Footer />
</div>
Expand Down
2 changes: 1 addition & 1 deletion duty_board/www/js/components/companyLogo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const CompanyLogo = ({ maxHeight, maxWidth }: Props) => {
return (
<img
src={import.meta.env.VITE_API_ADDRESS + "company_logo.png"}
alt="Logo"
alt="DutyBoard company logo"
style={{ maxHeight: maxHeight, maxWidth: maxWidth }}
/>
);
Expand Down
25 changes: 25 additions & 0 deletions duty_board/www/js/components/externalLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface LinkProps {
href: string; // Required - links with no `href` are not accessible
external?: boolean;
children: string | HTMLElement | JSX.Element;
onClick?: JSX.MouseEventHandler<HTMLAnchorElement>;
}

const ExternalLink = ({
children,
href,
external,
onClick,
}: LinkProps) => {
const target = external ? '_blank' : '_self';
// Protect older browsers - see https://mathiasbynens.github.io/rel-noopener
const rel = external && 'external noopener noreferrer';

return (
<a href={href} target={target} rel={rel} onClick={onClick}>
<span className="link__text">{children}</span>
</a>
);
};

export default ExternalLink;
5 changes: 4 additions & 1 deletion duty_board/www/js/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ export default function Footer() {
justify={{ base: "center", md: "space-between" }}
align={{ base: "center", md: "center" }}
>
<Text>© 2022 All rights reserved</Text>
<Text fontSize={"xs"}>
Copyright © 2023-2024 Jorrick Sleijster<br/>
Licensed under the Apache License, Version 2.0.
</Text>
<Stack direction={"row"} spacing={6}>
<SocialButton label={"Github"} href={"https://github.com/Jorricks/DutyBoard"}>
<FaGithub />
Expand Down
9 changes: 5 additions & 4 deletions duty_board/www/js/components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { GrUserAdmin } from "@react-icons/all-files/gr/GrUserAdmin";
import CompanyLogo from "./companyLogo";
import { useGetSchedule } from "../api";
import { Link } from "@tanstack/react-router";
import ExternalLink from "./externalLink";

const NavLink = ({ children }: { children: ReactNode }) => (
<Link to="/$category" params={{ category: children }} activeProps={{ className: "font-bold" }}>
Expand Down Expand Up @@ -64,7 +65,7 @@ export default function Navbar() {
<Flex alignItems={"center"} mr={"20px"}>
{config.gitRepositoryUrl &&
<Menu>
<Link to={config.gitRepositoryUrl} target="_blank">
<ExternalLink href={config.gitRepositoryUrl} external={true}>
<Button
leftIcon={<AiFillGithub color={config.textColor} fontSize={"30px"}/>}
colorScheme='teal'
Expand All @@ -73,20 +74,20 @@ export default function Navbar() {
>
<Text color={config.textColor}>Repo</Text>
</Button>
</Link>
</ExternalLink>
</Menu>
}
{config.enableAdminButton &&
<Menu>
<Link to={import.meta.env.VITE_API_ADDRESS + "admin/"} target="_blank">
<ExternalLink href={import.meta.env.VITE_API_ADDRESS + "admin/"} external={true}>
<Button
leftIcon={<GrUserAdmin color={config.textColor} fontSize={"25px"} />}
colorScheme='teal'
variant='outline'
>
<Text color={config.textColor}>Admin</Text>
</Button>
</Link>
</ExternalLink>
</Menu>
}
</Flex>
Expand Down
90 changes: 6 additions & 84 deletions duty_board/www/js/components/personComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,16 @@
import {
Box,
Button,
Text,
Popover,
PopoverContent,
PopoverHeader,
PopoverCloseButton,
PopoverBody,
PopoverFooter,
Portal,
PopoverTrigger
} from "@chakra-ui/react";
import * as React from "react";
import { ExtraInfoOnPerson, PersonEssentials, PersonResponse } from "../api/api-generated-types";
import { PersonEssentials } from "../api/api-generated-types";
import { FaInfoCircle } from "react-icons/fa";
import DynamicFAIcon from "./dynamicFAIcon";
import useGetPerson from "../api/useGetPerson";
import PersonLazyLoading from './personLazyLoading';

export interface DrawInfoOnPerson {
information: string;
icon: string;
iconColor: string;
}


const ExtraInfoComponent = ({ information, icon, iconColor }: DrawInfoOnPerson) => {
return (
<Box p={2} style={{ display: "flex", alignItems: "center" }}>
<DynamicFAIcon icon={icon} color={iconColor} style={{ display: "inline-block" }} />
<Text style={{ display: "inline-block", marginLeft: "10px" }}>{information}</Text>
</Box>
);
};

const URLExtraInfoComponent = ({ information, icon, iconColor, url }: ExtraInfoOnPerson) => {
return (
<>
{url ?
<a href={url} target="_blank">
<ExtraInfoComponent information={information} icon={icon} iconColor={iconColor}/>
</a>
: <ExtraInfoComponent information={information} icon={icon} iconColor={iconColor}/>
}
</>
);
};

const PersonImage = ({person}: {person: PersonResponse}) => {
{/* Inspiration from https://www.webfx.com/blog/web-design/circular-images-css/. Thanks William Craig! */}
return (
<div>
{person.imgHeight != null && person.imgWidth != null && person.imgWidth > person.imgHeight
? <div className="circular--landscape">
<img src={import.meta.env.VITE_API_ADDRESS + "person_img/" + person.imgFilename} alt={"Profile picture"}/>
</div>
: <div className="circular--portrait">
<img src={import.meta.env.VITE_API_ADDRESS + "person_img/" + person.imgFilename} alt={"Profile picture"}/>
</div>
}
</div>
)
}

const LazyLoadingPopoverContent = ({ personUid }: { personUid: number }) => {
const { data: apiPerson } = useGetPerson({ personUid });

return (
<>
<PopoverHeader mt="5px" pl="20px" pb="10px" style={{fontWeight: "bold", fontSize: "20px"}}>
{apiPerson ? apiPerson.username ?? apiPerson.email : "unknown"}
</PopoverHeader>
<PopoverCloseButton pt={"22px"} pr={"22px"} fontSize={"12px"}/>
<PopoverBody>
{apiPerson?.imgFilename &&
<Box mb={"10px"} mt={"10px"}>
<PersonImage person={apiPerson}/>
</Box>
}
{apiPerson?.extraAttributes.map((extraAttribute: ExtraInfoOnPerson, index) => (
<URLExtraInfoComponent {...extraAttribute} key={index} />
))}
</PopoverBody>
<PopoverFooter>
Last updated: {apiPerson?.lastUpdate}
<br />
Sync enabled: {apiPerson?.sync ? "True" : "False"}
</PopoverFooter>
</>
);
};

const capitalizeFirst = str => {
return str.charAt(0).toUpperCase() + str.slice(1);
Expand All @@ -98,8 +20,8 @@ const PersonComponent = ({ person }: { person: PersonEssentials }) => {
const initRef = React.useRef();

return (
<>
{person == undefined ? (
<article>
{person === undefined ? (
"Unknown error.."
) : (
<Popover placement="left" initialFocusRef={initRef} isLazy lazyBehavior='keepMounted'>
Expand All @@ -117,14 +39,14 @@ const PersonComponent = ({ person }: { person: PersonEssentials }) => {
</PopoverTrigger>
<Portal>
<PopoverContent backgroundColor={"#F5F5F5"}>
<LazyLoadingPopoverContent personUid={person.uid} />
<PersonLazyLoading personUid={person.uid} />
</PopoverContent>
</Portal>
</>
)}
</Popover>
)}
</>
</article>
);
};

Expand Down
80 changes: 80 additions & 0 deletions duty_board/www/js/components/personLazyLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {Box, PopoverBody, PopoverCloseButton, PopoverFooter, PopoverHeader, Text} from "@chakra-ui/react";
import DynamicFAIcon from "./dynamicFAIcon";
import {ExtraInfoOnPerson, PersonResponse} from "../api/api-generated-types";
import useGetPerson from "../api/useGetPerson";
import * as React from "react";

export interface DrawInfoOnPerson {
information: string;
icon: string;
iconColor: string;
}


const ExtraInfoComponent = ({ information, icon, iconColor }: DrawInfoOnPerson) => {
return (
<Box p={2} style={{ display: "flex", alignItems: "center" }}>
<DynamicFAIcon icon={icon} color={iconColor} style={{ display: "inline-block" }} />
<Text style={{ display: "inline-block", marginLeft: "10px" }}>{information}</Text>
</Box>
);
};

const URLExtraInfoComponent = ({ information, icon, iconColor, url }: ExtraInfoOnPerson) => {
return (
<>
{url ?
<a href={url} target="_blank">
<ExtraInfoComponent information={information} icon={icon} iconColor={iconColor}/>
</a>
: <ExtraInfoComponent information={information} icon={icon} iconColor={iconColor}/>
}
</>
);
};

const PersonImage = ({person}: {person: PersonResponse}) => {
{/* Inspiration from https://www.webfx.com/blog/web-design/circular-images-css/. Thanks William Craig! */}
return (
<div>
{person.imgHeight != null && person.imgWidth != null && person.imgWidth > person.imgHeight
? <div className="circular--landscape">
<img src={import.meta.env.VITE_API_ADDRESS + "person_img/" + person.imgFilename} alt={person.username}/>
</div>
: <div className="circular--portrait">
<img src={import.meta.env.VITE_API_ADDRESS + "person_img/" + person.imgFilename} alt={person.username}/>
</div>
}
</div>
)
}

const PersonLazyLoading = ({ personUid }: { personUid: number }) => {
const { data: apiPerson } = useGetPerson({ personUid });

return (
<>
<PopoverHeader mt="5px" pl="20px" pb="10px" style={{fontWeight: "bold", fontSize: "20px"}}>
{apiPerson?.username || apiPerson?.email || "unknown"}
</PopoverHeader>
<PopoverCloseButton pt={"22px"} pr={"22px"} fontSize={"12px"}/>
<PopoverBody>
{apiPerson?.imgFilename &&
<Box mb={"10px"} mt={"10px"}>
<PersonImage person={apiPerson}/>
</Box>
}
{apiPerson?.extraAttributes.map((extraAttribute: ExtraInfoOnPerson, index) => (
<URLExtraInfoComponent {...extraAttribute} key={index} />
))}
</PopoverBody>
<PopoverFooter>
Last updated: {apiPerson?.lastUpdate}
<br />
Sync enabled: {apiPerson?.sync ? "True" : "False"}
</PopoverFooter>
</>
);
};

export default PersonLazyLoading;

0 comments on commit 4aab22d

Please sign in to comment.