generic avatar component
This commit is contained in:
parent
1501e6dea4
commit
020cd4c5ac
@ -17,11 +17,12 @@ const OrganizationLayout = ({
|
|||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}>): ReactElement => {
|
}>): ReactElement => {
|
||||||
const selectedOrganization: Organization | undefined =
|
const organization: Organization | undefined = useOrganizationContext(
|
||||||
useOrganizationContext((state: OrganizationState) => state.selected);
|
(state: OrganizationState) => state.selected
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
{selectedOrganization ? children : <NoOrganizationSelected />}
|
{organization ? children : <NoOrganizationSelected />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import DashboardHeader from "@/components/dashboard/dashboard-header";
|
import DashboardHeader from "@/components/dashboard/dashboard-header";
|
||||||
|
import StatusPageList from "@/components/dashboard/org/status-page/status-page-list";
|
||||||
|
|
||||||
const StatusPagesPage = (): ReactElement => (
|
const StatusPagesPage = (): ReactElement => (
|
||||||
<main className="flex flex-col gap-3">
|
<main className="h-full flex flex-col gap-10">
|
||||||
<DashboardHeader title="Status Pages" />
|
<DashboardHeader title="Status Pages" />
|
||||||
|
<StatusPageList />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
export default StatusPagesPage;
|
export default StatusPagesPage;
|
||||||
|
@ -25,7 +25,7 @@ const NotFoundPage = (): ReactElement => (
|
|||||||
<div className="flex flex-col justify-center">
|
<div className="flex flex-col justify-center">
|
||||||
<div className="flex flex-col gap-0.5 pointer-events-none">
|
<div className="flex flex-col gap-0.5 pointer-events-none">
|
||||||
<h1 className="text-3xl font-bold">Wrong Door!</h1>
|
<h1 className="text-3xl font-bold">Wrong Door!</h1>
|
||||||
<p className="max-w-72 text-lg opacity-75">
|
<p className="max-w-64 text-lg opacity-75">
|
||||||
The page you were looking for could not be found.
|
The page you were looking for could not be found.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ReactElement } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import SimpleTooltip from "@/components/simple-tooltip";
|
||||||
|
import { Organization } from "@/app/types/org/organization";
|
||||||
|
import { useOrganizationContext } from "@/app/provider/organization-provider";
|
||||||
|
import { OrganizationState } from "@/app/store/organization-store";
|
||||||
|
import { StatusPage as StatusPageType } from "@/app/types/page/status-page";
|
||||||
|
import StatusPage from "@/components/dashboard/org/status-page/status-page";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of status pages for the
|
||||||
|
* currently selected {@link Organization}.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const StatusPageList = (): ReactElement => {
|
||||||
|
const organization: Organization | undefined = useOrganizationContext(
|
||||||
|
(state: OrganizationState) => state.selected
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-5">
|
||||||
|
{/* Create */}
|
||||||
|
<SimpleTooltip content="Create a new status page">
|
||||||
|
<Button className="w-24" variant="outline" size="sm" disabled>
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</SimpleTooltip>
|
||||||
|
|
||||||
|
{/* Status Pages */}
|
||||||
|
{organization?.statusPages.map(
|
||||||
|
(statusPage: StatusPageType, index: number) => (
|
||||||
|
<StatusPage key={index} statusPage={statusPage} />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default StatusPageList;
|
17
src/components/dashboard/org/status-page/status-page.tsx
Normal file
17
src/components/dashboard/org/status-page/status-page.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { ReactElement } from "react";
|
||||||
|
import { StatusPage as StatusPageType } from "@/app/types/page/status-page";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status page for an organization.
|
||||||
|
*
|
||||||
|
* @param statusPage
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
const StatusPage = ({
|
||||||
|
statusPage,
|
||||||
|
}: {
|
||||||
|
statusPage: StatusPageType;
|
||||||
|
}): ReactElement => (
|
||||||
|
<div className="p-3.5 w-72 border rounded-lg">sdfdssdfsfs</div>
|
||||||
|
);
|
||||||
|
export default StatusPage;
|
84
src/components/generic-avatar.tsx
Normal file
84
src/components/generic-avatar.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { ReactElement } from "react";
|
||||||
|
import { cva } from "class-variance-authority";
|
||||||
|
import Image from "next/image";
|
||||||
|
import InitialsAvatar from "react-initials-avatar";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The variants of the avatar.
|
||||||
|
*/
|
||||||
|
const avatarVariants = cva("relative rounded-full", {
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
sm: "w-6 h-6",
|
||||||
|
default: "w-11 h-11",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The props for the avatar.
|
||||||
|
*/
|
||||||
|
export type GenericAvatarProps = {
|
||||||
|
/**
|
||||||
|
* The image URL of the
|
||||||
|
* avatar to display, if any.
|
||||||
|
*/
|
||||||
|
image?: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The alt text for the image.
|
||||||
|
*/
|
||||||
|
imageAlt?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The backup text to extract initials
|
||||||
|
* from if the avatar image is unavailable.
|
||||||
|
*/
|
||||||
|
initialsText: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The size of the avatar.
|
||||||
|
*/
|
||||||
|
size?: "sm" | "default";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The optional class name to apply to the logo.
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An avatar for a user.
|
||||||
|
*
|
||||||
|
* @param props the avatar props
|
||||||
|
* @return the avatar jsx
|
||||||
|
*/
|
||||||
|
const GenericAvatar = ({
|
||||||
|
image,
|
||||||
|
imageAlt,
|
||||||
|
initialsText,
|
||||||
|
size,
|
||||||
|
className,
|
||||||
|
}: GenericAvatarProps): ReactElement => (
|
||||||
|
<div className={cn(avatarVariants({ size, className }))}>
|
||||||
|
{image ? (
|
||||||
|
<Image
|
||||||
|
className="rounded-full"
|
||||||
|
src={image}
|
||||||
|
alt={imageAlt ?? ""}
|
||||||
|
fill
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<InitialsAvatar
|
||||||
|
className="w-full h-full flex justify-center items-center bg-muted rounded-full"
|
||||||
|
name={initialsText}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
export default GenericAvatar;
|
@ -1,25 +1,7 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { cva } from "class-variance-authority";
|
|
||||||
import Image from "next/image";
|
|
||||||
import InitialsAvatar from "react-initials-avatar";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { Organization } from "@/app/types/org/organization";
|
import { Organization } from "@/app/types/org/organization";
|
||||||
|
import GenericAvatar from "@/components/generic-avatar";
|
||||||
/**
|
|
||||||
* The variants of the logo.
|
|
||||||
*/
|
|
||||||
const logoVariants = cva("relative rounded-full", {
|
|
||||||
variants: {
|
|
||||||
size: {
|
|
||||||
sm: "w-5 h-5",
|
|
||||||
default: "w-10 h-10",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
size: "default",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The props for this component.
|
* The props for this component.
|
||||||
@ -29,45 +11,30 @@ type OrganizationLogoProps = {
|
|||||||
* The organization to show the logo for.
|
* The organization to show the logo for.
|
||||||
*/
|
*/
|
||||||
organization: Organization;
|
organization: Organization;
|
||||||
|
} & Omit<
|
||||||
/**
|
React.ComponentProps<typeof GenericAvatar>,
|
||||||
* The size of the logo.
|
"image" | "imageAlt" | "initialsText"
|
||||||
*/
|
>;
|
||||||
size?: "sm" | "default";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The optional class name to apply to the logo.
|
|
||||||
*/
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logo for an organization.
|
* The logo for an organization.
|
||||||
*
|
*
|
||||||
* @param organization the organization
|
* @param organization the org to show the logo for
|
||||||
* @param size the size
|
* @param props additional props
|
||||||
* @param className additional class names
|
* @return the logo jsx
|
||||||
* @return the organization jsx
|
|
||||||
*/
|
*/
|
||||||
const OrganizationLogo = ({
|
const OrganizationLogo = ({
|
||||||
organization,
|
organization,
|
||||||
size,
|
...props
|
||||||
className,
|
|
||||||
}: OrganizationLogoProps): ReactElement => (
|
}: OrganizationLogoProps): ReactElement => (
|
||||||
<div className={cn(logoVariants({ size, className }))}>
|
<GenericAvatar
|
||||||
{organization.logo ? (
|
image={
|
||||||
<Image
|
organization.logo &&
|
||||||
className="rounded-full"
|
`${process.env.NEXT_PUBLIC_CDN_ENDPOINT}/organizations/${organization.logo}.webp`
|
||||||
src={`${process.env.NEXT_PUBLIC_CDN_ENDPOINT}/organizations/${organization.logo}.webp`}
|
}
|
||||||
alt={`${organization.name}'s Logo`}
|
imageAlt={`${organization.name}'s Logo`}
|
||||||
fill
|
initialsText={organization.name}
|
||||||
/>
|
{...props}
|
||||||
) : (
|
/>
|
||||||
<InitialsAvatar
|
|
||||||
className="-translate-y-0.5 w-[130%] h-[130%] flex justify-center items-center bg-muted rounded-full"
|
|
||||||
name={organization.name}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
export default OrganizationLogo;
|
export default OrganizationLogo;
|
||||||
|
@ -1,73 +1,37 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { cva } from "class-variance-authority";
|
|
||||||
import Image from "next/image";
|
|
||||||
import InitialsAvatar from "react-initials-avatar";
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
import { User } from "@/app/types/user/user";
|
import { User } from "@/app/types/user/user";
|
||||||
|
import GenericAvatar from "@/components/generic-avatar";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The variants of the avatar.
|
* The props for the avatar.
|
||||||
*/
|
|
||||||
const avatarVariants = cva("relative rounded-full", {
|
|
||||||
variants: {
|
|
||||||
size: {
|
|
||||||
sm: "w-6 h-6",
|
|
||||||
default: "w-11 h-11",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
size: "default",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The props for this component.
|
|
||||||
*/
|
*/
|
||||||
type UserAvatarProps = {
|
type UserAvatarProps = {
|
||||||
/**
|
/**
|
||||||
* The user to show the avatar for.
|
* The user to show the avatar for.
|
||||||
*/
|
*/
|
||||||
user: User;
|
user: User;
|
||||||
|
} & Omit<
|
||||||
/**
|
React.ComponentProps<typeof GenericAvatar>,
|
||||||
* The size of the avatar.
|
"image" | "imageAlt" | "initialsText"
|
||||||
*/
|
>;
|
||||||
size?: "sm" | "default";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The optional class name to apply to the logo.
|
|
||||||
*/
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An avatar for a user.
|
* The avatar for a user.
|
||||||
*
|
*
|
||||||
* @param user the user
|
* @param user the user to show the avatar for
|
||||||
* @param size the size
|
* @param props additional props
|
||||||
* @param className additional class names
|
|
||||||
* @return the avatar jsx
|
* @return the avatar jsx
|
||||||
*/
|
*/
|
||||||
const UserAvatar = ({
|
const UserAvatar = ({ user, ...props }: UserAvatarProps): ReactElement => (
|
||||||
user,
|
<GenericAvatar
|
||||||
size,
|
image={
|
||||||
className,
|
user.avatar &&
|
||||||
}: UserAvatarProps): ReactElement => (
|
`${process.env.NEXT_PUBLIC_CDN_ENDPOINT}/avatars/${user.avatar}.webp`
|
||||||
<div className={cn(avatarVariants({ size, className }))}>
|
}
|
||||||
{user.avatar ? (
|
imageAlt={`${user.username}'s Avatar`}
|
||||||
<Image
|
initialsText={user.username}
|
||||||
className="rounded-full"
|
{...props}
|
||||||
src={`${process.env.NEXT_PUBLIC_CDN_ENDPOINT}/avatars/${user.avatar}.webp`}
|
/>
|
||||||
alt={`${user.username}'s Avatar`}
|
|
||||||
fill
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<InitialsAvatar
|
|
||||||
className="w-full h-full flex justify-center items-center bg-muted rounded-full"
|
|
||||||
name={user.username}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
export default UserAvatar;
|
export default UserAvatar;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user