diff --git a/src/frontend/src/components/nav/Layout.tsx b/src/frontend/src/components/nav/Layout.tsx
index c3b084c835..c13e8e50b8 100644
--- a/src/frontend/src/components/nav/Layout.tsx
+++ b/src/frontend/src/components/nav/Layout.tsx
@@ -29,6 +29,7 @@ import {
type PluginUIFeature,
PluginUIFeatureType
} from '../plugins/PluginUIFeature';
+import GlobalPreviewDrawer from '../previews/GlobalPreviewDrawer';
import { Footer } from './Footer';
import { Header } from './Header';
@@ -146,6 +147,7 @@ export default function LayoutComponent() {
)}
+
>
);
diff --git a/src/frontend/src/components/previews/GlobalPreviewDrawer.tsx b/src/frontend/src/components/previews/GlobalPreviewDrawer.tsx
new file mode 100644
index 0000000000..c926fef5a3
--- /dev/null
+++ b/src/frontend/src/components/previews/GlobalPreviewDrawer.tsx
@@ -0,0 +1,22 @@
+import { usePreviewDrawerState } from '../../states/PreviewDrawerState';
+import PreviewDrawer from './PreviewDrawer';
+
+export default function GlobalPreviewDrawer() {
+ const isOpen = usePreviewDrawerState((state) => state.isOpen);
+ const modelType = usePreviewDrawerState((state) => state.modelType);
+ const instance = usePreviewDrawerState((state) => state.instance);
+ const closePreview = usePreviewDrawerState((state) => state.closePreview);
+
+ if (!isOpen || instance === null) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/src/frontend/src/components/previews/PreviewDrawer.tsx b/src/frontend/src/components/previews/PreviewDrawer.tsx
new file mode 100644
index 0000000000..1a0be1edaa
--- /dev/null
+++ b/src/frontend/src/components/previews/PreviewDrawer.tsx
@@ -0,0 +1,39 @@
+import { t } from '@lingui/core/macro';
+import { Drawer, Stack, Text } from '@mantine/core';
+
+import { StylishText } from '@lib/components/StylishText';
+import type { ModelType } from '@lib/index';
+import { getModelInfo } from '../render/ModelType';
+
+export default function PreviewDrawer({
+ modelType,
+ instance,
+ opened,
+ onClose
+}: Readonly<{
+ modelType?: ModelType;
+ instance?: any;
+ opened: boolean;
+ onClose: () => void;
+}>) {
+ const modelInfo = getModelInfo(instance.model);
+
+ return (
+
+ TODO - implement actual preview content
+
+ }
+ opened={opened}
+ onClose={onClose}
+ withCloseButton
+ >
+
+ {t`Preview for ${modelInfo.label} #${instance.pk}`}
+
+
+ );
+}
diff --git a/src/frontend/src/states/PreviewDrawerState.tsx b/src/frontend/src/states/PreviewDrawerState.tsx
new file mode 100644
index 0000000000..9ff5574c7f
--- /dev/null
+++ b/src/frontend/src/states/PreviewDrawerState.tsx
@@ -0,0 +1,37 @@
+import type { ModelType } from '@lib/enums/ModelType';
+import { create } from 'zustand';
+
+interface PreviewDrawerStateProps {
+ isOpen: boolean;
+ modelType?: ModelType;
+ instance?: any;
+ openPreview: (modelType: ModelType, instance: any) => void;
+ closePreview: () => void;
+}
+
+export const usePreviewDrawerState = create()(
+ (set) => ({
+ isOpen: false,
+ instance: null,
+
+ openPreview: (modelType: ModelType, instance: any) => {
+ set({ modelType, instance, isOpen: true });
+ },
+
+ closePreview: () => {
+ set({ modelType: undefined, instance: null, isOpen: false });
+ }
+ })
+);
+
+export function openGlobalPreview(modelType: ModelType, instance: any) {
+ usePreviewDrawerState.getState().openPreview(modelType, instance);
+}
+
+export function closeGlobalPreview() {
+ usePreviewDrawerState.getState().closePreview();
+}
+
+export function getGlobalPreviewState() {
+ return usePreviewDrawerState.getState();
+}