feat: extend skeleton loaders
This commit is contained in:
@@ -15,6 +15,7 @@ import WorkspaceMenu from "~/components/WorkspaceMenu";
|
||||
|
||||
interface SideNavigationProps {
|
||||
user: UserType;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
interface UserType {
|
||||
@@ -22,7 +23,10 @@ interface UserType {
|
||||
image?: string | null | undefined;
|
||||
}
|
||||
|
||||
export default function SideNavigation({ user }: SideNavigationProps) {
|
||||
export default function SideNavigation({
|
||||
user,
|
||||
isLoading,
|
||||
}: SideNavigationProps) {
|
||||
const pathname = usePathname();
|
||||
const { theme } = useTheme();
|
||||
|
||||
@@ -65,6 +69,7 @@ export default function SideNavigation({ user }: SideNavigationProps) {
|
||||
<UserMenu
|
||||
email={user?.email ?? ""}
|
||||
imageUrl={user?.image ?? undefined}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</nav>
|
||||
</>
|
||||
|
||||
@@ -7,13 +7,18 @@ import createClient from "~/utils/supabase/client";
|
||||
interface UserMenuProps {
|
||||
imageUrl: string | undefined;
|
||||
email: string;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
function classNames(...classes: string[]): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export default function UserMenu({ imageUrl, email }: UserMenuProps) {
|
||||
export default function UserMenu({
|
||||
imageUrl,
|
||||
email,
|
||||
isLoading,
|
||||
}: UserMenuProps) {
|
||||
const router = useRouter();
|
||||
const { theme, switchTheme } = useTheme();
|
||||
|
||||
@@ -28,6 +33,12 @@ export default function UserMenu({ imageUrl, email }: UserMenuProps) {
|
||||
return (
|
||||
<Menu as="div" className="relative inline-block w-full text-left">
|
||||
<div>
|
||||
{isLoading ? (
|
||||
<div className="flex">
|
||||
<div className="h-[30px] w-[30px] animate-pulse rounded-full bg-light-200 dark:bg-dark-200" />
|
||||
<div className="mx-2 h-[30px] w-[175px] animate-pulse rounded-md bg-light-200 dark:bg-dark-200" />
|
||||
</div>
|
||||
) : (
|
||||
<Menu.Button className="flex w-full items-center rounded-md p-1.5 text-neutral-900 hover:bg-light-200 dark:text-dark-900 dark:hover:bg-dark-200 dark:hover:text-dark-1000">
|
||||
{imageUrl ? (
|
||||
<Image
|
||||
@@ -50,6 +61,7 @@ export default function UserMenu({ imageUrl, email }: UserMenuProps) {
|
||||
)}
|
||||
<span className="mx-2 truncate text-sm">{email}</span>
|
||||
</Menu.Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
|
||||
@@ -7,12 +7,19 @@ import { useWorkspace } from "~/providers/workspace";
|
||||
import { HiCheck } from "react-icons/hi2";
|
||||
|
||||
export default function WorkspaceMenu() {
|
||||
const { workspace, availableWorkspaces, switchWorkspace } = useWorkspace();
|
||||
const { workspace, isLoading, availableWorkspaces, switchWorkspace } =
|
||||
useWorkspace();
|
||||
const { openModal } = useModal();
|
||||
|
||||
return (
|
||||
<Menu as="div" className="relative inline-block w-full pb-3 text-left">
|
||||
<div>
|
||||
{isLoading ? (
|
||||
<div className="mb-1 flex p-1.5">
|
||||
<div className="h-6 w-6 animate-pulse rounded-[5px] bg-light-200 dark:bg-dark-200" />
|
||||
<div className="ml-2 h-6 w-[150px] animate-pulse rounded-[5px] bg-light-200 dark:bg-dark-200" />
|
||||
</div>
|
||||
) : (
|
||||
<Menu.Button className="mb-1 flex w-full items-center rounded-[5px] p-1.5 hover:bg-light-200 dark:hover:bg-dark-200">
|
||||
<span className="inline-flex h-6 w-6 items-center justify-center rounded-[5px] bg-indigo-700">
|
||||
<span className="text-xs font-bold leading-none text-white">
|
||||
@@ -23,6 +30,7 @@ export default function WorkspaceMenu() {
|
||||
{workspace?.name}
|
||||
</span>
|
||||
</Menu.Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
|
||||
@@ -3,7 +3,7 @@ import SideNavigation from "./SideNavigation";
|
||||
import FeedbackButton from "./FeedbackButton";
|
||||
|
||||
export default function Dashboard(props: { children: React.ReactNode }) {
|
||||
const { data } = api.auth.getUser.useQuery();
|
||||
const { data, isLoading } = api.auth.getUser.useQuery();
|
||||
|
||||
return (
|
||||
<div className="flex h-screen flex-col items-center bg-light-100 dark:bg-dark-50">
|
||||
@@ -17,7 +17,7 @@ export default function Dashboard(props: { children: React.ReactNode }) {
|
||||
</div>
|
||||
|
||||
<div className="flex h-full w-full">
|
||||
<SideNavigation user={{ email: data?.email }} />
|
||||
<SideNavigation user={{ email: data?.email }} isLoading={isLoading} />
|
||||
<div className="w-full overflow-hidden">{props.children}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useRouter } from "next/navigation";
|
||||
|
||||
interface WorkspaceContextProps {
|
||||
workspace: Workspace;
|
||||
isLoading: boolean;
|
||||
switchWorkspace: (_workspace: Workspace) => void;
|
||||
availableWorkspaces: Workspace[];
|
||||
}
|
||||
@@ -41,7 +42,7 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
|
||||
initialAvailableWorkspaces,
|
||||
);
|
||||
|
||||
const { data } = api.workspace.all.useQuery();
|
||||
const { data, isLoading } = api.workspace.all.useQuery();
|
||||
|
||||
const switchWorkspace = (_workspace: Workspace) => {
|
||||
localStorage.setItem("workspacePublicId", _workspace.publicId);
|
||||
@@ -97,7 +98,7 @@ export const WorkspaceProvider: React.FC<{ children: ReactNode }> = ({
|
||||
|
||||
return (
|
||||
<WorkspaceContext.Provider
|
||||
value={{ workspace, availableWorkspaces, switchWorkspace }}
|
||||
value={{ workspace, isLoading, availableWorkspaces, switchWorkspace }}
|
||||
>
|
||||
{children}
|
||||
</WorkspaceContext.Provider>
|
||||
|
||||
@@ -14,11 +14,13 @@ interface LabelSelectorProps {
|
||||
selected: boolean;
|
||||
colourCode: string;
|
||||
}[];
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export default function LabelSelector({
|
||||
cardPublicId,
|
||||
labels,
|
||||
isLoading,
|
||||
}: LabelSelectorProps) {
|
||||
const { openModal } = useModal();
|
||||
const utils = api.useUtils();
|
||||
@@ -47,6 +49,11 @@ export default function LabelSelector({
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<div className="flex w-full">
|
||||
<div className="h-full w-[175px] animate-pulse rounded-[5px] bg-light-300 dark:bg-dark-300" />
|
||||
</div>
|
||||
) : (
|
||||
<Menu
|
||||
as="div"
|
||||
className="relative flex w-full flex-wrap items-center text-left"
|
||||
@@ -145,6 +152,7 @@ export default function LabelSelector({
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ interface ListSelectorProps {
|
||||
name: string;
|
||||
selected: boolean;
|
||||
}[];
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export default function ListSelector({
|
||||
cardPublicId,
|
||||
lists,
|
||||
isLoading,
|
||||
}: ListSelectorProps) {
|
||||
const utils = api.useUtils();
|
||||
|
||||
@@ -42,6 +44,11 @@ export default function ListSelector({
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<div className="flex w-full">
|
||||
<div className="h-full w-[150px] animate-pulse rounded-[5px] bg-light-300 dark:bg-dark-300" />
|
||||
</div>
|
||||
) : (
|
||||
<Menu
|
||||
as="div"
|
||||
className="relative flex w-full flex-wrap items-center text-left"
|
||||
@@ -102,6 +109,7 @@ export default function ListSelector({
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,13 @@ interface MemberSelectorProps {
|
||||
};
|
||||
selected: boolean;
|
||||
}[];
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export default function LabelSelector({
|
||||
cardPublicId,
|
||||
members,
|
||||
isLoading,
|
||||
}: MemberSelectorProps) {
|
||||
const utils = api.useUtils();
|
||||
|
||||
@@ -46,6 +48,11 @@ export default function LabelSelector({
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<div className="flex w-full">
|
||||
<div className="h-full w-[125px] animate-pulse rounded-[5px] bg-light-300 dark:bg-dark-300" />
|
||||
</div>
|
||||
) : (
|
||||
<Menu
|
||||
as="div"
|
||||
className="relative flex w-full flex-wrap items-center text-left"
|
||||
@@ -118,7 +125,10 @@ export default function LabelSelector({
|
||||
onChange={formik.handleChange}
|
||||
checked={formik.values[member.publicId]}
|
||||
/>
|
||||
<label htmlFor={member.publicId} className="ml-3 text-sm">
|
||||
<label
|
||||
htmlFor={member.publicId}
|
||||
className="ml-3 text-sm"
|
||||
>
|
||||
{member?.user?.name}
|
||||
</label>
|
||||
</div>
|
||||
@@ -129,6 +139,7 @@ export default function LabelSelector({
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -152,15 +152,27 @@ export default function CardPage() {
|
||||
<div className="min-w-[325px] border-l-[1px] border-light-600 bg-light-200 p-8 text-light-900 dark:border-dark-400 dark:bg-dark-100 dark:text-dark-900">
|
||||
<div className="mb-4 flex w-full">
|
||||
<p className="my-2 w-[100px] text-sm">List</p>
|
||||
<ListSelector cardPublicId={cardId} lists={formattedLists} />
|
||||
<ListSelector
|
||||
cardPublicId={cardId}
|
||||
lists={formattedLists}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4 flex w-full">
|
||||
<p className="my-2 w-[100px] text-sm">Labels</p>
|
||||
<LabelSelector cardPublicId={cardId} labels={formattedLabels} />
|
||||
<LabelSelector
|
||||
cardPublicId={cardId}
|
||||
labels={formattedLabels}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full">
|
||||
<p className="my-2 w-[100px] text-sm">Members</p>
|
||||
<MemberSelector cardPublicId={cardId} members={formattedMembers} />
|
||||
<MemberSelector
|
||||
cardPublicId={cardId}
|
||||
members={formattedMembers}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user