Files
sofa/apps/native/src/components/settings/settings-section.tsx
T
jake 1f8b3d8f37 feat: redesign dashboard stats cards with sparklines, icons, and period selection
- Replace horizontal scrolling stats list on native with a 2×2 grid layout; each card gets a color-coded icon, tint background, and an inline sparkline
- Add `Sparkline` component to `apps/native/src/components/ui/` (path-based SVG rendered via `react-native-svg`)
- Upgrade `StatsCard` (native) to support `icon`, `color`/`tintColor`/`bgColor` theming, `sparklineData`, and a `zeego` dropdown menu for switching the time period (today / this week / this month / this year)
- Wire `moviePeriod` / `episodePeriod` state in the home screen and fetch `dashboard.watchHistory` for each so counts and sparklines react to the selected period
- Rewrite `apps/web/src/components/dashboard/sparkline.tsx` as a self-contained SVG sparkline (no Recharts), and update `stats-display.tsx` accordingly
- Remove `apps/web/src/components/ui/chart.tsx` (Recharts wrapper) and drop the `recharts` dependency from `apps/web/package.json`
2026-03-19 11:42:30 -04:00

73 lines
1.9 KiB
TypeScript

import type { Icon } from "@tabler/icons-react-native";
import { Children, Fragment, isValidElement, type ReactNode } from "react";
import { StyleSheet, View } from "react-native";
import { SectionHeader } from "@/components/ui/section-header";
import { Text } from "@/components/ui/text";
function flattenChildren(node: ReactNode): ReactNode[] {
const result: ReactNode[] = [];
Children.forEach(node, (child) => {
if (isValidElement(child) && child.type === Fragment) {
result.push(...flattenChildren((child.props as { children?: ReactNode }).children));
} else if (child != null && child !== false) {
result.push(child);
}
});
return result;
}
export function SettingsSection({
title,
icon,
badge,
children,
}: {
title: string;
icon?: Icon;
badge?: string;
children: ReactNode;
}) {
const items = flattenChildren(children);
return (
<View className="mb-6">
<View className="flex-row items-center gap-2">
<SectionHeader title={title} icon={icon} />
{badge ? (
<View className="bg-primary/10 mb-2.5 rounded-full px-2 py-0.5">
<Text
maxFontSizeMultiplier={1.0}
className="text-primary font-sans text-xs font-medium"
>
{badge}
</Text>
</View>
) : null}
</View>
<View
className="bg-card rounded-xl border border-white/[0.06] px-3"
style={{ borderCurve: "continuous" }}
>
{items.map((child, i) => {
const key =
isValidElement(child) && child.key != null ? String(child.key) : `settings-item-${i}`;
return (
<Fragment key={key}>
{i > 0 && <View className="bg-border" style={styles.separator} />}
{child}
</Fragment>
);
})}
</View>
</View>
);
}
const styles = StyleSheet.create({
separator: {
height: StyleSheet.hairlineWidth,
},
});