"use client"; import { useState, createContext, useContext, useEffect } from "react"; import { useSearchParams } from "next/navigation"; import { Header } from "../PageHeader"; import { Footer } from "components/ActionBar/Footer"; import { Sidebar } from "components/ActionBar/Sidebar"; import { DesktopNavigation, MobileNavigation, navPages, } from "components/ActionBar/Navigation"; import { create } from "zustand"; import { Popover } from "components/Popover"; import { Checkbox } from "components/Checkbox"; import { Separator } from "components/Layout"; import { CloseTiny } from "components/Icons/CloseTiny"; import { MediaContents } from "components/Media"; import { SortSmall } from "components/Icons/SortSmall"; import { TabsSmall } from "components/Icons/TabsSmall"; import { Input } from "components/Input"; import { SearchTiny } from "components/Icons/SearchTiny"; import { InterfaceState, useIdentityData } from "components/IdentityProvider"; import { updateIdentityInterfaceState } from "actions/updateIdentityInterfaceState"; import Link from "next/link"; import { ExternalLinkTiny } from "components/Icons/ExternalLinkTiny"; import { usePreserveScroll } from "src/hooks/usePreserveScroll"; export type DashboardState = { display?: "grid" | "list"; sort?: "created" | "alphabetical"; filter: { drafts: boolean; published: boolean; docs: boolean; templates: boolean; }; }; type DashboardStore = { dashboards: { [id: string]: DashboardState }; setDashboard: (id: string, partial: Partial) => void; }; const defaultDashboardState: DashboardState = { display: undefined, sort: undefined, filter: { drafts: false, published: false, docs: false, templates: false }, }; export const useDashboardStore = create((set, get) => ({ dashboards: {}, setDashboard: (id: string, partial: Partial) => { console.log(partial); set((state) => ({ dashboards: { ...state.dashboards, [id]: { ...(state.dashboards[id] || defaultDashboardState), ...partial, }, }, })); }, })); const DashboardIdContext = createContext(null); export const useDashboardId = () => { const id = useContext(DashboardIdContext); if (!id) { throw new Error("useDashboardId must be used within a DashboardLayout"); } return id; }; export const useDashboardState = () => { const id = useDashboardId(); let { identity } = useIdentityData(); let localState = useDashboardStore( (state) => state.dashboards[id] || defaultDashboardState, ); if (!identity) return localState; let metadata = identity.interface_state as InterfaceState; return metadata?.dashboards?.[id] || defaultDashboardState; }; export const useSetDashboardState = () => { const id = useDashboardId(); let { identity, mutate } = useIdentityData(); const setDashboard = useDashboardStore((state) => state.setDashboard); return async (partial: Partial) => { if (!identity) return setDashboard(id, partial); let interface_state = (identity.interface_state as InterfaceState) || {}; let newDashboardState = { ...defaultDashboardState, ...interface_state.dashboards?.[id], ...partial, }; mutate( { ...identity, interface_state: { ...interface_state, dashboards: { ...interface_state.dashboards, [id]: newDashboardState, }, }, }, { revalidate: false }, ); await updateIdentityInterfaceState({ ...interface_state, dashboards: { [id]: newDashboardState, }, }); }; }; export function DashboardLayout< T extends { [name: string]: { content: React.ReactNode; controls: React.ReactNode; }; }, >(props: { id: string; cardBorderHidden: boolean; tabs: T; defaultTab: keyof T; currentPage: navPages; publication?: string; actions: React.ReactNode; }) { const searchParams = useSearchParams(); const tabParam = searchParams.get("tab"); // Initialize tab from search param if valid, otherwise use default const initialTab = tabParam && props.tabs[tabParam] ? tabParam : props.defaultTab; let [tab, setTab] = useState(initialTab); // Custom setter that updates both state and URL const setTabWithUrl = (newTab: keyof T) => { setTab(newTab); const params = new URLSearchParams(searchParams.toString()); params.set("tab", newTab as string); const newUrl = `${window.location.pathname}?${params.toString()}`; window.history.replaceState(null, "", newUrl); }; let { content, controls } = props.tabs[tab]; let { ref } = usePreserveScroll( `dashboard-${props.id}-${tab as string}`, ); let [headerState, setHeaderState] = useState<"default" | "controls">( "default", ); return (
{props.actions && {props.actions}}
{Object.keys(props.tabs).length <= 1 && !controls ? null : ( <>
{headerState === "default" ? ( <> {Object.keys(props.tabs).length > 1 && (
{Object.keys(props.tabs).map((t) => { return ( setTabWithUrl(t)} /> ); })}
)} {props.publication && ( )}
{controls}
) : ( <> {controls} )}
)} {content}
{props.actions && ( <> {props.actions} )}
); } export const HomeDashboardControls = (props: { searchValue: string; setSearchValueAction: (searchValue: string) => void; hasBackgroundImage: boolean; defaultDisplay: Exclude; hasPubs: boolean; hasTemplates: boolean; }) => { let { display, sort } = useDashboardState(); console.log({ display, props }); display = display || props.defaultDisplay; let setState = useSetDashboardState(); let { identity } = useIdentityData(); console.log(props); return (
{identity && ( )}
{props.hasPubs || props.hasTemplates ? ( <> {props.hasPubs} {props.hasTemplates} {" "} ) : null}
); }; export const PublicationDashboardControls = (props: { searchValue: string; setSearchValueAction: (searchValue: string) => void; hasBackgroundImage: boolean; defaultDisplay: Exclude; }) => { let { display, sort } = useDashboardState(); console.log({ display, props }); display = display || props.defaultDisplay; let setState = useSetDashboardState(); return (
); }; const SortToggle = (props: { setState: (partial: Partial) => Promise; sort: string | undefined; }) => { return ( ); }; const DisplayToggle = (props: { setState: (partial: Partial) => Promise; display: string | undefined; }) => { return ( ); }; function Tab(props: { name: string; selected: boolean; onSelect: () => void; href?: string; }) { return (
props.onSelect()} > {props.name} {props.href && }
); } const FilterOptions = (props: { hasPubs: boolean; hasTemplates: boolean }) => { let { filter } = useDashboardState(); let setState = useSetDashboardState(); let filterCount = Object.values(filter).filter(Boolean).length; return ( Filter {filterCount > 0 && `(${filterCount})`}} > {props.hasPubs && ( <> setState({ filter: { ...filter, drafts: !!e.target.checked }, }) } > Drafts setState({ filter: { ...filter, published: !!e.target.checked }, }) } > Published )} {props.hasTemplates && ( <> setState({ filter: { ...filter, templates: !!e.target.checked }, }) } > Templates )} setState({ filter: { ...filter, docs: !!e.target.checked }, }) } > Docs
); }; const SearchInput = (props: { searchValue: string; setSearchValue: (searchValue: string) => void; hasBackgroundImage: boolean; }) => { return (
{ props.setSearchValue(e.currentTarget.value); }} />
); };