Compare commits

...

2 Commits

Author SHA1 Message Date
Kyle Doherty
c076d8e2b4 text fix 2025-07-22 14:46:12 -04:00
Kyle Doherty
d486011399 layout and styling tweaks 2025-07-22 14:23:24 -04:00
3 changed files with 157 additions and 53 deletions

View File

@@ -6,7 +6,6 @@ import { useEffect } from "react";
import { Box } from "metabase/ui";
import styles from "./Editor.module.css";
import { EditorToolbar } from "./EditorToolbar";
import { QuestionMentionPlugin } from "./QuestionMentionPlugin";
import {
MarkdownSerializer,
@@ -84,7 +83,6 @@ export const Editor: React.FC<EditorProps> = ({
return (
<Box className={styles.editor}>
<EditorToolbar editor={editor} />
<Box
className={styles.editorContent}
onClick={(e) => {

View File

@@ -1,9 +1,52 @@
.reportPage {
display: flex;
flex-direction: column;
height: calc(100vh - var(--nav-sidebar-height, 60px));
background-color: var(--mb-color-bg-white);
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 0;
background-color: var(--mb-color-bg-white);
gap: 1rem;
max-width: 900px;
margin: 0 auto;
width: 100%;
}
.titleInput {
flex: 1;
max-width: 600px;
border: none;
outline: none;
background: transparent;
font-size: 2rem;
font-weight: 700;
line-height: 1.2;
color: var(--mb-color-text-dark);
font-family: inherit;
padding: 0;
margin: 0;
}
.titleInput::placeholder {
color: var(--mb-color-text-light);
opacity: 0.7;
}
.titleInput:focus {
outline: none;
}
.contentArea {
display: flex;
flex: 1;
overflow: hidden;
}
.mainContent {
flex: 1;
display: flex;

View File

@@ -1,7 +1,18 @@
import { useCallback, useState } from "react";
import { t } from "ttag";
import { Box, Button, Icon, Loader, Stack, Text } from "metabase/ui";
import {
ActionIcon,
Box,
Button,
Flex,
Icon,
Loader,
Menu,
Paper,
Stack,
Text,
} from "metabase/ui";
import { Editor } from "./Editor";
import styles from "./ReportPage.module.css";
@@ -13,6 +24,8 @@ export const ReportPage = () => {
Array<{ id: number; name: string }>
>([]);
const [isDownloading, setIsDownloading] = useState(false);
const [reportTitle, setReportTitle] = useState("");
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const handleSave = useCallback(() => {
if (!editorInstance) {
@@ -21,7 +34,7 @@ export const ReportPage = () => {
const markdown = editorInstance.storage.markdown?.getMarkdown();
const reportData = {
title: "New Report",
title: reportTitle,
content: markdown,
questionReferences: questionRefs,
createdAt: new Date().toISOString(),
@@ -29,7 +42,16 @@ export const ReportPage = () => {
// eslint-disable-next-line no-console
console.log("Report data to save:", reportData);
}, [editorInstance, questionRefs]);
}, [editorInstance, questionRefs, reportTitle]);
const handleSaveAndRun = useCallback(() => {
handleSave();
// TODO: Add run logic here
}, [handleSave]);
const toggleSidebar = useCallback(() => {
setIsSidebarOpen((prev) => !prev);
}, []);
const handleQuestionClick = useCallback(
(questionId: number) => {
@@ -98,56 +120,97 @@ export const ReportPage = () => {
return (
<Box className={styles.reportPage}>
<Box className={styles.mainContent}>
<Box className={styles.documentContainer}>
<Editor
onEditorReady={setEditorInstance}
onQuestionRefsChange={setQuestionRefs}
/>
</Box>
</Box>
<Box className={styles.contentArea}>
<Box className={styles.mainContent}>
<Flex p="sm">
<ActionIcon
variant="subtle"
size="md"
ml="auto"
onClick={toggleSidebar}
aria-label={isSidebarOpen ? t`Hide sidebar` : t`Show sidebar`}
>
<Icon name={isSidebarOpen ? "sidebar_open" : "sidebar_closed"} />
</ActionIcon>
</Flex>
<Box className={styles.documentContainer}>
<Box className={styles.header} mt="xl" pt="xl">
<input
value={reportTitle}
onChange={(event) => setReportTitle(event.currentTarget.value)}
placeholder={t`New report`}
className={styles.titleInput}
/>
<Box
style={{ display: "flex", gap: "0.5rem", alignItems: "center" }}
>
<Button variant="filled" onClick={handleSaveAndRun} size="md">
{t`Save and run`}
</Button>
<Box className={styles.sidebar}>
<Stack gap="lg" p="lg">
<Button variant="filled" onClick={handleSave} fullWidth>
{t`Save Report`}
</Button>
<Button
variant="outline"
onClick={handleDownloadMarkdown}
leftSection={
isDownloading ? <Loader size="xs" /> : <Icon name="download" />
}
fullWidth
disabled={isDownloading}
>
{isDownloading ? t`Downloading...` : t`Download`}
</Button>
<Box>
<Text size="sm" fw="bold" mb="sm">
{t`Question References`}
</Text>
{questionRefs.length === 0 ? (
<Text size="sm" color="text-light">
{t`No questions embedded yet`}
</Text>
) : (
<Stack gap="xs">
{questionRefs.map((ref) => (
<Box
key={ref.id}
className={styles.questionRef}
onClick={() => handleQuestionClick(ref.id)}
>
<Text size="sm">{ref.name}</Text>
</Box>
))}
</Stack>
)}
<Menu position="bottom-end">
<Menu.Target>
<ActionIcon
variant="subtle"
size="md"
aria-label={t`More options`}
>
<Icon name="ellipsis" />
</ActionIcon>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={
isDownloading ? (
<Loader size="xs" />
) : (
<Icon name="download" />
)
}
onClick={handleDownloadMarkdown}
disabled={isDownloading}
>
{isDownloading ? t`Downloading...` : t`Download`}
</Menu.Item>
</Menu.Dropdown>
</Menu>
</Box>
</Box>
<Editor
onEditorReady={setEditorInstance}
onQuestionRefsChange={setQuestionRefs}
/>
</Box>
</Stack>
</Box>
{isSidebarOpen && (
<Box className={styles.sidebar}>
<Stack gap="lg" p="lg">
<Paper>
<Text size="sm" fw="bold" mb="sm">
{t`Question References`}
</Text>
{questionRefs.length === 0 ? (
<Text size="sm" color="text-light">
{t`No questions embedded yet`}
</Text>
) : (
<Stack gap="xs">
{questionRefs.map((ref) => (
<Box
key={ref.id}
className={styles.questionRef}
onClick={() => handleQuestionClick(ref.id)}
>
<Text size="sm">{ref.name}</Text>
</Box>
))}
</Stack>
)}
</Paper>
</Stack>
</Box>
)}
</Box>
</Box>
);