feat: implement dashboard layout with sidebar navigation, finance page, and supporting UI architecture
This commit is contained in:
@@ -0,0 +1,255 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Search, Filter, Brain, ArrowUpRight, ArrowDownRight, DollarSign, Activity, PieChart, Download, Wallet, Plus } from "lucide-react";
|
||||||
|
import {
|
||||||
|
Bar,
|
||||||
|
BarChart,
|
||||||
|
CartesianGrid,
|
||||||
|
ResponsiveContainer,
|
||||||
|
Tooltip,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
Area,
|
||||||
|
AreaChart,
|
||||||
|
Pie,
|
||||||
|
PieChart as RechartsPieChart,
|
||||||
|
Cell
|
||||||
|
} from "recharts";
|
||||||
|
|
||||||
|
// Mock Data
|
||||||
|
const cashFlowData = [
|
||||||
|
{ month: "Jan", in: 45000, out: 28000 },
|
||||||
|
{ month: "Feb", in: 52000, out: 31000 },
|
||||||
|
{ month: "Mar", in: 48000, out: 29000 },
|
||||||
|
{ month: "Apr", in: 61000, out: 34000 },
|
||||||
|
{ month: "May", in: 59000, out: 42000 },
|
||||||
|
{ month: "Jun", in: 75000, out: 38000 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const expensesData = [
|
||||||
|
{ name: "Payroll", value: 45000, color: "#6C5BB0" },
|
||||||
|
{ name: "Software/SaaS", value: 12000, color: "#54468B" },
|
||||||
|
{ name: "Marketing", value: 18000, color: "#3B3161" },
|
||||||
|
{ name: "Office/Rent", value: 8000, color: "#221C38" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const recentTransactions = [
|
||||||
|
{ id: 1, desc: "Stripe Payout", category: "Revenue", amount: "+$12,450.00", date: "Today", status: "Completed" },
|
||||||
|
{ id: 2, desc: "Amazon Web Services", category: "Software", amount: "-$3,240.50", date: "Yesterday", status: "Completed" },
|
||||||
|
{ id: 3, desc: "Google Ads", category: "Marketing", amount: "-$4,500.00", date: "May 15", status: "Pending" },
|
||||||
|
{ id: 4, desc: "WeWork Remote", category: "Office", amount: "-$1,200.00", date: "May 12", status: "Completed" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function FinancePage() {
|
||||||
|
return (
|
||||||
|
<div className="mx-auto max-w-7xl animate-in fade-in slide-in-from-bottom-4 duration-500 h-full flex flex-col text-foreground font-sans space-y-6">
|
||||||
|
|
||||||
|
{/* Top Header */}
|
||||||
|
<div className="flex items-center justify-between pb-4 border-b border-white/5 mt-4">
|
||||||
|
<h1 className="text-lg font-medium text-muted-foreground">
|
||||||
|
<span className="text-foreground">Management</span> / Finance
|
||||||
|
</h1>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button className="bg-white/5 hover:bg-white/10 text-foreground border border-white/10 px-4 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Download className="h-4 w-4" />
|
||||||
|
STATEMENT
|
||||||
|
</button>
|
||||||
|
<button className="bg-primary hover:bg-primary/90 text-primary-foreground border border-primary/20 px-4 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
NEW INVOICE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* AI Financial Analyst */}
|
||||||
|
<div className="rounded-sm border border-primary/20 bg-primary/5 p-6 flex flex-col md:flex-row items-start md:items-center justify-between gap-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="p-3 bg-primary/10 rounded-sm shrink-0">
|
||||||
|
<Brain className="h-6 w-6 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold text-primary mb-1 flex items-center gap-2">
|
||||||
|
AI Financial Analyst
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-foreground/80 leading-relaxed max-w-3xl">
|
||||||
|
I detected an anomaly: <strong>Software/SaaS expenses</strong> increased by 22% compared to last month. Specifically, AWS costs spiked. AI recommends auditing idle EC2 instances to save approximately $850/mo.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button className="bg-primary/20 text-primary hover:bg-primary/30 border border-primary/30 px-4 py-2 rounded-sm text-xs font-semibold transition-colors shrink-0">
|
||||||
|
Run Cloud Audit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* KPI Row */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
|
||||||
|
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-6">
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4 flex items-center gap-2">
|
||||||
|
<DollarSign className="h-3.5 w-3.5" /> Total Revenue (MTD)
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-end gap-3 mb-1">
|
||||||
|
<span className="text-3xl font-bold text-foreground">$124,500</span>
|
||||||
|
<span className="text-xs text-emerald-500 flex items-center font-medium mb-1">
|
||||||
|
<ArrowUpRight className="h-3 w-3 mr-0.5" /> 8.4%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-6">
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4 flex items-center gap-2">
|
||||||
|
<Activity className="h-3.5 w-3.5" /> Monthly Burn Rate
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-end gap-3 mb-1">
|
||||||
|
<span className="text-3xl font-bold text-foreground">$42,300</span>
|
||||||
|
<span className="text-xs text-red-500 flex items-center font-medium mb-1">
|
||||||
|
<ArrowUpRight className="h-3 w-3 mr-0.5" /> 2.1%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-6">
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4 flex items-center gap-2">
|
||||||
|
<Wallet className="h-3.5 w-3.5" /> Cash Runway
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-end gap-3 mb-1">
|
||||||
|
<span className="text-3xl font-bold text-foreground">14.2</span>
|
||||||
|
<span className="text-xs text-muted-foreground flex items-center font-medium mb-1">
|
||||||
|
Months
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-6">
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-4 flex items-center gap-2">
|
||||||
|
<Brain className="h-3.5 w-3.5 text-primary" /> AI Profitability Score
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-end gap-3 mb-1">
|
||||||
|
<span className="text-3xl font-bold text-primary">94/100</span>
|
||||||
|
<span className="text-xs text-emerald-500 flex items-center font-medium mb-1">
|
||||||
|
Optimal
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Charts & Tables */}
|
||||||
|
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6 flex-1 min-h-[400px]">
|
||||||
|
|
||||||
|
{/* Cash Flow Chart */}
|
||||||
|
<div className="xl:col-span-2 rounded-sm border border-white/5 bg-[#0A0710] p-6 flex flex-col">
|
||||||
|
<div className="flex justify-between items-start mb-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold mb-1">Cash Flow Overview</h3>
|
||||||
|
<p className="text-xs text-muted-foreground">Revenue vs Expenses over the last 6 months.</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-4 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
|
||||||
|
<div className="flex items-center gap-2"><div className="w-2 h-2 rounded-sm bg-emerald-500"></div> Inflow</div>
|
||||||
|
<div className="flex items-center gap-2"><div className="w-2 h-2 rounded-sm bg-red-500"></div> Outflow</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 w-full min-h-[250px]">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<BarChart data={cashFlowData}>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#ffffff05" />
|
||||||
|
<XAxis dataKey="month" axisLine={false} tickLine={false} tick={{ fontSize: 11, fill: '#8F89A5' }} dy={10} />
|
||||||
|
<YAxis axisLine={false} tickLine={false} tick={{ fontSize: 11, fill: '#8F89A5' }} dx={-10} tickFormatter={(val) => `$${val/1000}k`} />
|
||||||
|
<Tooltip
|
||||||
|
cursor={{ fill: '#ffffff05' }}
|
||||||
|
contentStyle={{ backgroundColor: "#1F172B", border: "none", fontSize: "12px", color: "#FBF9FE", borderRadius: "4px" }}
|
||||||
|
itemStyle={{ color: "#FBF9FE" }}
|
||||||
|
/>
|
||||||
|
<Bar dataKey="in" fill="#10b981" radius={[2, 2, 0, 0]} barSize={16} />
|
||||||
|
<Bar dataKey="out" fill="#ef4444" radius={[2, 2, 0, 0]} barSize={16} />
|
||||||
|
</BarChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Expenses Pie & Recent Transactions */}
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-6 flex-1 flex flex-col">
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2">Expenses Breakdown</h3>
|
||||||
|
<div className="flex-1 w-full flex items-center justify-center relative min-h-[160px]">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<RechartsPieChart>
|
||||||
|
<Pie
|
||||||
|
data={expensesData}
|
||||||
|
cx="50%"
|
||||||
|
cy="50%"
|
||||||
|
innerRadius={50}
|
||||||
|
outerRadius={70}
|
||||||
|
paddingAngle={2}
|
||||||
|
dataKey="value"
|
||||||
|
stroke="none"
|
||||||
|
>
|
||||||
|
{expensesData.map((entry, index) => (
|
||||||
|
<Cell key={`cell-${index}`} fill={entry.color} />
|
||||||
|
))}
|
||||||
|
</Pie>
|
||||||
|
<Tooltip contentStyle={{ backgroundColor: "#1F172B", border: "none", fontSize: "12px" }} />
|
||||||
|
</RechartsPieChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center pointer-events-none flex-col">
|
||||||
|
<span className="text-[10px] text-muted-foreground uppercase font-bold tracking-widest">Total</span>
|
||||||
|
<span className="text-sm font-bold">$83k</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 mt-4">
|
||||||
|
{expensesData.map((item, idx) => (
|
||||||
|
<div key={idx} className="flex justify-between items-center text-xs">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-sm" style={{ backgroundColor: item.color }}></div>
|
||||||
|
<span className="text-muted-foreground">{item.name}</span>
|
||||||
|
</div>
|
||||||
|
<span className="font-semibold">${(item.value / 1000).toFixed(1)}k</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* AI Categorized Transactions */}
|
||||||
|
<div className="bg-[#0A0710] rounded-sm border border-white/5 overflow-hidden">
|
||||||
|
<div className="p-4 border-b border-white/5 flex justify-between items-center">
|
||||||
|
<h3 className="text-sm font-semibold">Recent Transactions</h3>
|
||||||
|
<span className="text-[10px] font-bold uppercase tracking-wider text-primary flex items-center gap-1"><Brain className="h-3 w-3" /> Auto-Categorized by AI</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-12 gap-4 p-4 border-b border-white/5 bg-[#0F0B15]/50 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
|
||||||
|
<div className="col-span-5">Description</div>
|
||||||
|
<div className="col-span-2">Category</div>
|
||||||
|
<div className="col-span-2">Date</div>
|
||||||
|
<div className="col-span-2">Amount</div>
|
||||||
|
<div className="col-span-1 text-right">Status</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-white/5">
|
||||||
|
{recentTransactions.map((tx) => (
|
||||||
|
<div key={tx.id} className="grid grid-cols-12 gap-4 p-4 items-center hover:bg-white/[0.02] transition-colors">
|
||||||
|
<div className="col-span-5 text-sm font-medium">{tx.desc}</div>
|
||||||
|
<div className="col-span-2 text-xs text-muted-foreground">{tx.category}</div>
|
||||||
|
<div className="col-span-2 text-xs text-muted-foreground">{tx.date}</div>
|
||||||
|
<div className={`col-span-2 text-sm font-bold ${tx.amount.startsWith('+') ? 'text-emerald-400' : 'text-foreground'}`}>
|
||||||
|
{tx.amount}
|
||||||
|
</div>
|
||||||
|
<div className="col-span-1 text-right">
|
||||||
|
<span className={`text-[10px] uppercase font-bold px-2 py-1 rounded-sm border inline-block ${
|
||||||
|
tx.status === 'Completed' ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' :
|
||||||
|
'bg-orange-500/10 text-orange-400 border-orange-500/20'
|
||||||
|
}`}>
|
||||||
|
{tx.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,17 +1,165 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Plus, Brain, Target, Compass, Award, MoreHorizontal, CircleDashed, CheckCircle2 } from "lucide-react";
|
||||||
|
|
||||||
|
// Mock Data
|
||||||
|
const goals = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Launch SaaS Product MVP",
|
||||||
|
category: "Career & Business",
|
||||||
|
progress: 75,
|
||||||
|
dueDate: "Q3 2026",
|
||||||
|
status: "On Track",
|
||||||
|
color: "#6C5BB0", // Primary
|
||||||
|
milestones: [
|
||||||
|
{ name: "Finalize Designs", done: true },
|
||||||
|
{ name: "Backend Architecture", done: true },
|
||||||
|
{ name: "Frontend Development", done: true },
|
||||||
|
{ name: "Beta Testing", done: false },
|
||||||
|
{ name: "Public Launch", done: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Run a Half-Marathon",
|
||||||
|
category: "Health & Fitness",
|
||||||
|
progress: 40,
|
||||||
|
dueDate: "Oct 12, 2026",
|
||||||
|
status: "Behind",
|
||||||
|
color: "#10b981", // Emerald
|
||||||
|
milestones: [
|
||||||
|
{ name: "Run 5K Without Stopping", done: true },
|
||||||
|
{ name: "Run 10K", done: true },
|
||||||
|
{ name: "Maintain 15K weekly volume", done: false },
|
||||||
|
{ name: "Complete 18K long run", done: false },
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Read 24 Books",
|
||||||
|
category: "Personal Growth",
|
||||||
|
progress: 50,
|
||||||
|
dueDate: "Dec 31, 2026",
|
||||||
|
status: "Ahead",
|
||||||
|
color: "#f97316", // Orange
|
||||||
|
milestones: [
|
||||||
|
{ name: "Read 6 Books (Q1)", done: true },
|
||||||
|
{ name: "Read 12 Books (Q2)", done: true },
|
||||||
|
{ name: "Read 18 Books (Q3)", done: false },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default function GoalsPage() {
|
export default function GoalsPage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="mx-auto max-w-7xl animate-in fade-in slide-in-from-bottom-4 duration-500 h-full flex flex-col text-foreground font-sans space-y-6">
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
{/* Top Header */}
|
||||||
<h1 className="text-3xl font-bold tracking-tight text-foreground">Hedefler</h1>
|
<div className="flex items-center justify-between pb-4 border-b border-white/5 mt-4">
|
||||||
<p className="text-muted-foreground mt-2">
|
<h1 className="text-lg font-medium text-muted-foreground">
|
||||||
Kısa ve uzun vadeli hedeflerinizi belirleyin ve ilerlemenizi izleyin.
|
<span className="text-foreground">Personal</span> / Strategic Goals
|
||||||
</p>
|
</h1>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button className="bg-primary hover:bg-primary/90 text-primary-foreground border border-primary/20 px-4 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
NEW GOAL
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-2xl border border-white/5 bg-white/5 p-8 text-center text-muted-foreground backdrop-blur-sm">
|
|
||||||
Hedef yönetim modülü yakında eklenecek.
|
{/* AI Goal Strategist */}
|
||||||
|
<div className="rounded-sm border border-primary/20 bg-primary/5 p-6 flex flex-col md:flex-row items-center justify-between gap-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="p-3 bg-primary/10 rounded-sm shrink-0">
|
||||||
|
<Brain className="h-6 w-6 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold text-primary mb-1 flex items-center gap-2">
|
||||||
|
AI Goal Strategist
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-foreground/80 leading-relaxed max-w-3xl">
|
||||||
|
You are falling behind on your <strong>Half-Marathon</strong> goal. Your running habit completion has dropped to 30% this month. AI suggests breaking the next milestone ("Maintain 15K weekly volume") into smaller, manageable 3K chunks to rebuild momentum.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button className="bg-primary hover:bg-primary/90 text-primary-foreground px-4 py-2 rounded-sm text-xs font-semibold transition-colors shrink-0 shadow-lg shadow-primary/20">
|
||||||
|
Refactor Milestones
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Goals List */}
|
||||||
|
<div className="space-y-6 pb-6">
|
||||||
|
{goals.map((goal) => (
|
||||||
|
<div key={goal.id} className="rounded-sm border border-white/5 bg-[#0A0710] p-6 flex flex-col lg:flex-row gap-8 relative overflow-hidden group">
|
||||||
|
|
||||||
|
{/* Left Side: Overview & Progress */}
|
||||||
|
<div className="flex-1 flex flex-col justify-between">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<h2 className="text-xl font-bold text-foreground">{goal.title}</h2>
|
||||||
|
<span className={`text-[10px] uppercase font-bold px-2 py-0.5 rounded-sm border ${
|
||||||
|
goal.status === 'Ahead' ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' :
|
||||||
|
goal.status === 'Behind' ? 'bg-orange-500/10 text-orange-400 border-orange-500/20' :
|
||||||
|
'bg-blue-500/10 text-blue-400 border-blue-500/20'
|
||||||
|
}`}>
|
||||||
|
{goal.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 text-xs text-muted-foreground mb-6">
|
||||||
|
<span className="flex items-center gap-1.5"><Compass className="h-3.5 w-3.5" /> {goal.category}</span>
|
||||||
|
<span className="flex items-center gap-1.5"><Target className="h-3.5 w-3.5" /> Due: {goal.dueDate}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Big Progress Bar */}
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between items-end mb-2">
|
||||||
|
<span className="text-sm font-semibold">Overall Progress</span>
|
||||||
|
<span className="text-2xl font-bold" style={{ color: goal.color }}>{goal.progress}%</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-3 w-full bg-white/5 rounded-full overflow-hidden">
|
||||||
|
<div
|
||||||
|
className="h-full rounded-full transition-all duration-1000 relative"
|
||||||
|
style={{ width: `${goal.progress}%`, backgroundColor: goal.color }}
|
||||||
|
>
|
||||||
|
{/* Inner highlight for 3D effect */}
|
||||||
|
<div className="absolute top-0 left-0 right-0 h-1/2 bg-white/20"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Side: Milestones */}
|
||||||
|
<div className="w-full lg:w-96 rounded-sm bg-[#150F1D] border border-white/5 p-4 flex flex-col">
|
||||||
|
<div className="flex justify-between items-center mb-4">
|
||||||
|
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Milestones</h3>
|
||||||
|
<button className="text-muted-foreground hover:text-foreground transition-colors"><MoreHorizontal className="h-4 w-4" /></button>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-3 flex-1 overflow-y-auto tiny-scrollbar">
|
||||||
|
{goal.milestones.map((ms, idx) => (
|
||||||
|
<div key={idx} className="flex items-start gap-3">
|
||||||
|
{ms.done ? (
|
||||||
|
<CheckCircle2 className="h-4 w-4 shrink-0 mt-0.5" style={{ color: goal.color }} />
|
||||||
|
) : (
|
||||||
|
<CircleDashed className="h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
|
||||||
|
)}
|
||||||
|
<span className={`text-sm ${ms.done ? 'text-muted-foreground line-through' : 'text-foreground font-medium'}`}>
|
||||||
|
{ms.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="mt-4 w-full border border-dashed border-white/10 hover:border-white/30 text-muted-foreground hover:text-foreground py-2 rounded-sm text-xs font-semibold transition-colors flex items-center justify-center gap-2">
|
||||||
|
<Plus className="h-3 w-3" /> Add Milestone
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,188 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Plus, Filter, Brain, Check, X, Flame, Target, Trophy, TrendingUp } from "lucide-react";
|
||||||
|
import { Area, AreaChart, ResponsiveContainer, XAxis, Tooltip } from "recharts";
|
||||||
|
|
||||||
|
// Mock Data
|
||||||
|
const habits = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "Morning Meditation",
|
||||||
|
category: "Mindfulness",
|
||||||
|
streak: 12,
|
||||||
|
goal: "Daily",
|
||||||
|
color: "#6C5BB0", // Primary
|
||||||
|
status: [true, true, true, false, true, true, false],
|
||||||
|
trend: [40, 45, 30, 60, 50, 70, 85]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Read 20 Pages",
|
||||||
|
category: "Learning",
|
||||||
|
streak: 4,
|
||||||
|
goal: "Daily",
|
||||||
|
color: "#10b981", // Emerald
|
||||||
|
status: [false, true, true, true, true, false, false],
|
||||||
|
trend: [20, 40, 50, 40, 60, 80, 75]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "Gym Workout",
|
||||||
|
category: "Health",
|
||||||
|
streak: 2,
|
||||||
|
goal: "4x Week",
|
||||||
|
color: "#f97316", // Orange
|
||||||
|
status: [true, false, true, false, true, false, false],
|
||||||
|
trend: [50, 55, 45, 65, 60, 75, 90]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "No Sugar",
|
||||||
|
category: "Diet",
|
||||||
|
streak: 0,
|
||||||
|
goal: "Daily",
|
||||||
|
color: "#ef4444", // Red
|
||||||
|
status: [true, true, false, false, false, false, false],
|
||||||
|
trend: [80, 70, 60, 40, 30, 20, 10]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const days = ["M", "T", "W", "T", "F", "S", "S"];
|
||||||
|
|
||||||
export default function HabitsPage() {
|
export default function HabitsPage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="mx-auto max-w-7xl animate-in fade-in slide-in-from-bottom-4 duration-500 h-full flex flex-col text-foreground font-sans space-y-6">
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
{/* Top Header */}
|
||||||
<h1 className="text-3xl font-bold tracking-tight text-foreground">Alışkanlıklar</h1>
|
<div className="flex items-center justify-between pb-4 border-b border-white/5 mt-4">
|
||||||
<p className="text-muted-foreground mt-2">
|
<h1 className="text-lg font-medium text-muted-foreground">
|
||||||
Günlük alışkanlıklarınızı takip edin ve zinciri kırmayın.
|
<span className="text-foreground">Personal</span> / Habits Tracker
|
||||||
</p>
|
</h1>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button className="bg-[#150F1D] hover:bg-white/5 text-foreground border border-white/10 px-3 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Filter className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<button className="bg-primary hover:bg-primary/90 text-primary-foreground border border-primary/20 px-4 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
NEW HABIT
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-2xl border border-white/5 bg-white/5 p-8 text-center text-muted-foreground backdrop-blur-sm">
|
|
||||||
Alışkanlık takip modülü yakında eklenecek.
|
{/* AI Habit Optimizer */}
|
||||||
|
<div className="rounded-sm border border-primary/20 bg-[linear-gradient(90deg,rgba(108,91,176,0.1)_0%,rgba(10,7,16,0)_100%)] p-6 flex flex-col md:flex-row items-center justify-between gap-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="p-3 bg-primary/10 rounded-sm shrink-0">
|
||||||
|
<Brain className="h-6 w-6 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold text-primary mb-1 flex items-center gap-2">
|
||||||
|
AI Habit Optimizer
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-foreground/80 leading-relaxed max-w-3xl">
|
||||||
|
I noticed a pattern: You often break your <strong>"No Sugar"</strong> streak on Thursdays. This correlates with high-stress days marked in your Journal. AI suggests scheduling a stress-relief activity (like a walk) around 3 PM on Thursdays to curb cravings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button className="bg-primary/20 text-primary hover:bg-primary/30 border border-primary/30 px-4 py-2 rounded-sm text-xs font-semibold transition-colors shrink-0">
|
||||||
|
Add Routine Intervention
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* KPI Row */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-5 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[10px] uppercase tracking-wider text-muted-foreground font-bold mb-1">Perfect Days This Month</h4>
|
||||||
|
<div className="text-3xl font-bold text-foreground">8</div>
|
||||||
|
</div>
|
||||||
|
<Trophy className="h-10 w-10 text-emerald-500 opacity-20" />
|
||||||
|
</div>
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-5 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[10px] uppercase tracking-wider text-muted-foreground font-bold mb-1">Longest Active Streak</h4>
|
||||||
|
<div className="text-3xl font-bold text-foreground">12 <span className="text-sm text-muted-foreground font-normal">days</span></div>
|
||||||
|
</div>
|
||||||
|
<Flame className="h-10 w-10 text-orange-500 opacity-20" />
|
||||||
|
</div>
|
||||||
|
<div className="rounded-sm border border-white/5 bg-[#0A0710] p-5 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 className="text-[10px] uppercase tracking-wider text-muted-foreground font-bold mb-1">Overall Completion Rate</h4>
|
||||||
|
<div className="text-3xl font-bold text-foreground">68%</div>
|
||||||
|
</div>
|
||||||
|
<Target className="h-10 w-10 text-blue-500 opacity-20" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Habit Grid */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 pb-6">
|
||||||
|
{habits.map((habit) => (
|
||||||
|
<div key={habit.id} className="rounded-sm border border-white/5 bg-[#0A0710] p-6 flex flex-col group hover:border-white/10 transition-colors relative overflow-hidden">
|
||||||
|
|
||||||
|
{/* Top Info */}
|
||||||
|
<div className="flex justify-between items-start mb-6 z-10">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-bold text-foreground mb-1">{habit.name}</h3>
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<span className="text-[10px] uppercase tracking-wider font-bold px-2 py-0.5 rounded-sm bg-white/5 text-muted-foreground border border-white/5">{habit.category}</span>
|
||||||
|
<span className="text-xs text-muted-foreground flex items-center gap-1">
|
||||||
|
<Target className="h-3 w-3" /> {habit.goal}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-end">
|
||||||
|
<span className="text-xs text-muted-foreground mb-0.5">Current Streak</span>
|
||||||
|
<div className="flex items-center gap-1.5 font-bold" style={{ color: habit.streak > 0 ? habit.color : '#8F89A5' }}>
|
||||||
|
<Flame className="h-4 w-4" /> {habit.streak}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Weekly Checkboxes */}
|
||||||
|
<div className="flex justify-between items-center mb-6 z-10">
|
||||||
|
{days.map((day, idx) => (
|
||||||
|
<div key={idx} className="flex flex-col items-center gap-2">
|
||||||
|
<span className="text-[10px] font-bold text-muted-foreground">{day}</span>
|
||||||
|
<button
|
||||||
|
className="w-8 h-8 rounded-sm flex items-center justify-center transition-all border"
|
||||||
|
style={{
|
||||||
|
backgroundColor: habit.status[idx] ? `${habit.color}20` : '#150F1D',
|
||||||
|
borderColor: habit.status[idx] ? `${habit.color}50` : '#ffffff10',
|
||||||
|
color: habit.status[idx] ? habit.color : '#ffffff20'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{habit.status[idx] ? <Check className="h-4 w-4" /> : <X className="h-3 w-3" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Micro Chart (Background styling) */}
|
||||||
|
<div className="h-16 w-full mt-auto relative z-0 opacity-50 group-hover:opacity-100 transition-opacity">
|
||||||
|
<div className="absolute top-0 left-0 text-[10px] text-muted-foreground uppercase tracking-widest font-bold flex items-center gap-1">
|
||||||
|
<TrendingUp className="h-3 w-3" /> 30-Day Trend
|
||||||
|
</div>
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<AreaChart data={habit.trend.map((val, i) => ({ name: i, value: val }))}>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id={`grad-${habit.id}`} x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="5%" stopColor={habit.color} stopOpacity={0.3}/>
|
||||||
|
<stop offset="95%" stopColor={habit.color} stopOpacity={0}/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<Tooltip
|
||||||
|
cursor={false}
|
||||||
|
contentStyle={{ display: 'none' }}
|
||||||
|
/>
|
||||||
|
<Area type="monotone" dataKey="value" stroke={habit.color} strokeWidth={2} fillOpacity={1} fill={`url(#grad-${habit.id})`} />
|
||||||
|
</AreaChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+134
-228
@@ -1,245 +1,151 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { Plus, Search, Filter, Brain, PenTool, Calendar, AlignLeft, Tag, Lock, Sparkles, Image as ImageIcon } from "lucide-react";
|
||||||
import { useLiveQuery } from "dexie-react-hooks";
|
|
||||||
import { v4 as uuidv4 } from "uuid";
|
|
||||||
|
|
||||||
import { analyzeJournalWithLocalAI } from "@/lib/ai";
|
// Mock Data
|
||||||
import { db } from "@/lib/db";
|
const journalEntries = [
|
||||||
import { Button } from "@/components/ui/button";
|
{
|
||||||
import {
|
id: 1,
|
||||||
Card,
|
title: "Breakthrough in System Design",
|
||||||
CardContent,
|
preview: "Today we finally cracked the database architecture. By moving to a distributed model...",
|
||||||
CardDescription,
|
date: "Today, 10:45 AM",
|
||||||
CardHeader,
|
tags: ["Work", "Idea", "Win"],
|
||||||
CardTitle,
|
sentiment: "Positive"
|
||||||
} from "@/components/ui/card";
|
},
|
||||||
import { Input } from "@/components/ui/input";
|
{
|
||||||
import { Label } from "@/components/ui/label";
|
id: 2,
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
title: "Feeling a bit overwhelmed",
|
||||||
|
preview: "The Q3 deadlines are approaching fast. I need to make sure I'm prioritizing...",
|
||||||
const moods = [
|
date: "Yesterday",
|
||||||
{ id: "happy", label: "Mutlu", emoji: "😊" },
|
tags: ["Mental Health", "Vent"],
|
||||||
{ id: "neutral", label: "Nötr", emoji: "😐" },
|
sentiment: "Anxious"
|
||||||
{ id: "sad", label: "Üzgün", emoji: "😔" },
|
},
|
||||||
{ id: "angry", label: "Sinirli", emoji: "😠" },
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Morning Routine Reflection",
|
||||||
|
preview: "Woke up at 6 AM. Did 20 minutes of meditation. It really sets the tone for...",
|
||||||
|
date: "May 06, 2026",
|
||||||
|
tags: ["Habits", "Morning"],
|
||||||
|
sentiment: "Calm"
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function JournalPage() {
|
export default function JournalPage() {
|
||||||
const [content, setContent] = useState("");
|
|
||||||
const [mood, setMood] = useState("happy");
|
|
||||||
const [energy, setEnergy] = useState("3");
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
|
|
||||||
// Dexie.js üzerinden günlükleri tarih sırasına göre çekiyoruz (en yeni en üstte).
|
|
||||||
const journals = useLiveQuery(() =>
|
|
||||||
db.journals.orderBy("date").reverse().toArray(),
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleSubmit = async (event: React.FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
if (!content.trim()) return;
|
|
||||||
|
|
||||||
setIsSubmitting(true);
|
|
||||||
|
|
||||||
const now = new Date().toISOString();
|
|
||||||
const entryId = uuidv4();
|
|
||||||
const currentContent = content;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Veriyi önce kaydet, AI analizini daha sonra arka planda tamamla.
|
|
||||||
await db.journals.add({
|
|
||||||
id: entryId,
|
|
||||||
date: now.split("T")[0],
|
|
||||||
mood,
|
|
||||||
energy: parseInt(energy, 10),
|
|
||||||
content: currentContent,
|
|
||||||
created_at: now,
|
|
||||||
updated_at: now,
|
|
||||||
});
|
|
||||||
|
|
||||||
setContent("");
|
|
||||||
setEnergy("3");
|
|
||||||
setMood("happy");
|
|
||||||
|
|
||||||
try {
|
|
||||||
const aiResult = await analyzeJournalWithLocalAI(currentContent);
|
|
||||||
if (aiResult) {
|
|
||||||
await db.journals.update(entryId, {
|
|
||||||
ai_tags: aiResult.ai_tags,
|
|
||||||
ai_sentiment_score: aiResult.ai_sentiment_score,
|
|
||||||
ai_summary: aiResult.ai_summary,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (aiResult.suggested_tasks?.length) {
|
|
||||||
for (const taskTitle of aiResult.suggested_tasks) {
|
|
||||||
await db.tasks.add({
|
|
||||||
id: uuidv4(),
|
|
||||||
journal_id: entryId,
|
|
||||||
title: `AI Önerisi: ${taskTitle}`,
|
|
||||||
status: "todo",
|
|
||||||
ai_generated: true,
|
|
||||||
date: now.split("T")[0],
|
|
||||||
created_at: now,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (aiError) {
|
|
||||||
console.error("Arka plan AI analizi hatası:", aiError);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Günlük kaydedilemedi:", error);
|
|
||||||
} finally {
|
|
||||||
setIsSubmitting(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = async (id: string) => {
|
|
||||||
if (confirm("Bu günlüğü silmek istediğinden emin misin?")) {
|
|
||||||
await db.journals.delete(id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-4xl animate-in space-y-6 fade-in slide-in-from-bottom-4 duration-500">
|
<div className="mx-auto max-w-7xl animate-in fade-in slide-in-from-bottom-4 duration-500 h-full flex flex-col text-foreground font-sans space-y-6">
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Günlük</h1>
|
{/* Top Header */}
|
||||||
<p className="text-muted-foreground">
|
<div className="flex items-center justify-between pb-4 border-b border-white/5 mt-4">
|
||||||
Zihnini boşalt, hislerini kaydet. Verilerin sadece cihazında kalır.
|
<h1 className="text-lg font-medium text-muted-foreground">
|
||||||
</p>
|
<span className="text-foreground">Personal</span> / Daily Journal
|
||||||
|
</h1>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="bg-[#150F1D] border border-white/5 rounded-sm px-3 py-1.5 flex items-center gap-2">
|
||||||
|
<Search className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search memories..."
|
||||||
|
className="bg-transparent border-none outline-none text-xs w-48 placeholder:text-muted-foreground/50 text-foreground"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className="bg-primary hover:bg-primary/90 text-primary-foreground border border-primary/20 px-4 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
NEW ENTRY
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card>
|
{/* Main Layout */}
|
||||||
<CardHeader>
|
<div className="flex flex-col lg:flex-row gap-6 flex-1 min-h-0">
|
||||||
<CardTitle>Yeni Kayıt</CardTitle>
|
|
||||||
<CardDescription>Bugün nasıl hissediyorsun?</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
|
||||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Ruh Hali</Label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
{moods.map((currentMood) => (
|
|
||||||
<button
|
|
||||||
key={currentMood.id}
|
|
||||||
type="button"
|
|
||||||
onClick={() => setMood(currentMood.id)}
|
|
||||||
className={`flex flex-1 items-center justify-center rounded-md border px-1 py-2 text-xl transition-colors ${
|
|
||||||
mood === currentMood.id
|
|
||||||
? "border-primary bg-primary/20"
|
|
||||||
: "border-border bg-card hover:bg-muted"
|
|
||||||
}`}
|
|
||||||
title={currentMood.label}
|
|
||||||
>
|
|
||||||
{currentMood.emoji}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
{/* Left Panel: Entry List & AI */}
|
||||||
<Label>Enerji Seviyesi (1-5): {energy}</Label>
|
<div className="w-full lg:w-80 flex flex-col gap-6 shrink-0">
|
||||||
<Input
|
|
||||||
type="range"
|
{/* AI Sentiment Analysis */}
|
||||||
min="1"
|
<div className="rounded-sm border border-primary/20 bg-[linear-gradient(135deg,rgba(108,91,176,0.1)_0%,rgba(10,7,16,0)_100%)] p-5">
|
||||||
max="5"
|
<h3 className="text-sm font-semibold text-primary mb-2 flex items-center gap-2">
|
||||||
step="1"
|
<Brain className="h-4 w-4" /> AI Reflection Insight
|
||||||
value={energy}
|
</h3>
|
||||||
onChange={(event) => setEnergy(event.target.value)}
|
<p className="text-[11px] text-foreground/80 leading-relaxed mb-3">
|
||||||
className="w-full"
|
Over the last 14 days, your entries tagged with <strong>"Work"</strong> show a 30% increase in stress vocabulary. However, entries after <strong>Morning Meditation</strong> are consistently highly positive.
|
||||||
/>
|
</p>
|
||||||
</div>
|
<button className="bg-primary/20 text-primary hover:bg-primary/30 border border-primary/30 px-3 py-1.5 rounded-sm text-[10px] font-bold uppercase tracking-wider transition-colors w-full">
|
||||||
|
Generate Weekly Summary
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Entries List */}
|
||||||
|
<div className="flex-1 rounded-sm border border-white/5 bg-[#0A0710] flex flex-col overflow-hidden">
|
||||||
|
<div className="p-4 border-b border-white/5 flex justify-between items-center">
|
||||||
|
<span className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Recent Entries</span>
|
||||||
|
<Filter className="h-4 w-4 text-muted-foreground hover:text-foreground cursor-pointer" />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex-1 overflow-y-auto tiny-scrollbar p-2 space-y-1">
|
||||||
<div className="space-y-2">
|
{journalEntries.map((entry, idx) => (
|
||||||
<Label htmlFor="content">Düşüncelerini buraya dök...</Label>
|
<div key={entry.id} className={`p-3 rounded-sm cursor-pointer transition-colors ${idx === 0 ? 'bg-[#1F172B] border border-primary/30' : 'hover:bg-white/5 border border-transparent'}`}>
|
||||||
<Textarea
|
<h4 className="text-sm font-semibold text-foreground mb-1 truncate">{entry.title}</h4>
|
||||||
id="content"
|
<p className="text-[11px] text-muted-foreground line-clamp-2 leading-relaxed mb-2">
|
||||||
value={content}
|
{entry.preview}
|
||||||
onChange={(event) => setContent(event.target.value)}
|
</p>
|
||||||
className="min-h-[150px] resize-y"
|
<div className="flex justify-between items-center text-[10px]">
|
||||||
placeholder="Örneğin: Bugün toplantıda işler ters gitti..."
|
<span className="text-primary font-medium">{entry.date}</span>
|
||||||
/>
|
<span className="flex gap-1">
|
||||||
</div>
|
{entry.tags.map(tag => (
|
||||||
|
<span key={tag} className="bg-white/10 px-1.5 py-0.5 rounded-sm">{tag}</span>
|
||||||
<Button
|
))}
|
||||||
type="submit"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
className="w-full md:w-auto"
|
|
||||||
>
|
|
||||||
{isSubmitting ? "Kaydediliyor..." : "Kaydet"}
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div className="space-y-4 pt-4">
|
|
||||||
<h2 className="text-xl font-bold">Geçmiş Kayıtlar</h2>
|
|
||||||
{!journals ? (
|
|
||||||
<p className="text-sm text-muted-foreground">Yükleniyor...</p>
|
|
||||||
) : journals.length === 0 ? (
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Henüz kayıt bulunmuyor.
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<div className="grid gap-4">
|
|
||||||
{journals.map((journal) => (
|
|
||||||
<Card key={journal.id}>
|
|
||||||
<CardHeader className="pb-2">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<CardTitle className="flex items-center gap-2 text-base">
|
|
||||||
<span>
|
|
||||||
{moods.find((currentMood) => currentMood.id === journal.mood)
|
|
||||||
?.emoji ?? "📝"}
|
|
||||||
</span>
|
|
||||||
<span>{journal.date}</span>
|
|
||||||
</CardTitle>
|
|
||||||
<span className="rounded-md bg-muted px-2 py-1 text-xs font-medium text-muted-foreground">
|
|
||||||
Enerji: {journal.energy}/5
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="pt-2">
|
))}
|
||||||
<p className="whitespace-pre-wrap text-sm leading-relaxed">
|
</div>
|
||||||
{journal.content}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{journal.ai_tags && (
|
|
||||||
<div className="mt-4 flex flex-wrap gap-2">
|
|
||||||
{journal.ai_tags.map((tag) => (
|
|
||||||
<span
|
|
||||||
key={tag}
|
|
||||||
className="rounded-md bg-primary/10 px-2 py-1 text-xs text-primary"
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{journal.ai_summary && (
|
|
||||||
<div className="mt-3 rounded-lg border border-l-2 border-l-primary bg-muted/40 p-3 text-sm italic text-muted-foreground">
|
|
||||||
“{journal.ai_summary}”
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-4 flex justify-end">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
className="text-red-500 hover:bg-red-500/10 hover:text-red-600"
|
|
||||||
onClick={() => handleDelete(journal.id)}
|
|
||||||
>
|
|
||||||
Sil
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
|
{/* Right Panel: Editor Mock */}
|
||||||
|
<div className="flex-1 rounded-sm border border-white/5 bg-[#0A0710] flex flex-col relative overflow-hidden group">
|
||||||
|
|
||||||
|
{/* Editor Header */}
|
||||||
|
<div className="p-4 border-b border-white/5 flex justify-between items-center bg-[#0F0B15]/50">
|
||||||
|
<div className="flex items-center gap-4 text-xs text-muted-foreground">
|
||||||
|
<span className="flex items-center gap-1.5"><Calendar className="h-4 w-4" /> Today, 10:45 AM</span>
|
||||||
|
<span className="flex items-center gap-1.5"><Tag className="h-4 w-4" /> 3 Tags</span>
|
||||||
|
<span className="flex items-center gap-1.5"><Lock className="h-4 w-4" /> Private</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button className="p-1.5 hover:bg-white/10 rounded-sm text-muted-foreground transition-colors"><AlignLeft className="h-4 w-4" /></button>
|
||||||
|
<button className="p-1.5 hover:bg-white/10 rounded-sm text-muted-foreground transition-colors"><ImageIcon className="h-4 w-4" /></button>
|
||||||
|
<button className="p-1.5 hover:bg-primary/20 bg-primary/10 rounded-sm text-primary transition-colors flex items-center gap-1">
|
||||||
|
<Sparkles className="h-4 w-4" /> <span className="text-[10px] font-bold">Ask AI</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Editor Body */}
|
||||||
|
<div className="flex-1 p-8 md:px-12 md:py-10 overflow-y-auto tiny-scrollbar">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="Breakthrough in System Design"
|
||||||
|
readOnly
|
||||||
|
className="w-full bg-transparent border-none outline-none text-3xl font-bold text-foreground mb-6"
|
||||||
|
/>
|
||||||
|
<div className="text-foreground/80 leading-loose space-y-6 text-sm">
|
||||||
|
<p>
|
||||||
|
Today we finally cracked the database architecture. By moving to a distributed model, we've essentially solved the latency issues we were seeing during peak load. It feels incredibly satisfying to see weeks of research finally click into place.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
I was discussing this with Alex earlier, and he pointed out that this structure directly mirrors the microservices split we planned for Q4. This means we are actually ahead of schedule!
|
||||||
|
</p>
|
||||||
|
<div className="pl-4 border-l-2 border-primary text-primary italic">
|
||||||
|
AI Note: You've mentioned "latency issues" in 3 previous entries. Consider documenting this final solution in the Engineering Wiki for future reference.
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
Gonna take the rest of the evening off to recharge. Need to keep this momentum going without burning out.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+188
-161
@@ -1,174 +1,201 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { Plus, Search, Filter, Brain, CheckSquare2, Clock, AlertCircle, MoreHorizontal, MessageSquare, ArrowUpRight, CheckCircle2 } from "lucide-react";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
|
||||||
import { useLiveQuery } from "dexie-react-hooks";
|
// Mock Data
|
||||||
import { db } from "@/lib/db";
|
const tasks = [
|
||||||
import {
|
{
|
||||||
Card,
|
id: 1,
|
||||||
CardContent,
|
title: "Finalize Q3 Marketing Assets",
|
||||||
CardHeader,
|
project: "Marketing Site",
|
||||||
CardTitle,
|
priority: "High",
|
||||||
CardDescription,
|
status: "In Progress",
|
||||||
} from "@/components/ui/card";
|
assignee: "Sarah J.",
|
||||||
import { Button } from "@/components/ui/button";
|
aiPredict: "2 days",
|
||||||
import { Input } from "@/components/ui/input";
|
dueDate: "May 15"
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
},
|
||||||
import { Trash2 } from "lucide-react";
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Database Migration Script",
|
||||||
|
project: "Infrastructure",
|
||||||
|
priority: "Critical",
|
||||||
|
status: "Review",
|
||||||
|
assignee: "Alex R.",
|
||||||
|
aiPredict: "4 hours",
|
||||||
|
dueDate: "May 12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Design System Tokens",
|
||||||
|
project: "Cognis Mobile",
|
||||||
|
priority: "Medium",
|
||||||
|
status: "To Do",
|
||||||
|
assignee: "Mike C.",
|
||||||
|
aiPredict: "3 days",
|
||||||
|
dueDate: "May 20"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "Client Interview Synthesis",
|
||||||
|
project: "Research",
|
||||||
|
priority: "Low",
|
||||||
|
status: "Done",
|
||||||
|
assignee: "Emma W.",
|
||||||
|
aiPredict: "Completed",
|
||||||
|
dueDate: "May 08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
title: "Optimize Webpack Config",
|
||||||
|
project: "Infrastructure",
|
||||||
|
priority: "Medium",
|
||||||
|
status: "To Do",
|
||||||
|
assignee: "Alex R.",
|
||||||
|
aiPredict: "1 day",
|
||||||
|
dueDate: "May 18"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default function TasksPage() {
|
export default function TasksPage() {
|
||||||
const [title, setTitle] = useState("");
|
|
||||||
|
|
||||||
const tasks = useLiveQuery(
|
|
||||||
() => db.tasks.toArray(), // Şimdilik hepsini çekiyoruz, sıralama yapılabilir
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleAddTask = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (!title.trim()) return;
|
|
||||||
|
|
||||||
const now = new Date().toISOString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await db.tasks.add({
|
|
||||||
id: uuidv4(),
|
|
||||||
title,
|
|
||||||
status: "todo",
|
|
||||||
ai_generated: false,
|
|
||||||
date: now.split("T")[0],
|
|
||||||
created_at: now,
|
|
||||||
});
|
|
||||||
setTitle("");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Görev eklenemedi:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleTaskStatus = async (id: string, currentStatus: string) => {
|
|
||||||
await db.tasks.update(id, {
|
|
||||||
status: currentStatus === "todo" ? "completed" : "todo",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteTask = async (id: string) => {
|
|
||||||
await db.tasks.delete(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
const pendingTasks = tasks?.filter((t) => t.status === "todo") || [];
|
|
||||||
const completedTasks = tasks?.filter((t) => t.status === "completed") || [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 max-w-4xl mx-auto animate-in fade-in slide-in-from-bottom-4 duration-500">
|
<div className="mx-auto max-w-7xl animate-in fade-in slide-in-from-bottom-4 duration-500 h-full flex flex-col text-foreground font-sans space-y-6">
|
||||||
<div>
|
|
||||||
<h1 className="text-3xl font-bold tracking-tight">
|
{/* Top Header */}
|
||||||
Görevler & Planlar
|
<div className="flex items-center justify-between pb-4 border-b border-white/5 mt-4">
|
||||||
|
<h1 className="text-lg font-medium text-muted-foreground">
|
||||||
|
<span className="text-foreground">Management</span> / Tasks
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-muted-foreground">
|
<div className="flex items-center gap-3">
|
||||||
Kişisel hedeflerin ve YZ tarafından önerilen aksiyonlar.
|
<div className="bg-[#150F1D] border border-white/5 rounded-sm px-3 py-1.5 flex items-center gap-2">
|
||||||
</p>
|
<Search className="h-4 w-4 text-muted-foreground" />
|
||||||
</div>
|
<input
|
||||||
|
type="text"
|
||||||
<Card>
|
placeholder="Search tasks..."
|
||||||
<CardHeader>
|
className="bg-transparent border-none outline-none text-xs w-48 placeholder:text-muted-foreground/50 text-foreground"
|
||||||
<CardTitle>Yeni Görev</CardTitle>
|
|
||||||
<CardDescription>Aklındakini aksiyona dönüştür.</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<form onSubmit={handleAddTask} className="flex gap-2">
|
|
||||||
<Input
|
|
||||||
placeholder="Örn: 15 dakika yürüyüş yap..."
|
|
||||||
value={title}
|
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
|
||||||
className="flex-1"
|
|
||||||
/>
|
/>
|
||||||
<Button type="submit">Ekle</Button>
|
|
||||||
</form>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2 pt-4">
|
|
||||||
{/* Yapılacaklar */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
|
||||||
Yapılacaklar{" "}
|
|
||||||
<span className="text-sm px-2 py-0.5 rounded-full bg-primary/20 text-primary">
|
|
||||||
{pendingTasks.length}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{pendingTasks.length === 0 ? (
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Bekleyen görev yok.
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
pendingTasks.map((task) => (
|
|
||||||
<div
|
|
||||||
key={task.id}
|
|
||||||
className="flex items-center gap-3 p-3 rounded-lg border bg-card shadow-sm group transition-all hover:border-primary/50"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={false}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
toggleTaskStatus(task.id, task.status)
|
|
||||||
}
|
|
||||||
className="w-5 h-5"
|
|
||||||
/>
|
|
||||||
<span className="flex-1 text-sm font-medium">
|
|
||||||
{task.title}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
onClick={() => deleteTask(task.id)}
|
|
||||||
className="opacity-0 group-hover:opacity-100 p-1.5 text-muted-foreground hover:text-red-500 transition-all rounded-md hover:bg-red-500/10"
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Tamamlananlar */}
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h2 className="text-xl font-semibold opacity-70 flex items-center gap-2">
|
|
||||||
Tamamlananlar{" "}
|
|
||||||
<span className="text-sm px-2 py-0.5 rounded-full bg-muted text-muted-foreground">
|
|
||||||
{completedTasks.length}
|
|
||||||
</span>
|
|
||||||
</h2>
|
|
||||||
<div className="space-y-2">
|
|
||||||
{completedTasks.length === 0 ? (
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Hiç görev tamamlanmadı.
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
completedTasks.map((task) => (
|
|
||||||
<div
|
|
||||||
key={task.id}
|
|
||||||
className="flex items-center gap-3 p-3 rounded-lg border bg-muted/40 shadow-sm group"
|
|
||||||
>
|
|
||||||
<Checkbox
|
|
||||||
checked={true}
|
|
||||||
onCheckedChange={() =>
|
|
||||||
toggleTaskStatus(task.id, task.status)
|
|
||||||
}
|
|
||||||
className="w-5 h-5 data-[state=checked]:bg-muted-foreground data-[state=checked]:border-muted-foreground"
|
|
||||||
/>
|
|
||||||
<span className="flex-1 text-sm line-through text-muted-foreground">
|
|
||||||
{task.title}
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
onClick={() => deleteTask(task.id)}
|
|
||||||
className="opacity-0 group-hover:opacity-100 p-1.5 text-muted-foreground hover:text-red-500 transition-all rounded-md hover:bg-red-500/10"
|
|
||||||
>
|
|
||||||
<Trash2 className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<button className="bg-[#150F1D] hover:bg-white/5 text-foreground border border-white/10 px-3 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Filter className="h-4 w-4" />
|
||||||
|
FILTER
|
||||||
|
</button>
|
||||||
|
<button className="bg-primary hover:bg-primary/90 text-primary-foreground border border-primary/20 px-4 py-1.5 rounded-sm text-xs font-semibold flex items-center gap-2 transition-colors">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
NEW TASK
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* AI Task Prioritization */}
|
||||||
|
<div className="rounded-sm border border-primary/20 bg-primary/5 p-6 flex flex-col md:flex-row items-start md:items-center justify-between gap-6">
|
||||||
|
<div className="flex items-start gap-4">
|
||||||
|
<div className="p-3 bg-primary/10 rounded-sm shrink-0">
|
||||||
|
<Brain className="h-6 w-6 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-semibold text-primary mb-1 flex items-center gap-2">
|
||||||
|
AI Task Prioritization
|
||||||
|
</h3>
|
||||||
|
<p className="text-xs text-foreground/80 leading-relaxed max-w-2xl">
|
||||||
|
Based on the upcoming <strong>Infrastructure</strong> deadlines and <strong>Alex R.</strong>'s historical velocity, AI strongly suggests starting the <em>Database Migration Script</em> immediately. It has a high risk of cascading delays.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button className="bg-primary/20 text-primary hover:bg-primary/30 border border-primary/30 px-4 py-2 rounded-sm text-xs font-semibold transition-colors shrink-0">
|
||||||
|
Auto-Sort Backlog
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Task List Header */}
|
||||||
|
<div className="flex items-center justify-between mt-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button className="px-3 py-1.5 text-xs font-medium rounded-sm bg-[#2B2538] text-foreground shadow-sm">List View</button>
|
||||||
|
<button className="px-3 py-1.5 text-xs font-medium rounded-sm text-muted-foreground hover:text-foreground transition-colors">Kanban Board</button>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground font-medium">12 Active Tasks</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tasks Table */}
|
||||||
|
<div className="bg-[#0A0710] rounded-sm border border-white/5 overflow-hidden flex-1">
|
||||||
|
<div className="grid grid-cols-12 gap-4 p-4 border-b border-white/5 bg-[#0F0B15]/50 text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">
|
||||||
|
<div className="col-span-5">Task Details</div>
|
||||||
|
<div className="col-span-2">Status</div>
|
||||||
|
<div className="col-span-2">Assignee</div>
|
||||||
|
<div className="col-span-2">AI Time Estimate</div>
|
||||||
|
<div className="col-span-1 text-right">Actions</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="divide-y divide-white/5">
|
||||||
|
{tasks.map((task) => (
|
||||||
|
<div key={task.id} className="grid grid-cols-12 gap-4 p-4 items-center hover:bg-white/[0.02] transition-colors group">
|
||||||
|
|
||||||
|
{/* Task Details */}
|
||||||
|
<div className="col-span-5 flex items-start gap-3">
|
||||||
|
<button className="mt-0.5 text-muted-foreground hover:text-primary transition-colors">
|
||||||
|
{task.status === "Done" ? <CheckCircle2 className="h-4 w-4 text-emerald-400" /> : <CheckSquare2 className="h-4 w-4" />}
|
||||||
|
</button>
|
||||||
|
<div>
|
||||||
|
<div className={`text-sm font-medium mb-1 ${task.status === 'Done' ? 'text-muted-foreground line-through' : 'text-foreground'}`}>
|
||||||
|
{task.title}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-[10px] text-muted-foreground font-medium bg-[#150F1D] px-2 py-0.5 rounded-sm border border-white/5">
|
||||||
|
{task.project}
|
||||||
|
</span>
|
||||||
|
<span className={`text-[10px] uppercase font-bold tracking-wider flex items-center gap-1 ${
|
||||||
|
task.priority === 'Critical' ? 'text-red-400' :
|
||||||
|
task.priority === 'High' ? 'text-orange-400' :
|
||||||
|
task.priority === 'Medium' ? 'text-blue-400' : 'text-muted-foreground'
|
||||||
|
}`}>
|
||||||
|
{task.priority === 'Critical' && <AlertCircle className="h-3 w-3" />}
|
||||||
|
{task.priority}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status */}
|
||||||
|
<div className="col-span-2">
|
||||||
|
<span className={`text-[10px] uppercase font-bold px-2 py-1 rounded-sm border inline-block ${
|
||||||
|
task.status === 'In Progress' ? 'bg-blue-500/10 text-blue-400 border-blue-500/20' :
|
||||||
|
task.status === 'Review' ? 'bg-orange-500/10 text-orange-400 border-orange-500/20' :
|
||||||
|
task.status === 'Done' ? 'bg-emerald-500/10 text-emerald-400 border-emerald-500/20' :
|
||||||
|
'bg-white/5 text-muted-foreground border-white/10'
|
||||||
|
}`}>
|
||||||
|
{task.status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Assignee */}
|
||||||
|
<div className="col-span-2 flex items-center gap-2">
|
||||||
|
<div className="h-6 w-6 rounded-sm bg-[#1F172B] border border-white/10 flex items-center justify-center text-[10px] font-bold text-primary">
|
||||||
|
{task.assignee.split(' ')[0][0]}{task.assignee.split(' ')[1][0]}
|
||||||
|
</div>
|
||||||
|
<span className="text-xs text-muted-foreground">{task.assignee}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* AI Estimate */}
|
||||||
|
<div className="col-span-2 flex items-center gap-2">
|
||||||
|
{task.status !== 'Done' && <Clock className="h-3.5 w-3.5 text-primary" />}
|
||||||
|
<span className={`text-xs ${task.status === 'Done' ? 'text-emerald-400 font-medium' : 'text-muted-foreground'}`}>
|
||||||
|
{task.aiPredict}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Actions */}
|
||||||
|
<div className="col-span-1 flex justify-end gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
|
<button className="p-1.5 hover:bg-white/10 rounded-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
<MessageSquare className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<button className="p-1.5 hover:bg-white/10 rounded-sm text-muted-foreground hover:text-foreground transition-colors">
|
||||||
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-11
@@ -2,7 +2,6 @@ import type { Metadata } from "next";
|
|||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { Geist } from "next/font/google";
|
import { Geist } from "next/font/google";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ThemeProvider } from "@/components/theme-provider";
|
|
||||||
import { Toaster } from "react-hot-toast";
|
import { Toaster } from "react-hot-toast";
|
||||||
|
|
||||||
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
|
const geist = Geist({ subsets: ["latin"], variable: "--font-sans" });
|
||||||
@@ -20,19 +19,12 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html
|
<html
|
||||||
lang="tr"
|
lang="tr"
|
||||||
className={cn("font-sans", geist.variable)}
|
className={cn("font-sans dark", geist.variable)}
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
>
|
>
|
||||||
<body>
|
<body>
|
||||||
<ThemeProvider
|
{children}
|
||||||
attribute="class"
|
<Toaster position="top-right" />
|
||||||
defaultTheme="dark"
|
|
||||||
enableSystem
|
|
||||||
disableTransitionOnChange
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
<Toaster position="top-right" />
|
|
||||||
</ThemeProvider>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
+4
-4
@@ -54,11 +54,11 @@ export const sidebarData: SidebarNavGroup[] = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "ORGANİZASYON",
|
title: "FREELANCE",
|
||||||
items: [
|
items: [
|
||||||
{ title: "Ekip", href: "/team", icon: Users },
|
{ title: "Müşteriler (CRM)", href: "/freelance/clients", icon: Building2 },
|
||||||
{ title: "Müşteriler", href: "/clients", icon: Building2 },
|
{ title: "Teklifler & Sözleşmeler", href: "/freelance/proposals", icon: Tags },
|
||||||
{ title: "Etiketler", href: "/tags", icon: Tags },
|
{ title: "Zaman Takibi", href: "/freelance/time-tracking", icon: Activity },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user