08: Migrate & redesign Blog, Ads, and Dashboard pages
- Blog listing page with hero, responsive grid, tag filters, load more - Blog post page with markdown rendering, related posts, social share - Ads landing page with conversion copy, pricing, FAQ, testimonials - Dashboard shell with sidebar, topbar, stat cards, activity feed - Dashboard components: Sidebar, TopBar, StatCard, ActivityFeed, QuickActions - Comprehensive test suite covering all pages and components
This commit is contained in:
80
web/src/components/dashboard/ActivityFeed.tsx
Normal file
80
web/src/components/dashboard/ActivityFeed.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { For, Show } from "solid-js";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { Badge } from "~/components/ui";
|
||||
|
||||
interface Activity {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
timestamp: string;
|
||||
type: "alert" | "success" | "info" | "warning";
|
||||
}
|
||||
|
||||
interface ActivityFeedProps {
|
||||
activities: Activity[];
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const typeVariants: Record<Activity["type"], "error" | "success" | "info" | "warning"> = {
|
||||
alert: "error",
|
||||
success: "success",
|
||||
info: "info",
|
||||
warning: "warning",
|
||||
};
|
||||
|
||||
function ActivityIcon(props: { type: Activity["type"] }) {
|
||||
return (
|
||||
<div class={cn(
|
||||
"w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0",
|
||||
props.type === "alert" && "bg-[var(--color-error-bg)] text-[var(--color-error)]",
|
||||
props.type === "success" && "bg-[var(--color-success-bg)] text-[var(--color-success)]",
|
||||
props.type === "info" && "bg-[var(--color-info-bg)] text-[var(--color-info)]",
|
||||
props.type === "warning" && "bg-[var(--color-warning-bg)] text-[var(--color-warning)]",
|
||||
)}>
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<Show when={props.type === "alert"}>
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M8 5v3M8 10.5v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</Show>
|
||||
<Show when={props.type === "success"}>
|
||||
<path d="M4 8l3 3 5-5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</Show>
|
||||
<Show when={props.type === "info"}>
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" stroke-width="1.5"/>
|
||||
<path d="M8 7v4M8 5v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</Show>
|
||||
<Show when={props.type === "warning"}>
|
||||
<path d="M8 2l6 11H2L8 2z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M8 7v3M8 12v.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</Show>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ActivityFeed(props: ActivityFeedProps) {
|
||||
return (
|
||||
<div class={cn("gradient-card border border-[var(--color-border)]/50 rounded-xl", props.class)}>
|
||||
<div class="px-5 py-4 border-b border-[var(--color-border)]/50">
|
||||
<h3 class="text-sm font-semibold text-[var(--color-text-primary)]">Recent Activity</h3>
|
||||
</div>
|
||||
<div class="divide-y divide-[var(--color-border)]/50">
|
||||
<For each={props.activities}>
|
||||
{(activity) => (
|
||||
<div class="px-5 py-3.5 flex items-start gap-3">
|
||||
<ActivityIcon type={activity.type} />
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-0.5">
|
||||
<span class="text-sm font-medium text-[var(--color-text-primary)]">{activity.title}</span>
|
||||
<Badge variant={typeVariants[activity.type]}>{activity.type}</Badge>
|
||||
</div>
|
||||
<p class="text-xs text-[var(--color-text-secondary)]">{activity.description}</p>
|
||||
</div>
|
||||
<span class="text-xs text-[var(--color-text-tertiary)] whitespace-nowrap">{activity.timestamp}</span>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user