refactor: improve mobile responsiveness and layout consistency across dashboard views by adding horizontal scrolling and unifying Sidebar components.
This commit is contained in:
@@ -133,14 +133,16 @@ export function CalendarClient({ events, clients, projects, tasks }: CalendarCli
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-7 border border-border text-center text-xs font-medium uppercase text-muted-foreground">
|
<div className="overflow-x-auto pb-4">
|
||||||
{["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"].map((day) => (
|
<div className="min-w-[600px]">
|
||||||
<div key={day} className="border-r border-border px-2 py-2 last:border-r-0">
|
<div className="grid grid-cols-7 border border-border text-center text-xs font-medium uppercase text-muted-foreground">
|
||||||
{day}
|
{["Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz"].map((day) => (
|
||||||
|
<div key={day} className="border-r border-border px-2 py-2 last:border-r-0">
|
||||||
|
{day}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="grid grid-cols-7 border-x border-border">
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-7 border-x border-border">
|
|
||||||
{days.map((day) => {
|
{days.map((day) => {
|
||||||
const dayEvents = eventsByDay.get(day.key) || [];
|
const dayEvents = eventsByDay.get(day.key) || [];
|
||||||
const isSelected = selectedDate === day.key;
|
const isSelected = selectedDate === day.key;
|
||||||
@@ -173,6 +175,8 @@ export function CalendarClient({ events, clients, projects, tasks }: CalendarCli
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ export default function AIChatPage() {
|
|||||||
className={`flex ${message.role === "user" ? "justify-end" : "justify-start"}`}
|
className={`flex ${message.role === "user" ? "justify-end" : "justify-start"}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`max-w-[85%] rounded-sm px-4 py-3 text-sm ${
|
className={`max-w-[92%] md:max-w-[85%] rounded-sm px-4 py-3 text-sm ${
|
||||||
message.role === "user"
|
message.role === "user"
|
||||||
? "bg-primary text-primary-foreground"
|
? "bg-primary text-primary-foreground"
|
||||||
: "border border-border bg-muted/40 text-foreground"
|
: "border border-border bg-muted/40 text-foreground"
|
||||||
@@ -264,7 +264,7 @@ export default function AIChatPage() {
|
|||||||
<div ref={messagesEndRef} />
|
<div ref={messagesEndRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="border-t border-border p-4">
|
<form onSubmit={handleSubmit} className="border-t border-border p-4 shrink-0">
|
||||||
<div className="mx-auto flex max-w-4xl items-end gap-2 rounded-sm border border-border bg-background p-2 focus-within:border-primary">
|
<div className="mx-auto flex max-w-4xl items-end gap-2 rounded-sm border border-border bg-background p-2 focus-within:border-primary">
|
||||||
<textarea
|
<textarea
|
||||||
value={input}
|
value={input}
|
||||||
|
|||||||
@@ -250,19 +250,21 @@ export function ClientsClient({
|
|||||||
|
|
||||||
<TabsContent value="list" className="mt-0">
|
<TabsContent value="list" className="mt-0">
|
||||||
{filteredClients.length > 0 ? (
|
{filteredClients.length > 0 ? (
|
||||||
<div className="overflow-hidden">
|
<div className="overflow-x-auto rounded-sm border border-border">
|
||||||
<div className="hidden grid-cols-[1.5fr_1fr_1fr_1fr_0.8fr_0.8fr] gap-4 border-b border-border px-4 py-3 text-xs font-medium uppercase text-muted-foreground lg:grid">
|
<div className="min-w-[900px]">
|
||||||
<span>Müşteri</span>
|
<div className="grid grid-cols-[1.5fr_1fr_1fr_1fr_0.8fr_0.8fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground">
|
||||||
<span>İletişim</span>
|
<span>Müşteri</span>
|
||||||
<span>Aşama</span>
|
<span>İletişim</span>
|
||||||
<span>Follow-up</span>
|
<span>Aşama</span>
|
||||||
<span>Projeler</span>
|
<span>Follow-up</span>
|
||||||
<span className="text-right">İşlem</span>
|
<span>Projeler</span>
|
||||||
</div>
|
<span className="text-right">İşlem</span>
|
||||||
<div className="divide-y divide-border">
|
</div>
|
||||||
{filteredClients.map((client) => (
|
<div className="divide-y divide-border">
|
||||||
<ClientRow key={client.id} client={client} />
|
{filteredClients.map((client) => (
|
||||||
))}
|
<ClientRow key={client.id} client={client} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -351,7 +353,7 @@ function ClientRow({ client }: { client: ClientListItem }) {
|
|||||||
const stage = pipelineStages.find(s => s.id === client.pipeline_stage) || pipelineStages[0];
|
const stage = pipelineStages.find(s => s.id === client.pipeline_stage) || pipelineStages[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 px-4 py-4 lg:grid-cols-[1.5fr_1fr_1fr_1fr_0.8fr_0.8fr] lg:items-center">
|
<div className="grid gap-4 px-4 py-4 grid-cols-[1.5fr_1fr_1fr_1fr_0.8fr_0.8fr] items-center">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-sm bg-primary/10 text-sm font-semibold text-primary">
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-sm bg-primary/10 text-sm font-semibold text-primary">
|
||||||
@@ -406,7 +408,7 @@ function ClientRow({ client }: { client: ClientListItem }) {
|
|||||||
<div className="text-muted-foreground">{formatCurrency(client.revenueTotal)}</div>
|
<div className="text-muted-foreground">{formatCurrency(client.revenueTotal)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-start gap-2 lg:justify-end">
|
<div className="flex justify-end gap-2">
|
||||||
<Link href={`/clients/${client.id}`}>
|
<Link href={`/clients/${client.id}`}>
|
||||||
<Button variant="ghost" className="h-9 w-9 p-0">
|
<Button variant="ghost" className="h-9 w-9 p-0">
|
||||||
<ArrowRight className="h-4 w-4" />
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
|||||||
@@ -206,14 +206,15 @@ export default function DocumentsPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-[#0A0710] border border-white/5 rounded-sm overflow-hidden shadow-2xl mb-8">
|
<div className="overflow-x-auto bg-[#0A0710] border border-white/5 rounded-sm shadow-2xl mb-8">
|
||||||
<div className="grid grid-cols-12 gap-4 p-5 border-b border-white/5 bg-[#0F0B15]/50 text-[9px] font-black uppercase tracking-[0.3em] text-muted-foreground">
|
<div className="min-w-[800px]">
|
||||||
<div className="col-span-6 px-2">Document Name</div>
|
<div className="grid grid-cols-12 gap-4 p-5 border-b border-white/5 bg-[#0F0B15]/50 text-[9px] font-black uppercase tracking-[0.3em] text-muted-foreground">
|
||||||
<div className="col-span-2">File Weight</div>
|
<div className="col-span-6 px-2">Document Name</div>
|
||||||
<div className="col-span-2">Ownership</div>
|
<div className="col-span-2">File Weight</div>
|
||||||
<div className="col-span-2 text-right">Strategic Date</div>
|
<div className="col-span-2">Ownership</div>
|
||||||
</div>
|
<div className="col-span-2 text-right">Strategic Date</div>
|
||||||
<div className="divide-y divide-white/5">
|
</div>
|
||||||
|
<div className="divide-y divide-white/5">
|
||||||
{documents.map(doc => (
|
{documents.map(doc => (
|
||||||
<div key={doc.id} onClick={() => setSelectedDoc(doc)} className="grid grid-cols-12 gap-4 p-6 items-center hover:bg-white/[0.02] transition-all group cursor-pointer border-l-2 border-transparent hover:border-primary">
|
<div key={doc.id} onClick={() => setSelectedDoc(doc)} className="grid grid-cols-12 gap-4 p-6 items-center hover:bg-white/[0.02] transition-all group cursor-pointer border-l-2 border-transparent hover:border-primary">
|
||||||
<div className="col-span-6 flex items-center gap-5">
|
<div className="col-span-6 flex items-center gap-5">
|
||||||
@@ -226,11 +227,12 @@ export default function DocumentsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 text-[11px] text-muted-foreground font-black uppercase tracking-tighter">{doc.size}</div>
|
<div className="col-span-2 text-[11px] text-muted-foreground font-black uppercase tracking-tighter">{doc.size}</div>
|
||||||
<div className="col-span-2 text-[11px] text-muted-foreground font-black uppercase tracking-widest">{doc.owner}</div>
|
<div className="col-span-2 text-[11px] text-muted-foreground font-black uppercase tracking-widest">{doc.owner}</div>
|
||||||
<div className="col-span-2 text-right text-[11px] text-muted-foreground font-black uppercase tracking-tighter">{doc.date.split(',')[0]}</div>
|
<div className="col-span-2 text-right text-[11px] text-muted-foreground font-black uppercase tracking-tighter">{doc.date.split(',')[0]}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -170,15 +170,16 @@ export function FinanceClient({ transactions, clients, projects }: FinanceClient
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{filteredTransactions.length > 0 ? (
|
{filteredTransactions.length > 0 ? (
|
||||||
<div className="overflow-hidden rounded-sm border border-border">
|
<div className="overflow-x-auto rounded-sm border border-border">
|
||||||
<div className="hidden grid-cols-[1.4fr_0.8fr_0.8fr_0.8fr_1fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground lg:grid">
|
<div className="min-w-[800px]">
|
||||||
<span>İşlem</span>
|
<div className="grid grid-cols-[1.4fr_0.8fr_0.8fr_0.8fr_1fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground">
|
||||||
<span>Tarih</span>
|
<span>İşlem</span>
|
||||||
<span>Tutar</span>
|
<span>Tarih</span>
|
||||||
<span>Durum</span>
|
<span>Tutar</span>
|
||||||
<span className="text-right">İşlem</span>
|
<span>Durum</span>
|
||||||
</div>
|
<span className="text-right">İşlem</span>
|
||||||
<div className="divide-y divide-border">
|
</div>
|
||||||
|
<div className="divide-y divide-border">
|
||||||
{filteredTransactions.map((transaction) => (
|
{filteredTransactions.map((transaction) => (
|
||||||
<TransactionRow
|
<TransactionRow
|
||||||
key={transaction.id}
|
key={transaction.id}
|
||||||
@@ -187,6 +188,7 @@ export function FinanceClient({ transactions, clients, projects }: FinanceClient
|
|||||||
projects={projects}
|
projects={projects}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -240,7 +242,7 @@ function TransactionRow({
|
|||||||
const isIncome = transaction.type === "income";
|
const isIncome = transaction.type === "income";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 px-4 py-4 lg:grid-cols-[1.4fr_0.8fr_0.8fr_0.8fr_1fr] lg:items-center">
|
<div className="grid gap-4 px-4 py-4 grid-cols-[1.4fr_0.8fr_0.8fr_0.8fr_1fr] items-center">
|
||||||
<div className="flex min-w-0 items-start gap-3">
|
<div className="flex min-w-0 items-start gap-3">
|
||||||
<div className={isIncome ? "rounded-sm bg-emerald-50 p-2 text-emerald-700" : "rounded-sm bg-rose-50 p-2 text-rose-700"}>
|
<div className={isIncome ? "rounded-sm bg-emerald-50 p-2 text-emerald-700" : "rounded-sm bg-rose-50 p-2 text-rose-700"}>
|
||||||
{isIncome ? <ArrowUpRight className="h-4 w-4" /> : <ArrowDownRight className="h-4 w-4" />}
|
{isIncome ? <ArrowUpRight className="h-4 w-4" /> : <ArrowDownRight className="h-4 w-4" />}
|
||||||
@@ -264,7 +266,7 @@ function TransactionRow({
|
|||||||
{paymentStatusLabels[transaction.payment_status]}
|
{paymentStatusLabels[transaction.payment_status]}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-start gap-2 lg:justify-end">
|
<div className="flex justify-end gap-2">
|
||||||
<FinanceDialog mode="edit" transaction={transaction} clients={clients} projects={projects} />
|
<FinanceDialog mode="edit" transaction={transaction} clients={clients} projects={projects} />
|
||||||
<form action={deleteFinanceTransactionRecord}>
|
<form action={deleteFinanceTransactionRecord}>
|
||||||
<input type="hidden" name="id" value={transaction.id} />
|
<input type="hidden" name="id" value={transaction.id} />
|
||||||
@@ -542,6 +544,29 @@ function calculateSummary(transactions: FinanceTransactionItem[]) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatMessageContent(text: string) {
|
||||||
|
if (!text) return null;
|
||||||
|
const lines = text.split("\n");
|
||||||
|
return lines.map((line, i) => (
|
||||||
|
<span key={i}>
|
||||||
|
{line.split(/(\*\*.*?\*\*|\*.*?\*)/g).map((part, j) => {
|
||||||
|
if (part.startsWith("**") && part.endsWith("**")) {
|
||||||
|
return (
|
||||||
|
<strong key={j} className="font-semibold text-foreground">
|
||||||
|
{part.slice(2, -2)}
|
||||||
|
</strong>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (part.startsWith("*") && part.endsWith("*")) {
|
||||||
|
return <em key={j}>{part.slice(1, -1)}</em>;
|
||||||
|
}
|
||||||
|
return <span key={j}>{part}</span>;
|
||||||
|
})}
|
||||||
|
{i !== lines.length - 1 && <br />}
|
||||||
|
</span>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
function AIFinanceDialog() {
|
function AIFinanceDialog() {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -572,7 +597,7 @@ function AIFinanceDialog() {
|
|||||||
AI Analizi
|
AI Analizi
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:max-w-2xl max-h-[80vh] overflow-y-auto">
|
<DialogContent className="w-[calc(100vw-2rem)] sm:max-w-2xl max-h-[80vh] overflow-y-auto rounded-lg p-6">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<Brain className="h-5 w-5 text-indigo-600" />
|
<Brain className="h-5 w-5 text-indigo-600" />
|
||||||
@@ -602,13 +627,13 @@ function AIFinanceDialog() {
|
|||||||
|
|
||||||
{result && (
|
{result && (
|
||||||
<div className="bg-muted/50 border border-border rounded-lg p-5 text-sm prose prose-sm dark:prose-invert max-w-none whitespace-pre-wrap">
|
<div className="bg-muted/50 border border-border rounded-lg p-5 text-sm prose prose-sm dark:prose-invert max-w-none whitespace-pre-wrap">
|
||||||
{result}
|
{formatMessageContent(result)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{result && (
|
{result && (
|
||||||
<DialogFooter>
|
<DialogFooter className="gap-2 sm:gap-0">
|
||||||
<Button variant="outline" onClick={() => setOpen(false)}>Kapat</Button>
|
<Button variant="outline" onClick={() => setOpen(false)}>Kapat</Button>
|
||||||
<Button variant="default" onClick={handleAnalyze} className="gap-2">
|
<Button variant="default" onClick={handleAnalyze} className="gap-2">
|
||||||
<Brain className="h-4 w-4" />
|
<Brain className="h-4 w-4" />
|
||||||
|
|||||||
@@ -194,18 +194,20 @@ export function JournalClient({ logs }: JournalClientProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{logs.length > 0 ? (
|
{logs.length > 0 ? (
|
||||||
<div className="overflow-hidden rounded-sm border border-border">
|
<div className="overflow-x-auto rounded-sm border border-border">
|
||||||
<div className="hidden grid-cols-[0.7fr_0.7fr_0.7fr_1.8fr_0.8fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground lg:grid">
|
<div className="min-w-[800px]">
|
||||||
<span>Tarih</span>
|
<div className="grid grid-cols-[0.7fr_0.7fr_0.7fr_1.8fr_0.8fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground">
|
||||||
<span>Mood</span>
|
<span>Tarih</span>
|
||||||
<span>Enerji</span>
|
<span>Mood</span>
|
||||||
<span>Not</span>
|
<span>Enerji</span>
|
||||||
<span className="text-right">İşlem</span>
|
<span>Not</span>
|
||||||
</div>
|
<span className="text-right">İşlem</span>
|
||||||
<div className="divide-y divide-border">
|
</div>
|
||||||
|
<div className="divide-y divide-border">
|
||||||
{logs.map((log) => (
|
{logs.map((log) => (
|
||||||
<DailyLogRow key={log.id} log={log} />
|
<DailyLogRow key={log.id} log={log} />
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -219,7 +221,7 @@ export function JournalClient({ logs }: JournalClientProps) {
|
|||||||
|
|
||||||
function DailyLogRow({ log }: { log: DailyLogItem }) {
|
function DailyLogRow({ log }: { log: DailyLogItem }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 px-4 py-4 lg:grid-cols-[0.7fr_0.7fr_0.7fr_1.8fr_0.8fr] lg:items-center">
|
<div className="grid gap-4 px-4 py-4 grid-cols-[0.7fr_0.7fr_0.7fr_1.8fr_0.8fr] items-center">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-foreground">{formatDate(log.log_date)}</div>
|
<div className="font-medium text-foreground">{formatDate(log.log_date)}</div>
|
||||||
<div className="text-xs text-muted-foreground">{formatWeekday(log.log_date)}</div>
|
<div className="text-xs text-muted-foreground">{formatWeekday(log.log_date)}</div>
|
||||||
|
|||||||
@@ -187,18 +187,20 @@ export function ProjectsClient({ projects, clients }: ProjectsClientProps) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-hidden rounded-sm border border-border">
|
<div className="overflow-x-auto rounded-sm border border-border">
|
||||||
<div className="hidden grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground lg:grid">
|
<div className="min-w-[800px]">
|
||||||
<span>Proje</span>
|
<div className="grid grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground">
|
||||||
<span>Tür</span>
|
<span>Proje</span>
|
||||||
<span>Durum</span>
|
<span>Tür</span>
|
||||||
<span>İlerleme</span>
|
<span>Durum</span>
|
||||||
<span className="text-right">İşlem</span>
|
<span>İlerleme</span>
|
||||||
</div>
|
<span className="text-right">İşlem</span>
|
||||||
<div className="divide-y divide-border">
|
</div>
|
||||||
{filteredProjects.map((project) => (
|
<div className="divide-y divide-border">
|
||||||
<ProjectRow key={project.id} project={project} clients={clients} />
|
{filteredProjects.map((project) => (
|
||||||
))}
|
<ProjectRow key={project.id} project={project} clients={clients} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -292,7 +294,7 @@ function ProjectRow({
|
|||||||
clients: ProjectClientOption[];
|
clients: ProjectClientOption[];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 px-4 py-4 lg:grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] lg:items-center">
|
<div className="grid gap-4 px-4 py-4 grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] items-center">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="font-medium text-foreground">{project.name}</div>
|
<div className="font-medium text-foreground">{project.name}</div>
|
||||||
<div className="truncate text-sm text-muted-foreground">
|
<div className="truncate text-sm text-muted-foreground">
|
||||||
@@ -306,7 +308,7 @@ function ProjectRow({
|
|||||||
<div>
|
<div>
|
||||||
<ProgressBar progress={project.progress} compact />
|
<ProgressBar progress={project.progress} compact />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-start lg:justify-end">
|
<div className="flex justify-end gap-2">
|
||||||
<ProjectActions project={project} clients={clients} showDetail />
|
<ProjectActions project={project} clients={clients} showDetail />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -144,14 +144,14 @@ export default function SettingsPage() {
|
|||||||
|
|
||||||
<div className="flex flex-col md:flex-row gap-8 flex-1 min-h-0 pb-12">
|
<div className="flex flex-col md:flex-row gap-8 flex-1 min-h-0 pb-12">
|
||||||
{/* Settings Sidebar */}
|
{/* Settings Sidebar */}
|
||||||
<div className="w-full md:w-64 flex flex-col gap-1 shrink-0">
|
<div className="w-full md:w-64 flex overflow-x-auto md:flex-col gap-2 shrink-0 pb-2 md:pb-0 tiny-scrollbar">
|
||||||
{tabs.map((tab) => {
|
{tabs.map((tab) => {
|
||||||
const Icon = tab.icon;
|
const Icon = tab.icon;
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={tab.name}
|
key={tab.name}
|
||||||
onClick={() => setActiveTab(tab.name)}
|
onClick={() => setActiveTab(tab.name)}
|
||||||
className={`flex items-center gap-3 px-4 py-3 rounded-md text-sm font-medium transition-colors text-left ${
|
className={`flex shrink-0 items-center gap-3 px-4 py-3 rounded-md text-sm font-medium transition-colors text-left ${
|
||||||
activeTab === tab.name
|
activeTab === tab.name
|
||||||
? "bg-primary/10 text-primary"
|
? "bg-primary/10 text-primary"
|
||||||
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
: "text-muted-foreground hover:bg-muted hover:text-foreground"
|
||||||
|
|||||||
@@ -236,18 +236,20 @@ function TaskList({
|
|||||||
projects: TaskRelationOption[];
|
projects: TaskRelationOption[];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-sm border border-border">
|
<div className="overflow-x-auto rounded-sm border border-border">
|
||||||
<div className="hidden grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground lg:grid">
|
<div className="min-w-[800px]">
|
||||||
<span>Görev</span>
|
<div className="grid grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] gap-4 border-b border-border bg-muted/40 px-4 py-3 text-xs font-medium uppercase text-muted-foreground">
|
||||||
<span>Bağlantı</span>
|
<span>Görev</span>
|
||||||
<span>Öncelik</span>
|
<span>Bağlantı</span>
|
||||||
<span>Son tarih</span>
|
<span>Öncelik</span>
|
||||||
<span className="text-right">İşlem</span>
|
<span>Son tarih</span>
|
||||||
</div>
|
<span className="text-right">İşlem</span>
|
||||||
<div className="divide-y divide-border">
|
</div>
|
||||||
|
<div className="divide-y divide-border">
|
||||||
{tasks.map((task) => (
|
{tasks.map((task) => (
|
||||||
<TaskRow key={task.id} task={task} clients={clients} projects={projects} />
|
<TaskRow key={task.id} task={task} clients={clients} projects={projects} />
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -263,7 +265,7 @@ function TaskRow({
|
|||||||
projects: TaskRelationOption[];
|
projects: TaskRelationOption[];
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 px-4 py-4 lg:grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] lg:items-center">
|
<div className="grid gap-4 px-4 py-4 grid-cols-[1.5fr_1fr_1fr_0.8fr_1fr] items-center">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className={task.status === "done" ? "font-medium text-muted-foreground line-through" : "font-medium text-foreground"}>
|
<div className={task.status === "done" ? "font-medium text-muted-foreground line-through" : "font-medium text-foreground"}>
|
||||||
{task.title}
|
{task.title}
|
||||||
|
|||||||
@@ -137,8 +137,8 @@ export default function TimeTrackingPage() {
|
|||||||
<h3 className="text-[10px] font-black uppercase tracking-[0.3em] text-muted-foreground">Today's Activity Ledger</h3>
|
<h3 className="text-[10px] font-black uppercase tracking-[0.3em] text-muted-foreground">Today's Activity Ledger</h3>
|
||||||
<button className="text-[10px] font-bold text-primary uppercase tracking-widest">Full History</button>
|
<button className="text-[10px] font-bold text-primary uppercase tracking-widest">Full History</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 overflow-y-auto tiny-scrollbar">
|
<div className="flex-1 overflow-y-auto overflow-x-auto tiny-scrollbar">
|
||||||
<div className="divide-y divide-white/5">
|
<div className="min-w-[600px] divide-y divide-white/5">
|
||||||
{recentLogs.map(log => (
|
{recentLogs.map(log => (
|
||||||
<div key={log.id} className="grid grid-cols-12 gap-4 p-5 items-center hover:bg-white/[0.02] transition-all group">
|
<div key={log.id} className="grid grid-cols-12 gap-4 p-5 items-center hover:bg-white/[0.02] transition-all group">
|
||||||
<div className="col-span-5 flex items-center gap-4">
|
<div className="col-span-5 flex items-center gap-4">
|
||||||
@@ -155,6 +155,7 @@ export default function TimeTrackingPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ export function DashboardShell({ children, user }: DashboardShellProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background text-foreground">
|
<div className="min-h-screen bg-background text-foreground">
|
||||||
<div className="flex min-h-screen">
|
<div className="flex min-h-screen">
|
||||||
<aside className="sticky top-0 hidden h-dvh shrink-0 self-start border-r border-border bg-background lg:block">
|
<AppSidebar
|
||||||
<AppSidebar pathname={pathname} user={user} />
|
pathname={pathname}
|
||||||
</aside>
|
user={user}
|
||||||
|
className="sticky top-0 hidden shrink-0 self-start lg:block"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Mobile Sidebar Overlay */}
|
{/* Mobile Sidebar Overlay */}
|
||||||
{isMobileSidebarOpen && (
|
{isMobileSidebarOpen && (
|
||||||
@@ -61,17 +63,14 @@ export function DashboardShell({ children, user }: DashboardShellProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Mobile Sidebar Drawer */}
|
{/* Mobile Sidebar Drawer */}
|
||||||
<aside
|
<AppSidebar
|
||||||
className={`fixed inset-y-0 left-0 z-50 w-72 transform border-r border-border bg-background transition-transform duration-300 ease-in-out lg:hidden ${
|
pathname={pathname}
|
||||||
|
user={user}
|
||||||
|
onNavigate={() => setIsMobileSidebarOpen(false)}
|
||||||
|
className={`fixed inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out lg:hidden ${
|
||||||
isMobileSidebarOpen ? "translate-x-0" : "-translate-x-full"
|
isMobileSidebarOpen ? "translate-x-0" : "-translate-x-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
/>
|
||||||
<AppSidebar
|
|
||||||
pathname={pathname}
|
|
||||||
user={user}
|
|
||||||
onNavigate={() => setIsMobileSidebarOpen(false)}
|
|
||||||
/>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<div className="flex flex-1 flex-col min-w-0">
|
<div className="flex flex-1 flex-col min-w-0">
|
||||||
<header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-border bg-background/95 px-4 backdrop-blur lg:hidden">
|
<header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-border bg-background/95 px-4 backdrop-blur lg:hidden">
|
||||||
@@ -107,15 +106,17 @@ function AppSidebar({
|
|||||||
pathname,
|
pathname,
|
||||||
user,
|
user,
|
||||||
onNavigate,
|
onNavigate,
|
||||||
|
className,
|
||||||
}: {
|
}: {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
user: DashboardShellProps["user"];
|
user: DashboardShellProps["user"];
|
||||||
onNavigate?: () => void;
|
onNavigate?: () => void;
|
||||||
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
className="flex h-dvh max-h-dvh flex-col overflow-hidden rounded-none border-0"
|
className={cn("flex h-dvh max-h-dvh flex-col overflow-hidden rounded-none border-0", className)}
|
||||||
>
|
>
|
||||||
<SidebarHeader className="shrink-0 border-b-0 px-6 py-3">
|
<SidebarHeader className="shrink-0 border-b-0 px-6 py-3">
|
||||||
<Link href="/" className="flex w-full items-center justify-center">
|
<Link href="/" className="flex w-full items-center justify-center">
|
||||||
@@ -196,7 +197,7 @@ function AppSidebar({
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align="end"
|
align="end"
|
||||||
side="right"
|
side="top"
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
className="w-64"
|
className="w-64"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -49,9 +49,12 @@ export function PortalShell({ children, user, progress }: PortalShellProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-background text-foreground">
|
<div className="min-h-screen bg-background text-foreground">
|
||||||
<div className="flex min-h-screen">
|
<div className="flex min-h-screen">
|
||||||
<aside className="sticky top-0 hidden h-dvh shrink-0 self-start border-r border-border bg-background lg:block">
|
<AppSidebar
|
||||||
<AppSidebar pathname={pathname} user={user} progress={progress} />
|
pathname={pathname}
|
||||||
</aside>
|
user={user}
|
||||||
|
progress={progress}
|
||||||
|
className="sticky top-0 hidden shrink-0 self-start lg:block"
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Mobile Sidebar Overlay */}
|
{/* Mobile Sidebar Overlay */}
|
||||||
{isMobileSidebarOpen && (
|
{isMobileSidebarOpen && (
|
||||||
@@ -62,18 +65,15 @@ export function PortalShell({ children, user, progress }: PortalShellProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Mobile Sidebar Drawer */}
|
{/* Mobile Sidebar Drawer */}
|
||||||
<aside
|
<AppSidebar
|
||||||
className={`fixed inset-y-0 left-0 z-50 w-72 transform border-r border-border bg-background transition-transform duration-300 ease-in-out lg:hidden ${
|
pathname={pathname}
|
||||||
|
user={user}
|
||||||
|
progress={progress}
|
||||||
|
onNavigate={() => setIsMobileSidebarOpen(false)}
|
||||||
|
className={`fixed inset-y-0 left-0 z-50 transform transition-transform duration-300 ease-in-out lg:hidden ${
|
||||||
isMobileSidebarOpen ? "translate-x-0" : "-translate-x-full"
|
isMobileSidebarOpen ? "translate-x-0" : "-translate-x-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
/>
|
||||||
<AppSidebar
|
|
||||||
pathname={pathname}
|
|
||||||
user={user}
|
|
||||||
progress={progress}
|
|
||||||
onNavigate={() => setIsMobileSidebarOpen(false)}
|
|
||||||
/>
|
|
||||||
</aside>
|
|
||||||
|
|
||||||
<div className="flex flex-1 flex-col min-w-0">
|
<div className="flex flex-1 flex-col min-w-0">
|
||||||
<header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-border bg-background/95 px-4 backdrop-blur lg:hidden">
|
<header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-border bg-background/95 px-4 backdrop-blur lg:hidden">
|
||||||
@@ -112,16 +112,18 @@ function AppSidebar({
|
|||||||
user,
|
user,
|
||||||
progress = 0,
|
progress = 0,
|
||||||
onNavigate,
|
onNavigate,
|
||||||
|
className,
|
||||||
}: {
|
}: {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
user: PortalShellProps["user"];
|
user: PortalShellProps["user"];
|
||||||
progress?: number;
|
progress?: number;
|
||||||
onNavigate?: () => void;
|
onNavigate?: () => void;
|
||||||
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Sidebar
|
<Sidebar
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
className="flex h-dvh max-h-dvh flex-col overflow-hidden rounded-none border-0"
|
className={cn("flex h-dvh max-h-dvh flex-col overflow-hidden rounded-none border-0", className)}
|
||||||
>
|
>
|
||||||
<SidebarHeader className="shrink-0 border-b-0 px-6 py-3">
|
<SidebarHeader className="shrink-0 border-b-0 px-6 py-3">
|
||||||
<Link href="/portal" className="flex w-full items-center justify-center">
|
<Link href="/portal" className="flex w-full items-center justify-center">
|
||||||
@@ -222,7 +224,7 @@ function AppSidebar({
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
align="end"
|
align="end"
|
||||||
side="right"
|
side="top"
|
||||||
sideOffset={8}
|
sideOffset={8}
|
||||||
className="w-64"
|
className="w-64"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user