fix PieChart rendering issues by giving default height.
This commit is contained in:
@@ -13,7 +13,7 @@ ChartJS.defaults.font.family = 'Inter';
|
|||||||
|
|
||||||
export interface ChartProps extends BoxProps {
|
export interface ChartProps extends BoxProps {
|
||||||
type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter';
|
type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter';
|
||||||
chartData?: ChartData & { focusLabel?: string };
|
chartData?: ChartData<any, any, unknown> & { focusLabel?: string };
|
||||||
chartOptions?: ChartOptions;
|
chartOptions?: ChartOptions;
|
||||||
updateMode?: UpdateMode;
|
updateMode?: UpdateMode;
|
||||||
animationDuration?: number;
|
animationDuration?: number;
|
||||||
@@ -66,8 +66,6 @@ export function Chart({
|
|||||||
|
|
||||||
const handleLegendClick = (item: LegendItem) => {
|
const handleLegendClick = (item: LegendItem) => {
|
||||||
if (onLegendClick && type === 'bar') {
|
if (onLegendClick && type === 'bar') {
|
||||||
// Controlled mode: caller owns the hidden state. We report the click
|
|
||||||
// and let the parent push a new hiddenLabels set on the next render.
|
|
||||||
const { datasetIndex } = item;
|
const { datasetIndex } = item;
|
||||||
const ds = chart.current.data.datasets[datasetIndex];
|
const ds = chart.current.data.datasets[datasetIndex];
|
||||||
onLegendClick(ds.label, !hiddenLabels?.has(ds.label));
|
onLegendClick(ds.label, !hiddenLabels?.has(ds.label));
|
||||||
@@ -124,16 +122,11 @@ export function Chart({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-apply caller-driven hidden flags after focusLabel handling so a
|
|
||||||
// dataset stays hidden across data changes (e.g. date-range switches)
|
|
||||||
// even though Chart.js regenerates dataset meta on every replace.
|
|
||||||
if (hiddenLabels) {
|
if (hiddenLabels) {
|
||||||
chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => {
|
chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => {
|
||||||
if (hiddenLabels.has(ds.label)) {
|
if (hiddenLabels.has(ds.label)) {
|
||||||
ds.hidden = true;
|
ds.hidden = true;
|
||||||
} else if (!chartData.focusLabel) {
|
} else if (!chartData.focusLabel) {
|
||||||
// Explicitly reset so un-hiding a label is always reflected,
|
|
||||||
// regardless of whether the focusLabel pass ran above.
|
|
||||||
ds.hidden = false;
|
ds.hidden = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface PieChartProps extends ChartProps {
|
|||||||
type?: 'doughnut' | 'pie';
|
type?: 'doughnut' | 'pie';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PieChart({ type = 'pie', ...props }: PieChartProps) {
|
export function PieChart({ type = 'pie', height = '300px', ...props }: PieChartProps) {
|
||||||
const [tooltip, setTooltip] = useState(null);
|
const [tooltip, setTooltip] = useState(null);
|
||||||
|
|
||||||
const handleTooltip = ({ tooltip }) => {
|
const handleTooltip = ({ tooltip }) => {
|
||||||
@@ -24,7 +24,7 @@ export function PieChart({ type = 'pie', ...props }: PieChartProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Chart {...props} type={type} onTooltip={handleTooltip} />
|
<Chart {...props} type={type} height={height} onTooltip={handleTooltip} />
|
||||||
{tooltip && <ChartTooltip {...tooltip} />}
|
{tooltip && <ChartTooltip {...tooltip} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,16 +58,11 @@ export function DataGrid({
|
|||||||
const showPager = allowPaging && data && data.count > 0;
|
const showPager = allowPaging && data && data.count > 0;
|
||||||
const { isMobile } = useMobile();
|
const { isMobile } = useMobile();
|
||||||
const [userDisplayMode, setUserDisplayMode] = useState<DisplayMode | null>(() => {
|
const [userDisplayMode, setUserDisplayMode] = useState<DisplayMode | null>(() => {
|
||||||
// localStorage can hold anything (extensions, manual edits, schema drift),
|
|
||||||
// so accept only the two values we know how to render and otherwise fall
|
|
||||||
// back to the useMobile-driven default.
|
|
||||||
const stored = getItem(DISPLAY_MODE_STORAGE_KEY);
|
const stored = getItem(DISPLAY_MODE_STORAGE_KEY);
|
||||||
return stored === 'table' || stored === 'cards' ? stored : null;
|
return stored === 'table' || stored === 'cards' ? stored : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Effective mode: explicit user choice wins, otherwise fall back to the
|
const displayMode: DisplayMode | undefined = isMobile ? 'cards' : userDisplayMode ?? undefined;
|
||||||
// mobile-driven default (cards on small viewports, table elsewhere).
|
|
||||||
const displayMode: DisplayMode | undefined = userDisplayMode ?? (isMobile ? 'cards' : undefined);
|
|
||||||
|
|
||||||
const handleToggleDisplayMode = () => {
|
const handleToggleDisplayMode = () => {
|
||||||
const next: DisplayMode = displayMode === 'cards' ? 'table' : 'cards';
|
const next: DisplayMode = displayMode === 'cards' ? 'table' : 'cards';
|
||||||
@@ -116,7 +111,7 @@ export function DataGrid({
|
|||||||
)}
|
)}
|
||||||
<Row alignItems="center" gap style={{ marginLeft: 'auto' }}>
|
<Row alignItems="center" gap style={{ marginLeft: 'auto' }}>
|
||||||
{renderActions?.()}
|
{renderActions?.()}
|
||||||
{viewToggleButton}
|
{!isMobile && viewToggleButton}
|
||||||
</Row>
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
<LoadingPanel
|
<LoadingPanel
|
||||||
|
|||||||
@@ -62,13 +62,6 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps)
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Each label has a preferred palette slot derived from a hash of the
|
|
||||||
// label, so the same event tends to get the same color across reloads
|
|
||||||
// and date-range changes. We walk labels in hash order and, when two
|
|
||||||
// labels prefer the same slot, the later one steps to the next free
|
|
||||||
// slot, so the visible set of <=12 events all get distinct colors.
|
|
||||||
// The right shift on hex6 sidesteps the FNV-1a low-bit bias mod 12
|
|
||||||
// (the FNV prime is close to 2^24).
|
|
||||||
const colorByKey: Record<string, string> = {};
|
const colorByKey: Record<string, string> = {};
|
||||||
const used = new Set<string>();
|
const used = new Set<string>();
|
||||||
const hashOf = Object.fromEntries(
|
const hashOf = Object.fromEntries(
|
||||||
|
|||||||
@@ -16,11 +16,6 @@ export const IP_ADDRESS_HEADERS = [
|
|||||||
'x-forwarded',
|
'x-forwarded',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize IP strings to a canonical form:
|
|
||||||
* - strips IPv4-mapped IPv6 (e.g. ::ffff:192.0.2.1 -> 192.0.2.1)
|
|
||||||
* - keeps valid IPv4/IPv6 as-is (canonically formatted by ipaddr.js)
|
|
||||||
*/
|
|
||||||
function normalizeIp(ip?: string | null) {
|
function normalizeIp(ip?: string | null) {
|
||||||
if (!ip) return ip;
|
if (!ip) return ip;
|
||||||
|
|
||||||
|
|||||||
@@ -130,9 +130,6 @@ export async function deleteUser(userId: string) {
|
|||||||
|
|
||||||
const teamIds = teams.map(a => a.id);
|
const teamIds = teams.map(a => a.id);
|
||||||
|
|
||||||
// Cloud mode keeps owned teams (and their team-owned content), so cleanup
|
|
||||||
// only covers user-direct rows. Non-cloud hard-deletes owned teams below,
|
|
||||||
// so we must also clean up team-owned content.
|
|
||||||
const ownedFilter = cloudMode
|
const ownedFilter = cloudMode
|
||||||
? { userId }
|
? { userId }
|
||||||
: { OR: [{ userId }, { teamId: { in: teamIds } }] };
|
: { OR: [{ userId }, { teamId: { in: teamIds } }] };
|
||||||
|
|||||||
Reference in New Issue
Block a user