diff --git a/src/app/(pages)/dashboard/user/billing/page.tsx b/src/app/(pages)/dashboard/user/billing/page.tsx index 11d5fac..21c8975 100644 --- a/src/app/(pages)/dashboard/user/billing/page.tsx +++ b/src/app/(pages)/dashboard/user/billing/page.tsx @@ -10,7 +10,7 @@ import UserSettingsHeader from "@/components/dashboard/user/user-settings-header * @return the page jsx */ const UserBillingPage = (): ReactElement => ( -
+
{/* Content */} diff --git a/src/app/(pages)/dashboard/user/profile/page.tsx b/src/app/(pages)/dashboard/user/profile/page.tsx index a154b51..d05b889 100644 --- a/src/app/(pages)/dashboard/user/profile/page.tsx +++ b/src/app/(pages)/dashboard/user/profile/page.tsx @@ -14,7 +14,7 @@ import UserSettingsHeader from "@/components/dashboard/user/user-settings-header * @return the page jsx */ const UserProfilePage = (): ReactElement => ( -
+
{/* Content */} diff --git a/src/app/(pages)/dashboard/user/settings/page.tsx b/src/app/(pages)/dashboard/user/settings/page.tsx index bef5fb2..7c7f980 100644 --- a/src/app/(pages)/dashboard/user/settings/page.tsx +++ b/src/app/(pages)/dashboard/user/settings/page.tsx @@ -12,7 +12,7 @@ import DevicesSetting from "@/components/dashboard/user/settings/device/devices- * @return the page jsx */ const UserSettingsPage = (): ReactElement => ( -
+
{/* Content */} diff --git a/src/app/provider/organization-provider.tsx b/src/app/provider/organization-provider.tsx index c6d5ea9..018384e 100644 --- a/src/app/provider/organization-provider.tsx +++ b/src/app/provider/organization-provider.tsx @@ -37,20 +37,24 @@ const OrganizationProvider = ({ children }: { children: ReactNode }) => { * Fetch the organizations for the logged in user. */ const fetchOrganizations = useCallback(async () => { - let selectedOrganization: string | null = localStorage.getItem( - "selected-organization" - ); const { data, error } = await apiRequest({ endpoint: "/organization/@me", session, }); + const selectedOrgSnowflake: string | null = localStorage.getItem( + "selected-organization" + ); const organizations: Organization[] = data as Organization[]; - if (!selectedOrganization && organizations.length > 0) { - selectedOrganization = organizations[0].snowflake; + let selected: Organization | undefined; + if (!selected && organizations.length > 0) { + selected = organizations[0]; + } else { + selected = organizations.find( + (organization: Organization) => + organization.snowflake === selectedOrgSnowflake + ); } - storeRef.current - ?.getState() - .update(selectedOrganization || undefined, organizations); + storeRef.current?.getState().update(organizations, selected); }, [session]); useEffect(() => { diff --git a/src/app/store/organization-store.ts b/src/app/store/organization-store.ts index 3443c13..6743c9d 100644 --- a/src/app/store/organization-store.ts +++ b/src/app/store/organization-store.ts @@ -13,15 +13,15 @@ export const OrganizationContext = createContext( * The props in this store. */ export type OrganizationStoreProps = { - /** - * The currently selected organization. - */ - selected: string | undefined; - /** * The organization's the user has. */ organizations: Organization[]; + + /** + * The currently selected organization. + */ + selected: Organization | undefined; }; /** @@ -31,12 +31,12 @@ export type OrganizationState = OrganizationStoreProps & { /** * Update the state. * - * @param selected the selected organization * @param organizations the user's organizations + * @param selected the selected organization */ update: ( - selected: string | undefined, - organizations: Organization[] + organizations: Organization[], + selected: Organization | undefined ) => void; /** @@ -44,7 +44,7 @@ export type OrganizationState = OrganizationStoreProps & { * * @param selected the selected organization */ - setSelected: (selected: string | undefined) => void; + setSelected: (selected: Organization | undefined) => void; }; /** @@ -62,9 +62,11 @@ const createOrganizationStore = () => { }; return createStore()((set) => ({ ...defaultProps, - update: (selected: string | undefined, organizations: Organization[]) => - set(() => ({ selected, organizations })), - setSelected: (selected: string | undefined) => + update: ( + organizations: Organization[], + selected: Organization | undefined + ) => set(() => ({ organizations, selected })), + setSelected: (selected: Organization | undefined) => set(() => ({ selected })), })); }; diff --git a/src/components/dashboard/sidebar/organization-selector.tsx b/src/components/dashboard/sidebar/organization-selector.tsx index 30fe677..1358aa2 100644 --- a/src/components/dashboard/sidebar/organization-selector.tsx +++ b/src/components/dashboard/sidebar/organization-selector.tsx @@ -29,36 +29,14 @@ import OrganizationLogo from "@/components/org/organization-logo"; * @return the selector jsx */ const OrganizationSelector = (): ReactElement => { - const selectedOrganization: string | undefined = useOrganizationContext( - (state: OrganizationState) => state.selected - ); - const setSelectedOrganization = useOrganizationContext( - (state) => state.setSelected - ); const organizations: Organization[] = useOrganizationContext( (state: OrganizationState) => state.organizations ); + const selected: Organization | undefined = useOrganizationContext( + (state: OrganizationState) => state.selected + ); + const setSelected = useOrganizationContext((state) => state.setSelected); const [open, setOpen] = useState(false); - const [selected, setSelected] = useState(); - - // Set the selected organization - useEffect(() => { - const toSelect: Organization | undefined = organizations?.find( - (organization: Organization) => { - return organization.snowflake === selectedOrganization; - } - ); - // Update the state for this page - setSelected( - toSelect || - (organizations?.length > 0 ? organizations[0] : undefined) - ); - - // Update the state for all pages - if (!toSelect && organizations?.length > 0) { - setSelectedOrganization(organizations[0].snowflake); - } - }, [organizations, selectedOrganization, setSelectedOrganization]); /** * Handle selecting an organization. @@ -68,7 +46,6 @@ const OrganizationSelector = (): ReactElement => { const selectOrganization = (organization: Organization) => { setOpen(false); setSelected(organization); - setSelectedOrganization(organization.snowflake); localStorage.setItem("selected-organization", organization.snowflake); }; @@ -122,8 +99,7 @@ const OrganizationSelector = (): ReactElement => { size="sm" /> {organization.name} - {organization.snowflake === - selectedOrganization && ( + {organization === selected && ( )} diff --git a/src/components/dashboard/sidebar/sidebar-links.tsx b/src/components/dashboard/sidebar/sidebar-links.tsx index ff668cf..4d88bd6 100644 --- a/src/components/dashboard/sidebar/sidebar-links.tsx +++ b/src/components/dashboard/sidebar/sidebar-links.tsx @@ -17,6 +17,7 @@ import { import { useOrganizationContext } from "@/app/provider/organization-provider"; import { OrganizationState } from "@/app/store/organization-store"; import { usePathname } from "next/navigation"; +import { Organization } from "@/app/types/org/organization"; const links: SidebarLink[] = [ { @@ -27,32 +28,32 @@ const links: SidebarLink[] = [ { name: "Status Pages", icon: , - href: "/dashboard/{org}/status-pages", + href: "/dashboard/status-pages", }, { name: "Automations", icon: , - href: "/dashboard/{org}/automations", + href: "/dashboard/automations", }, { name: "Incidents", icon: , - href: "/dashboard/{org}/incidents", + href: "/dashboard/incidents", }, { name: "Insights", icon: , - href: "/dashboard/{org}/insights", + href: "/dashboard/insights", }, { name: "Audit Logs", icon: , - href: "/dashboard/{org}/audit", + href: "/dashboard/audit", }, { name: "Settings", icon: , - href: "/dashboard/{org}/settings", + href: "/dashboard/settings", }, ]; @@ -63,37 +64,39 @@ const links: SidebarLink[] = [ * @return the links jsx */ const Links = (): ReactElement => { - const selectedOrganization: string | undefined = useOrganizationContext( - (state: OrganizationState) => state.selected - ); + const selectedOrganization: Organization | undefined = + useOrganizationContext((state: OrganizationState) => state.selected); const path: string = usePathname(); return (
- {links.map((link: SidebarLink, index: number) => { - const href: string = link.href.replace( - "{org}", - selectedOrganization as string - ); - const active: boolean = path.startsWith(href); - return ( - - + index === 0 || (index > 0 && selectedOrganization) + ) + .map((link: SidebarLink, index: number) => { + const active: boolean = path.startsWith(link.href); + return ( + -
{link.icon}
- {link.name} - -
- ); - })} + +
+ {link.icon} +
+ {link.name} + +
+ ); + })}
); }; diff --git a/src/components/dashboard/user/settings/device/device.tsx b/src/components/dashboard/user/settings/device/device.tsx index e48581e..beee1da 100644 --- a/src/components/dashboard/user/settings/device/device.tsx +++ b/src/components/dashboard/user/settings/device/device.tsx @@ -34,7 +34,7 @@ const Device = ({ setTimeSinceFirstLogin( DateTime.fromISO(device.firstLogin.toString()).toRelative() ); - }, 60000); + }, 1000); return () => clearInterval(interval); }, [device.firstLogin]); diff --git a/src/components/dashboard/user/settings/device/devices-setting.tsx b/src/components/dashboard/user/settings/device/devices-setting.tsx index 9b9cb7f..55ca41e 100644 --- a/src/components/dashboard/user/settings/device/devices-setting.tsx +++ b/src/components/dashboard/user/settings/device/devices-setting.tsx @@ -9,6 +9,7 @@ import { Device as DeviceType } from "@/app/types/user/device"; import { apiRequest } from "@/lib/api"; import Device from "@/components/dashboard/user/settings/device/device"; import { DateTime } from "luxon"; +import DevicesSkeleton from "@/components/dashboard/user/settings/device/devices-skeleton"; /** * The setting that allows a @@ -52,23 +53,34 @@ const DevicesSetting = (): ReactElement => { {/* Setting */}
- {devices - ?.sort( - (a: DeviceType, b: DeviceType) => - DateTime.fromISO( - b.firstLogin.toString() - ).toMillis() - - DateTime.fromISO(a.firstLogin.toString()).toMillis() - ) - .map((device: DeviceType, index: number) => ( - - ))} + {!devices ? ( + <> + {Array.from({ length: 4 }, (_, index) => ( + + ))} + + ) : ( + devices + .sort( + (a: DeviceType, b: DeviceType) => + DateTime.fromISO( + b.firstLogin.toString() + ).toMillis() - + DateTime.fromISO( + a.firstLogin.toString() + ).toMillis() + ) + .map((device: DeviceType, index: number) => ( + + )) + )}
); diff --git a/src/components/dashboard/user/settings/device/devices-skeleton.tsx b/src/components/dashboard/user/settings/device/devices-skeleton.tsx new file mode 100644 index 0000000..c5ff76f --- /dev/null +++ b/src/components/dashboard/user/settings/device/devices-skeleton.tsx @@ -0,0 +1,16 @@ +import { ReactElement } from "react"; +import { Skeleton } from "@/components/ui/skeleton"; + +/** + * The skeleton to indicate the + * loading of the user's devices. + * + * @param index the skeleton index + * @return the skeleton jsx + */ +const DevicesSkeleton = ({ index }: { index: number }): ReactElement => ( +
+ +
+); +export default DevicesSkeleton; diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx new file mode 100644 index 0000000..a51daf7 --- /dev/null +++ b/src/components/ui/skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils"; + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ); +} + +export { Skeleton };