Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .agents/react-doctor/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# React Doctor

Run after making React changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs in a React project.

Scans your React codebase for security, performance, correctness, and architecture issues. Outputs a 0-100 score with actionable diagnostics.

## Usage

```bash
npx -y react-doctor@latest . --verbose --diff
```

## Workflow

Run after making changes to catch issues early. Fix errors first, then re-run to verify the score improved.
19 changes: 19 additions & 0 deletions .agents/react-doctor/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: react-doctor
description: Run after making React changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs in a React project.
version: 1.0.0
---

# React Doctor

Scans your React codebase for security, performance, correctness, and architecture issues. Outputs a 0-100 score with actionable diagnostics.

## Usage

```bash
npx -y react-doctor@latest . --verbose --diff
```

## Workflow

Run after making changes to catch issues early. Fix errors first, then re-run to verify the score improved.
9 changes: 1 addition & 8 deletions services/platform/app/components/layout/sticky-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,14 @@ interface StickyHeaderProps {
/**
* Wrapper component that provides unified sticky positioning and blur effect
* for page headers with tab navigation.
*
* Uses inline styles for backdrop-filter to ensure Safari compatibility
* (-webkit-backdrop-filter is required for Safari).
*/
export function StickyHeader({ children, className }: StickyHeaderProps) {
return (
<div
className={cn(
'sticky top-0 z-50 bg-background/80 flex-shrink-0',
'sticky top-0 z-50 bg-background/80 flex-shrink-0 backdrop-blur-md',
className,
)}
style={{
WebkitBackdropFilter: 'blur(12px)',
backdropFilter: 'blur(12px)',
}}
>
{children}
</div>
Expand Down
12 changes: 2 additions & 10 deletions services/platform/app/components/theme/theme-color-meta.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useEffect, useState } from 'react';
import { useEffect } from 'react';

import { useTheme } from './theme-provider';

Expand All @@ -11,27 +11,19 @@ const THEME_COLORS = {

export function ThemeColorMeta() {
const { resolvedTheme } = useTheme();
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

useEffect(() => {
if (!mounted) return;

const color =
resolvedTheme === 'dark' ? THEME_COLORS.dark : THEME_COLORS.light;

// Update or create the theme-color meta tag
let metaThemeColor = document.querySelector('meta[name="theme-color"]');
if (!metaThemeColor) {
metaThemeColor = document.createElement('meta');
metaThemeColor.setAttribute('name', 'theme-color');
document.head.appendChild(metaThemeColor);
}
metaThemeColor.setAttribute('content', color);
}, [resolvedTheme, mounted]);
}, [resolvedTheme]);

return null;
}
18 changes: 7 additions & 11 deletions services/platform/app/components/ui/data-display/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import {
ComponentPropsWithoutRef,
forwardRef,
useState,
useEffect,
useState,
} from 'react';

import { cn } from '@/lib/utils/cn';
Expand Down Expand Up @@ -43,24 +43,20 @@ export const Image = forwardRef<HTMLImageElement, ImageProps>(function Image(
},
ref,
) {
const [currentSrc, setCurrentSrc] = useState(src || fallbackSrc);
const [hasError, setHasError] = useState(false);
const [failedSrc, setFailedSrc] = useState<string | null>(null);
const currentSrc = failedSrc === src ? fallbackSrc : src || fallbackSrc;

// Reset state when src changes
useEffect(() => {
setCurrentSrc(src || fallbackSrc);
setHasError(false);
}, [src, fallbackSrc]);
setFailedSrc(null);
}, [src]);

const handleError = () => {
if (!hasError && currentSrc !== fallbackSrc) {
setHasError(true);
setCurrentSrc(fallbackSrc);
}
setFailedSrc(src ?? null);
};

return (
<img
key={src}
ref={ref}
src={currentSrc}
alt={alt}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export interface DataTableFiltersProps {
className?: string;
}

const EMPTY_FILTERS: FilterConfig[] = [];

/**
* Composable filter bar for DataTable.
*
Expand All @@ -100,7 +102,7 @@ export interface DataTableFiltersProps {
*/
export function DataTableFilters({
search,
filters = [],
filters = EMPTY_FILTERS,
dateRange,
isLoading = false,
onClearAll,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ export const ClickableRows: Story = {

export const WithInfiniteScroll: Story = {
render: function InfiniteScrollStory() {
const [data, setData] = useState(manyUsers.slice(0, 10));
const [data, setData] = useState(() => manyUsers.slice(0, 10));
const [isLoading, setIsLoading] = useState(false);

const loadMore = () => {
Expand Down Expand Up @@ -413,7 +413,7 @@ export const WithInfiniteScroll: Story = {

export const WithManualLoadMore: Story = {
render: function ManualLoadMoreStory() {
const [data, setData] = useState(manyUsers.slice(0, 10));
const [data, setData] = useState(() => manyUsers.slice(0, 10));
const [isLoading, setIsLoading] = useState(false);

const loadMore = () => {
Expand Down
Loading
Loading