diff --git a/web/src/components/features/buttons/CollectionsButton.tsx b/web/src/components/features/buttons/CollectionsButton.tsx
index a1983f7bc..e72561e43 100644
--- a/web/src/components/features/buttons/CollectionsButton.tsx
+++ b/web/src/components/features/buttons/CollectionsButton.tsx
@@ -1,14 +1,17 @@
import React from 'react';
import { ASSET_ICONS } from '../config/assetIcons';
import { Button } from '@/components/ui/buttons';
+import { AppTooltip } from '@/components/ui/tooltip';
export const CollectionButton: React.FC<{
buttonType?: 'ghost' | 'default';
useText?: boolean;
}> = ({ buttonType = 'default', useText = false }) => {
return (
- } variant={buttonType}>
- {useText ? 'Collections' : ''}
-
+
+ } variant={buttonType}>
+ {useText ? 'Collections' : ''}
+
+
);
};
diff --git a/web/src/components/features/buttons/ShareButton.tsx b/web/src/components/features/buttons/ShareButton.tsx
index 2b068444f..b1164f297 100644
--- a/web/src/components/features/buttons/ShareButton.tsx
+++ b/web/src/components/features/buttons/ShareButton.tsx
@@ -1,9 +1,9 @@
import { Button } from '@/components/ui/buttons';
-import { ShareRight3 } from '@/components/ui/icons';
+import { ShareRight, ShareRight3 } from '@/components/ui/icons';
import React from 'react';
export const ShareButton = React.memo(() => {
- return } />;
+ return } />;
});
ShareButton.displayName = 'ShareButton';
diff --git a/web/src/components/ui/grid/BusterResizeableGrid.stories.tsx b/web/src/components/ui/grid/BusterResizeableGrid.stories.tsx
index aa099e0de..f0502e3de 100644
--- a/web/src/components/ui/grid/BusterResizeableGrid.stories.tsx
+++ b/web/src/components/ui/grid/BusterResizeableGrid.stories.tsx
@@ -164,3 +164,61 @@ export const CustomOverlay: Story = {
)
}
};
+
+export const EmptyColumnSizes: Story = {
+ args: {
+ rows: [
+ {
+ id: uuidv4(),
+ items: [
+ {
+ id: uuidv4(),
+ children:
+ },
+ {
+ id: uuidv4(),
+ children:
+ }
+ ],
+ columnSizes: [],
+ rowHeight: MIN_ROW_HEIGHT
+ },
+ {
+ id: uuidv4(),
+ items: [
+ {
+ id: uuidv4(),
+ children:
+ },
+ {
+ id: uuidv4(),
+ children:
+ },
+ {
+ id: uuidv4(),
+ children:
+ }
+ ],
+ columnSizes: [],
+ rowHeight: MIN_ROW_HEIGHT
+ },
+ {
+ id: uuidv4(),
+ items: [
+ {
+ id: uuidv4(),
+ children:
+ },
+ {
+ id: uuidv4(),
+ children:
+ }
+ ],
+ columnSizes: [],
+ rowHeight: MIN_ROW_HEIGHT
+ }
+ ],
+ onRowLayoutChange: fn(),
+ readOnly: false
+ }
+};
diff --git a/web/src/components/ui/grid/BusterResizeableGrid.tsx b/web/src/components/ui/grid/BusterResizeableGrid.tsx
index fe2144a4b..0151e2b70 100644
--- a/web/src/components/ui/grid/BusterResizeableGrid.tsx
+++ b/web/src/components/ui/grid/BusterResizeableGrid.tsx
@@ -57,8 +57,6 @@ export const BusterResizeableGrid: React.FC<{
const [rows, setRows] = useState(serverRows);
const styleRef = useRef(undefined);
- console.log(serverRows);
-
const onRowLayoutChangePreflight = useMemoizedFn((newLayout: BusterResizeableGridRow[]) => {
const filteredRows = newRowPreflight(newLayout);
diff --git a/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardContainerHeaderButtons.tsx b/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardContainerHeaderButtons.tsx
index feef5cd32..a5343c365 100644
--- a/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardContainerHeaderButtons.tsx
+++ b/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardContainerHeaderButtons.tsx
@@ -10,6 +10,7 @@ import { ShareDashboardButton } from '@/components/features/buttons/ShareDashboa
import { Button } from '@/components/ui/buttons';
import { Plus } from '@/components/ui/icons';
import { DashboardThreeDotMenu } from './DashboardThreeDotMenu';
+import { AppTooltip } from '@/components/ui/tooltip';
export const DashboardContainerHeaderButtons: React.FC = React.memo(
() => {
@@ -19,7 +20,8 @@ export const DashboardContainerHeaderButtons: React.FC
-
+
+
@@ -39,9 +41,9 @@ SaveToCollectionButton.displayName = 'SaveToCollectionButton';
const AddContentToDashboardButton = React.memo(() => {
return (
-
+
);
});
AddContentToDashboardButton.displayName = 'AddContentToDashboardButton';
diff --git a/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx b/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx
index 2832bfad4..490d4cb3e 100644
--- a/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx
+++ b/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx
@@ -251,7 +251,11 @@ const useFilterDashboardSelectMenu = () => {
label: 'Filter dashboard',
value: 'filter-dashboard',
icon: ,
- items: [Coming soon...
]
+ items: [
+
+ Coming soon...
+
+ ]
}),
[]
);
diff --git a/web/src/middleware.ts b/web/src/middleware.ts
index 17de75e14..c77cef29d 100644
--- a/web/src/middleware.ts
+++ b/web/src/middleware.ts
@@ -13,7 +13,6 @@ export async function middleware(request: NextRequest) {
nextPathnameMiddleware(request, supabaseResponse);
if (performUserCheck && !user) {
- console.log(performUserCheck, user, request.nextUrl.pathname);
return NextResponse.redirect(
new URL(createBusterRoute({ route: BusterRoutes.AUTH_LOGIN }), process.env.NEXT_PUBLIC_URL)
);
diff --git a/web/src/middleware/cspPolicyMiddleware.ts b/web/src/middleware/cspPolicyMiddleware.ts
index a7b123888..e1bdb9434 100644
--- a/web/src/middleware/cspPolicyMiddleware.ts
+++ b/web/src/middleware/cspPolicyMiddleware.ts
@@ -43,7 +43,7 @@ const embedCspHeader = {
// Fonts
"font-src 'self' https://fonts.gstatic.com",
// Frame ancestors - allow embedding from any domain for /embed routes
- 'frame-ancestors *',
+ `frame-ancestors 'self' *`,
// Connect sources for API calls
"connect-src 'self' https://*.vercel.app https://*.supabase.co wss://*.supabase.co",
// Media
@@ -65,15 +65,23 @@ export const cspPolicyMiddleware = (request: NextRequest) => {
// Add CSP headers based on route
request.headers.set(
'Content-Security-Policy',
- isEmbedRoute
+ (isEmbedRoute
? embedCspHeader['Content-Security-Policy']
: defaultCspHeader['Content-Security-Policy']
+ ).trim()
);
// Add additional security headers
- if (!isEmbedRoute) request.headers.set('X-Frame-Options', 'DENY');
+ if (isEmbedRoute) {
+ request.headers.set('X-Frame-Options', 'ALLOW-FROM *');
+ } else {
+ request.headers.set('X-Frame-Options', 'DENY');
+ }
request.headers.set('X-Content-Type-Options', 'nosniff');
request.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
+ request.headers.set('frame-ancestors', '*');
+
+ console.log('request.headers', isEmbedRoute ? 'embed' : 'default', request.headers);
return request;
};
diff --git a/web/src/middleware/publicPageMiddleware.test.ts b/web/src/middleware/publicPageMiddleware.test.ts
new file mode 100644
index 000000000..da64637e6
--- /dev/null
+++ b/web/src/middleware/publicPageMiddleware.test.ts
@@ -0,0 +1,31 @@
+import { isEmbedPage } from './publicPageMiddleware';
+
+// Unit tests for isEmbedPage
+describe('isEmbedPage', () => {
+ it('should return true for embed metric routes', () => {
+ const request = {
+ nextUrl: {
+ pathname: '/embed/metrics/123'
+ }
+ } as any;
+ expect(isEmbedPage(request)).toBe(true);
+ });
+
+ it('should return true for embed dashboard routes', () => {
+ const request = {
+ nextUrl: {
+ pathname: '/embed/dashboards/456'
+ }
+ } as any;
+ expect(isEmbedPage(request)).toBe(true);
+ });
+
+ it('should return false for non-embed routes', () => {
+ const request = {
+ nextUrl: {
+ pathname: '/metrics/123'
+ }
+ } as any;
+ expect(isEmbedPage(request)).toBe(false);
+ });
+});