Skip to content
Open
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
2 changes: 2 additions & 0 deletions frontend/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!-- LOVABLE:BEGIN -->

> [!IMPORTANT]
> This project is connected to [Lovable](https://lovable.dev). Avoid rewriting
> published git history — force pushing, or rebasing/amending/squashing commits
Expand All @@ -7,4 +8,5 @@
>
> Commits you push to the connected branch sync back to Lovable and show up in
> the editor, so keep the branch in a working state.
<!-- LOVABLE:END -->
12 changes: 4 additions & 8 deletions frontend/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ const groups: Group[] = [
},
{
label: "Account",
items: [
{ label: "Settings", to: "/settings", icon: <Settings size={16} /> },
],
items: [{ label: "Settings", to: "/settings", icon: <Settings size={16} /> }],
},
];

Expand Down Expand Up @@ -147,10 +145,7 @@ function SidebarGroup({ group, onNav }: { group: Group; onNav: () => void }) {
className="flex w-full items-center justify-between px-2 py-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground"
>
{group.label}
<ChevronRight
size={12}
className={cn("transition-transform", open && "rotate-90")}
/>
<ChevronRight size={12} className={cn("transition-transform", open && "rotate-90")} />
</button>
<AnimatePresence initial={false}>
{open && (
Expand All @@ -161,7 +156,8 @@ function SidebarGroup({ group, onNav }: { group: Group; onNav: () => void }) {
className="overflow-hidden"
>
{group.items.map((item) => {
const active = pathname === item.to || pathname.startsWith(item.to.split("?")[0] + "/");
const active =
pathname === item.to || pathname.startsWith(item.to.split("?")[0] + "/");
return (
<li key={item.label}>
<Link
Expand Down
56 changes: 44 additions & 12 deletions frontend/src/components/shared/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ export const Markdown = memo(function Markdown({ content, className }: MarkdownP
if (!content?.trim()) return null;

return (
<div className={cn("max-w-none break-words text-[13px] leading-relaxed text-foreground", className)}>
<div
className={cn(
"max-w-none break-words text-[13px] leading-relaxed text-foreground",
className,
)}
>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[[rehypeSanitize, schema]]}
Expand All @@ -36,8 +41,11 @@ export const Markdown = memo(function Markdown({ content, className }: MarkdownP
/>
),
img: ({ node, ...props }) => (
// eslint-disable-next-line jsx-a11y/alt-text
<img {...props} loading="lazy" className="my-2 max-w-full rounded-md border border-border" />
<img
{...props}
loading="lazy"
className="my-2 max-w-full rounded-md border border-border"
/>
),
h1: ({ node, ...props }) => (
<h1 {...props} className="mt-4 mb-2 text-[19px] font-bold text-foreground first:mt-0" />
Expand All @@ -46,17 +54,30 @@ export const Markdown = memo(function Markdown({ content, className }: MarkdownP
<h2 {...props} className="mt-4 mb-2 text-[17px] font-bold text-foreground first:mt-0" />
),
h3: ({ node, ...props }) => (
<h3 {...props} className="mt-3 mb-1.5 text-[15px] font-semibold text-foreground first:mt-0" />
<h3
{...props}
className="mt-3 mb-1.5 text-[15px] font-semibold text-foreground first:mt-0"
/>
),
h4: ({ node, ...props }) => (
<h4 {...props} className="mt-3 mb-1.5 text-[14px] font-semibold text-foreground first:mt-0" />
<h4
{...props}
className="mt-3 mb-1.5 text-[14px] font-semibold text-foreground first:mt-0"
/>
),
p: ({ node, ...props }) => <p {...props} className="mb-2 last:mb-0" />,
ul: ({ node, ...props }) => <ul {...props} className="mb-2 ml-5 list-disc space-y-1 last:mb-0" />,
ol: ({ node, ...props }) => <ol {...props} className="mb-2 ml-5 list-decimal space-y-1 last:mb-0" />,
ul: ({ node, ...props }) => (
<ul {...props} className="mb-2 ml-5 list-disc space-y-1 last:mb-0" />
),
ol: ({ node, ...props }) => (
<ol {...props} className="mb-2 ml-5 list-decimal space-y-1 last:mb-0" />
),
li: ({ node, ...props }) => <li {...props} className="pl-0.5" />,
blockquote: ({ node, ...props }) => (
<blockquote {...props} className="my-2 border-l-2 border-border pl-3 text-muted-foreground" />
<blockquote
{...props}
className="my-2 border-l-2 border-border pl-3 text-muted-foreground"
/>
),
hr: () => <hr className="my-4 border-border" />,
table: ({ node, ...props }) => (
Expand All @@ -66,14 +87,22 @@ export const Markdown = memo(function Markdown({ content, className }: MarkdownP
),
thead: ({ node, ...props }) => <thead {...props} className="bg-muted" />,
th: ({ node, ...props }) => (
<th {...props} className="border-b border-border px-3 py-1.5 font-semibold text-foreground" />
<th
{...props}
className="border-b border-border px-3 py-1.5 font-semibold text-foreground"
/>
),
td: ({ node, ...props }) => (
<td {...props} className="border-b border-border px-3 py-1.5 align-top" />
),
td: ({ node, ...props }) => <td {...props} className="border-b border-border px-3 py-1.5 align-top" />,
code: ({ node, className: codeClassName, children, ...props }) => {
const isBlock = /language-/.test(codeClassName ?? "");
if (!isBlock) {
return (
<code {...props} className="rounded bg-muted px-1 py-0.5 font-mono text-[12px] text-foreground">
<code
{...props}
className="rounded bg-muted px-1 py-0.5 font-mono text-[12px] text-foreground"
>
{children}
</code>
);
Expand All @@ -85,7 +114,10 @@ export const Markdown = memo(function Markdown({ content, className }: MarkdownP
);
},
pre: ({ node, ...props }) => (
<pre {...props} className="my-2 overflow-x-auto rounded-md border border border-border bg-muted p-3 text-[12px]" />
<pre
{...props}
className="my-2 overflow-x-auto rounded-md border border border-border bg-muted p-3 text-[12px]"
/>
),
}}
>
Expand Down
73 changes: 60 additions & 13 deletions frontend/src/components/shared/primitives.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cn } from "@/lib/utils";
import { Link } from "@tanstack/react-router";
import type { ReactNode } from "react";
import { FolderKanban, BellOff, MessageSquareDashed, UserPlus, Search } from "lucide-react";

export function SectionHeader({
title,
Expand All @@ -18,16 +19,11 @@ export function SectionHeader({
<h3 className="text-[14px] font-semibold text-foreground">{title}</h3>
{action &&
(actionTo ? (
<Link
to={actionTo}
className="text-[12px] font-medium text-primary hover:underline"
>
<Link to={actionTo} className="text-[12px] font-medium text-primary hover:underline">
{action}
</Link>
) : (
<button className="text-[12px] font-medium text-primary hover:underline">
{action}
</button>
<button className="text-[12px] font-medium text-primary hover:underline">{action}</button>
))}
</div>
);
Expand Down Expand Up @@ -61,23 +57,74 @@ export function EmptyState({
title,
desc,
action,
variant = "default",
className,
}: {
title: string;
desc?: string;
action?: ReactNode;
variant?: "projects" | "notifications" | "messages" | "connections" | "search" | "default";
className?: string;
}) {
const renderIllustration = () => {
switch (variant) {
case "projects":
return (
<div className="group relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-tr from-cyan-500/10 to-blue-500/10 border border-cyan-500/20 text-primary shadow-soft transition-all duration-300 hover:scale-105 hover:shadow-card">
<FolderKanban className="h-10 w-10 transition-transform duration-300 group-hover:scale-110 group-hover:rotate-3" />
<div className="absolute -inset-4 rounded-full bg-primary opacity-5 blur-xl pointer-events-none transition-all duration-300 group-hover:opacity-10 group-hover:scale-110" />
</div>
);
case "notifications":
return (
<div className="group relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-tr from-amber-500/10 to-orange-500/10 border border-amber-500/20 text-warning shadow-soft transition-all duration-300 hover:scale-105 hover:shadow-card">
<BellOff className="h-10 w-10 transition-transform duration-300 group-hover:scale-110 group-hover:rotate-6" />
<div className="absolute -inset-4 rounded-full bg-warning opacity-5 blur-xl pointer-events-none transition-all duration-300 group-hover:opacity-10 group-hover:scale-110" />
</div>
);
case "messages":
return (
<div className="group relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-tr from-indigo-500/10 to-purple-500/10 border border-indigo-500/20 text-indigo-500 shadow-soft transition-all duration-300 hover:scale-105 hover:shadow-card">
<MessageSquareDashed className="h-10 w-10 transition-transform duration-300 group-hover:scale-110 group-hover:rotate-[-6deg]" />
<div className="absolute -inset-4 rounded-full bg-indigo-500 opacity-5 blur-xl pointer-events-none transition-all duration-300 group-hover:opacity-10 group-hover:scale-110" />
</div>
);
case "connections":
return (
<div className="group relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-tr from-emerald-500/10 to-teal-500/10 border border-emerald-500/20 text-success shadow-soft transition-all duration-300 hover:scale-105 hover:shadow-card">
<UserPlus className="h-10 w-10 transition-transform duration-300 group-hover:scale-110 group-hover:-translate-y-0.5" />
<div className="absolute -inset-4 rounded-full bg-success opacity-5 blur-xl pointer-events-none transition-all duration-300 group-hover:opacity-10 group-hover:scale-110" />
</div>
);
case "search":
return (
<div className="group relative flex h-20 w-20 items-center justify-center rounded-2xl bg-gradient-to-tr from-rose-500/10 to-pink-500/10 border border-rose-500/20 text-rose-500 shadow-soft transition-all duration-300 hover:scale-105 hover:shadow-card">
<Search className="h-10 w-10 transition-transform duration-300 group-hover:scale-110 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
<div className="absolute -inset-4 rounded-full bg-rose-500 opacity-5 blur-xl pointer-events-none transition-all duration-300 group-hover:opacity-10 group-hover:scale-110" />
</div>
);
default:
return (
<div className="group relative flex h-12 w-12 items-center justify-center rounded-full bg-muted text-muted-foreground transition-all duration-300 hover:scale-105">
<span className="text-xl transition-transform duration-300 group-hover:scale-110">✨</span>
</div>
);
}
};

return (
<div className="flex flex-col items-center justify-center py-12 text-center">
<div className="mb-3 grid h-12 w-12 place-items-center rounded-full bg-muted text-muted-foreground">
<div className={cn("flex flex-col items-center justify-center py-16 px-4 text-center", className)}>
<div className="mb-4">
{renderIllustration()}
</div>
<p className="text-[14px] font-semibold text-foreground">{title}</p>
{desc && <p className="mt-1 max-w-xs text-[13px] text-muted-foreground">{desc}</p>}
{action && <div className="mt-4">{action}</div>}
<h3 className="text-[16px] font-bold text-foreground tracking-tight">{title}</h3>
{desc && <p className="mt-1.5 max-w-sm text-[13px] text-muted-foreground leading-relaxed">{desc}</p>}
{action && <div className="mt-5">{action}</div>}
</div>
);
}


export function TagChip({ children, className }: { children: ReactNode; className?: string }) {
return (
<span
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/features/dashboard/GreetingHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ function MiniStat({
<p className="truncate text-[11px] font-medium text-muted-foreground">{label}</p>
<p className="text-[15px] font-bold text-foreground">
<span className={valueTint}>{value}</span>
{suffix && <span className="text-[12px] font-medium text-muted-foreground">{suffix}</span>}
{suffix && (
<span className="text-[12px] font-medium text-muted-foreground">{suffix}</span>
)}
</p>
{progress !== undefined && (
<div className="mt-1 h-1 w-full overflow-hidden rounded-full bg-muted">
Expand Down
75 changes: 61 additions & 14 deletions frontend/src/features/dashboard/sections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,12 @@ export function InviteRequests() {
<ul className="divide-y divide-border">
{data.map((r) => (
<li key={r.id} className="flex items-center gap-3 px-4 py-3">
<span className={cn("grid h-10 w-10 shrink-0 place-items-center rounded-md text-lg", r.color)}>
<span
className={cn(
"grid h-10 w-10 shrink-0 place-items-center rounded-md text-lg",
r.color,
)}
>
{r.icon}
</span>
<div className="min-w-0 flex-1">
Expand Down Expand Up @@ -170,9 +175,7 @@ export function SuggestedBuilders() {
<TagChip key={s}>{s}</TagChip>
))}
</div>
<p className="mt-1.5 text-[11px] text-muted-foreground">
{b.yearsExp} yrs exp
</p>
<p className="mt-1.5 text-[11px] text-muted-foreground">{b.yearsExp} yrs exp</p>
<p className="text-[11px] font-semibold text-success">{b.matchScore}% Match</p>
<div className="mt-2 flex gap-1.5">
<button className="flex-1 rounded-md bg-primary px-2 py-1 text-[11px] font-semibold text-primary-foreground hover:opacity-90">
Expand Down Expand Up @@ -233,7 +236,11 @@ export function AIRecommendations() {
Top Match
</p>
<div className="mt-2 flex items-center gap-3">
<Avatar src="https://api.dicebear.com/9.x/notionists-neutral/svg?seed=Rahul" alt="Rahul" size={40} />
<Avatar
src="https://api.dicebear.com/9.x/notionists-neutral/svg?seed=Rahul"
alt="Rahul"
size={40}
/>
<div className="min-w-0 flex-1">
<p className="truncate text-[13px] font-semibold text-foreground">Rahul Verma</p>
<p className="text-[11px] text-muted-foreground">Full Stack Developer</p>
Expand Down Expand Up @@ -298,12 +305,42 @@ export function MessagesPreview() {

export function QuickActions() {
const actions = [
{ icon: FolderPlus, label: "New Project", tint: "bg-info/10 text-info", to: "/projects" as const },
{ icon: Flame, label: "Create Flare", tint: "bg-warning/10 text-warning", to: "/flares" as const },
{ icon: Users2, label: "Find Builder", tint: "bg-success/10 text-success", to: "/builders" as const },
{ icon: Trophy, label: "Start Hackathon", tint: "bg-primary-soft text-primary", to: "/hackathons" as const },
{ icon: FileText, label: "AI Description", tint: "bg-destructive/10 text-destructive", to: "/dashboard" as const },
{ icon: BarChart3, label: "View Analytics", tint: "bg-info/10 text-info", to: "/analytics" as const },
{
icon: FolderPlus,
label: "New Project",
tint: "bg-info/10 text-info",
to: "/projects" as const,
},
{
icon: Flame,
label: "Create Flare",
tint: "bg-warning/10 text-warning",
to: "/flares" as const,
},
{
icon: Users2,
label: "Find Builder",
tint: "bg-success/10 text-success",
to: "/builders" as const,
},
{
icon: Trophy,
label: "Start Hackathon",
tint: "bg-primary-soft text-primary",
to: "/hackathons" as const,
},
{
icon: FileText,
label: "AI Description",
tint: "bg-destructive/10 text-destructive",
to: "/dashboard" as const,
},
{
icon: BarChart3,
label: "View Analytics",
tint: "bg-info/10 text-info",
to: "/analytics" as const,
},
];
return (
<Card>
Expand Down Expand Up @@ -343,7 +380,9 @@ export function UpcomingDeadlines() {
<p className="min-w-0 flex-1 truncate text-[13px] text-foreground">
{d.project} — <span className="text-muted-foreground">{d.milestone}</span>
</p>
<span className={cn("whitespace-nowrap text-[11px] font-semibold", sevTint[d.severity])}>
<span
className={cn("whitespace-nowrap text-[11px] font-semibold", sevTint[d.severity])}
>
Due in {d.dueDays} days
</span>
</li>
Expand All @@ -354,14 +393,22 @@ export function UpcomingDeadlines() {
}

export function NotificationsFeed() {
const { data = [] } = useQuery({ queryKey: ["notifications"], queryFn: notificationsService.list });
const { data = [] } = useQuery({
queryKey: ["notifications"],
queryFn: notificationsService.list,
});
return (
<Card>
<SectionHeader title="Notifications Feed" action="View All" actionTo="/notifications" />
<ul className="divide-y divide-border">
{data.map((n) => (
<li key={n.id} className="flex items-center gap-3 px-4 py-2.5">
<span className={cn("h-2 w-2 shrink-0 rounded-full", n.unread ? "bg-primary" : "bg-transparent")} />
<span
className={cn(
"h-2 w-2 shrink-0 rounded-full",
n.unread ? "bg-primary" : "bg-transparent",
)}
/>
<p className="min-w-0 flex-1 truncate text-[13px] text-foreground">{n.text}</p>
<span className="text-[11px] text-muted-foreground">{n.ago}</span>
</li>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/logo.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import logo from "@/assets/lodo-dev.jpeg";

export const APP_LOGO = logo;
export const APP_LOGO = logo;
Loading
Loading