feat: add dashboard client components for finance, journal, projects, tasks, analytics, calendar, clients, and main overview
This commit is contained in:
@@ -58,8 +58,8 @@ export function AnalyticsClient({ data }: AnalyticsClientProps) {
|
||||
const projectIncomeData = Array.from(projectIncomeMap.values());
|
||||
|
||||
// Task completion stats
|
||||
const completedTasks = filteredTasks.filter(t => t.status === "completed").length;
|
||||
const activeTasks = filteredTasks.filter(t => t.status !== "completed" && t.status !== "cancelled").length;
|
||||
const completedTasks = filteredTasks.filter(t => t.status === "done").length;
|
||||
const activeTasks = filteredTasks.filter(t => t.status !== "done" && t.status !== "cancelled").length;
|
||||
|
||||
const taskStatusData = [
|
||||
{ name: "Tamamlanan", value: completedTasks },
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
toast,
|
||||
} from "poyraz-ui/molecules";
|
||||
import { CalendarDays, Clock, Pencil, Plus, Trash2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
@@ -293,6 +294,13 @@ function CalendarEventDialog({
|
||||
try {
|
||||
await action(formData);
|
||||
setOpen(false);
|
||||
toast.success(mode === "create" ? "Etkinlik eklendi." : "Etkinlik güncellendi.");
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Etkinlik kaydedilirken beklenmeyen bir hata oluştu.",
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -306,20 +314,21 @@ function CalendarEventDialog({
|
||||
{mode === "create" ? "Etkinlik ekle" : "Düzenle"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-[min(680px,calc(100dvh-6rem))] overflow-hidden sm:max-w-xl">
|
||||
<form action={handleSubmit} className="flex max-h-[min(640px,calc(100dvh-9rem))] flex-col">
|
||||
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-h-[min(680px,calc(100dvh-4rem))] sm:max-w-xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
{event ? <input type="hidden" name="id" value={event.id} /> : null}
|
||||
<DialogHeader className="shrink-0 pb-5">
|
||||
<DialogHeader className="shrink-0 px-5 pb-4 pt-5 pr-12">
|
||||
<DialogTitle>{mode === "create" ? "Yeni etkinlik" : "Etkinliği düzenle"}</DialogTitle>
|
||||
<DialogDescription>Takvim etkinliğini proje, görev veya müşteriyle ilişkilendir.</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto pr-2">
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto px-5 pb-5">
|
||||
<EventFormFields event={event} defaultDate={defaultDate} clients={clients} projects={projects} tasks={tasks} />
|
||||
</div>
|
||||
|
||||
<DialogFooter className="shrink-0 border-t border-border pt-5">
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
<DialogFooter className="shrink-0 border-t border-border bg-background p-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full gap-2 sm:w-auto">
|
||||
{mode === "create" ? <Plus className="h-4 w-4" /> : <Pencil className="h-4 w-4" />}
|
||||
{isSubmitting ? "Kaydediliyor" : mode === "create" ? "Etkinliği ekle" : "Değişiklikleri kaydet"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
TabsContent,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
toast,
|
||||
} from "poyraz-ui/molecules";
|
||||
import {
|
||||
Archive,
|
||||
@@ -133,8 +134,14 @@ export function ClientsClient({
|
||||
|
||||
try {
|
||||
await updateClientPipelineStage(clientId, newStage as any);
|
||||
toast.success("Müşteri aşaması güncellendi.");
|
||||
} catch (error) {
|
||||
setLocalClients(clients);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Müşteri aşaması güncellenemedi.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +447,13 @@ function ClientDialog({
|
||||
try {
|
||||
await action(formData);
|
||||
setOpen(false);
|
||||
toast.success(mode === "create" ? "Müşteri eklendi." : "Müşteri güncellendi.");
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Müşteri kaydedilirken beklenmeyen bir hata oluştu.",
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -458,10 +472,10 @@ function ClientDialog({
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-xl max-h-[90vh] overflow-y-auto">
|
||||
<form action={handleSubmit} className="space-y-5">
|
||||
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-h-[min(680px,calc(100dvh-4rem))] sm:max-w-xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
{client ? <input type="hidden" name="id" value={client.id} /> : null}
|
||||
<DialogHeader>
|
||||
<DialogHeader className="shrink-0 px-5 pb-4 pt-5 pr-12">
|
||||
<DialogTitle>
|
||||
{mode === "create" ? "Yeni müşteri" : "Müşteriyi düzenle"}
|
||||
</DialogTitle>
|
||||
@@ -470,10 +484,12 @@ function ClientDialog({
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto px-5 pb-5">
|
||||
<ClientFormFields client={client} />
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="submit" disabled={isSubmitting} className="gap-2">
|
||||
<DialogFooter className="shrink-0 border-t border-border bg-background p-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full gap-2 sm:w-auto">
|
||||
{mode === "create" ? <Plus className="h-4 w-4" /> : <Pencil className="h-4 w-4" />}
|
||||
{isSubmitting
|
||||
? "Kaydediliyor"
|
||||
|
||||
@@ -50,7 +50,7 @@ export function DashboardClient({ data }: DashboardClientProps) {
|
||||
|
||||
const activeProjectsCount = (data.projects || []).filter(p => p.status === "active").length;
|
||||
|
||||
const completedTasksCount = (data.tasks || []).filter(t => t.status === "completed" && filterByDate(t.updated_at || t.created_at)).length;
|
||||
const completedTasksCount = (data.tasks || []).filter(t => t.status === "done" && filterByDate(t.updated_at || t.created_at)).length;
|
||||
|
||||
const filteredLogs = (data.logs || []).filter(l => filterByDate(l.log_date));
|
||||
const avgMood = filteredLogs.length > 0
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
toast,
|
||||
} from "poyraz-ui/molecules";
|
||||
import {
|
||||
ArrowDownRight,
|
||||
@@ -300,6 +301,13 @@ function FinanceDialog({
|
||||
try {
|
||||
await action(formData);
|
||||
setOpen(false);
|
||||
toast.success(mode === "create" ? "İşlem eklendi." : "İşlem güncellendi.");
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Finans işlemi kaydedilirken beklenmeyen bir hata oluştu.",
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -313,18 +321,19 @@ function FinanceDialog({
|
||||
{mode === "create" ? "İşlem ekle" : "Düzenle"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-[min(680px,calc(100dvh-6rem))] overflow-hidden sm:max-w-xl">
|
||||
<form action={handleSubmit} className="flex max-h-[min(640px,calc(100dvh-9rem))] flex-col">
|
||||
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-h-[min(680px,calc(100dvh-4rem))] sm:max-w-xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
{transaction ? <input type="hidden" name="id" value={transaction.id} /> : null}
|
||||
<DialogHeader className="shrink-0 pb-5">
|
||||
<DialogHeader className="shrink-0 px-5 pb-4 pt-5 pr-12">
|
||||
<DialogTitle>{mode === "create" ? "Yeni finans işlemi" : "Finans işlemini düzenle"}</DialogTitle>
|
||||
<DialogDescription>Gelir veya gider kaydını müşteri/proje bağlantısıyla kaydet.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto pr-2">
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto px-5 pb-5">
|
||||
<FinanceFormFields transaction={transaction} clients={clients} projects={projects} />
|
||||
</div>
|
||||
<DialogFooter className="shrink-0 border-t border-border pt-5">
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
<DialogFooter className="shrink-0 border-t border-border bg-background p-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full gap-2 sm:w-auto">
|
||||
{mode === "create" ? <Plus className="h-4 w-4" /> : <Pencil className="h-4 w-4" />}
|
||||
{isSubmitting ? "Kaydediliyor" : mode === "create" ? "İşlemi ekle" : "Değişiklikleri kaydet"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
toast,
|
||||
} from "poyraz-ui/molecules";
|
||||
import {
|
||||
Activity,
|
||||
@@ -259,6 +260,13 @@ function DailyLogDialog({ mode, log }: { mode: "create" | "edit"; log?: DailyLog
|
||||
try {
|
||||
await action(formData);
|
||||
setOpen(false);
|
||||
toast.success(mode === "create" ? "Günlük eklendi." : "Günlük güncellendi.");
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Günlük kaydedilirken beklenmeyen bir hata oluştu.",
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -272,22 +280,22 @@ function DailyLogDialog({ mode, log }: { mode: "create" | "edit"; log?: DailyLog
|
||||
{mode === "create" ? "Günlük ekle" : "Düzenle"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-[min(640px,calc(100dvh-6rem))] overflow-hidden sm:max-w-xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex max-h-[min(600px,calc(100dvh-9rem))] flex-col">
|
||||
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-h-[min(680px,calc(100dvh-4rem))] sm:max-w-xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
{log ? <input type="hidden" name="id" value={log.id} /> : null}
|
||||
<DialogHeader className="shrink-0 pb-5">
|
||||
<DialogHeader className="shrink-0 px-5 pb-4 pt-5 pr-12">
|
||||
<DialogTitle>{mode === "create" ? "Yeni günlük kayıt" : "Günlük kaydı düzenle"}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Günün mood, enerji ve çalışma memnuniyeti skorlarını kaydet.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto pr-2">
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto px-5 pb-5">
|
||||
<DailyLogFormFields log={log} />
|
||||
</div>
|
||||
|
||||
<DialogFooter className="shrink-0 border-t border-border pt-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="gap-2">
|
||||
<DialogFooter className="shrink-0 border-t border-border bg-background p-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full gap-2 sm:w-auto">
|
||||
{isSubmitting ? "Kaydediliyor" : mode === "create" ? "Kaydı ekle" : "Değişiklikleri kaydet"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@@ -448,17 +448,17 @@ function ProjectDialog({
|
||||
{iconOnly ? null : mode === "create" ? "Proje ekle" : "Düzenle"}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-[min(720px,calc(100dvh-6rem))] overflow-hidden sm:max-w-2xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex max-h-[min(680px,calc(100dvh-9rem))] flex-col">
|
||||
<DialogContent className="flex max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] flex-col overflow-hidden p-0 sm:max-h-[min(680px,calc(100dvh-4rem))] sm:max-w-2xl data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95">
|
||||
<form action={handleSubmit} className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
{project ? <input type="hidden" name="id" value={project.id} /> : null}
|
||||
<DialogHeader className="shrink-0 pb-5">
|
||||
<DialogHeader className="shrink-0 px-5 pb-4 pt-5 pr-12">
|
||||
<DialogTitle>{mode === "create" ? "Yeni proje" : "Projeyi düzenle"}</DialogTitle>
|
||||
<DialogDescription>
|
||||
Müşteri projelerini ve kişisel side projectleri aynı modelde takip et.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto pr-2">
|
||||
<div className="tiny-scrollbar min-h-0 flex-1 overflow-y-auto px-5 pb-5">
|
||||
<ProjectFormFields
|
||||
project={project}
|
||||
clients={clients}
|
||||
@@ -467,8 +467,8 @@ function ProjectDialog({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="shrink-0 border-t border-border pt-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="gap-2">
|
||||
<DialogFooter className="shrink-0 border-t border-border bg-background p-5">
|
||||
<Button type="submit" disabled={isSubmitting} className="w-full gap-2 sm:w-auto">
|
||||
{mode === "create" ? <Plus className="h-4 w-4" /> : <Pencil className="h-4 w-4" />}
|
||||
{isSubmitting
|
||||
? "Kaydediliyor"
|
||||
|
||||
@@ -551,6 +551,13 @@ function TaskDialog({
|
||||
try {
|
||||
await action(formData);
|
||||
setOpen(false);
|
||||
toast.success(mode === "create" ? "Görev eklendi." : "Görev güncellendi.");
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Görev kaydedilirken beklenmeyen bir hata oluştu.",
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user