diff --git a/public/images/profile-empty-comments.png b/public/images/profile-empty-comments.png new file mode 100644 index 0000000..490c170 Binary files /dev/null and b/public/images/profile-empty-comments.png differ diff --git a/public/images/profile-empty-likes.png b/public/images/profile-empty-likes.png new file mode 100644 index 0000000..b2b9439 Binary files /dev/null and b/public/images/profile-empty-likes.png differ diff --git a/public/images/profile-empty-payments.png b/public/images/profile-empty-payments.png new file mode 100644 index 0000000..2a2911e Binary files /dev/null and b/public/images/profile-empty-payments.png differ diff --git a/public/images/profile-empty-saved.png b/public/images/profile-empty-saved.png new file mode 100644 index 0000000..7db391f Binary files /dev/null and b/public/images/profile-empty-saved.png differ diff --git a/src/lib/api.ts b/src/lib/api.ts index 8258911..c61edf0 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -723,15 +723,15 @@ class ApiClient { return this.request('/api/blog/me/activity'); } - async getMyBlogPostsPage(section: "liked" | "saved", page: number) { + async getMyBlogPostsPage(section: "liked" | "saved", page: number, sortOrder: "asc" | "desc") { return this.request>( - `/api/blog/me/activity/${section}?page=${page}`, + `/api/blog/me/activity/${section}?page=${page}&sort_order=${sortOrder}`, ); } - async getMyBlogCommentsPage(page: number) { + async getMyBlogCommentsPage(page: number, sortOrder: "asc" | "desc") { return this.request>( - `/api/blog/me/activity/comments?page=${page}`, + `/api/blog/me/activity/comments?page=${page}&sort_order=${sortOrder}`, ); } @@ -1065,9 +1065,20 @@ class ApiClient { return this.request("/api/payments/me", { method: "GET" }); } - async getMyPaymentsPage(page: number) { + async getMyPaymentsPage( + status: "all" | Types.UserPaymentSchema["status"], + sortBy: "time" | "amount", + sortOrder: "asc" | "desc", + page: number, + ) { + const params = new URLSearchParams({ + status, + sort_by: sortBy, + sort_order: sortOrder, + page: String(page), + }); return this.request>( - `/api/payments/me/paginated?page=${page}`, + `/api/payments/me/paginated?${params.toString()}`, { method: "GET" }, ); } diff --git a/src/views/Profile.tsx b/src/views/Profile.tsx index 6f1eaf5..73c3212 100644 --- a/src/views/Profile.tsx +++ b/src/views/Profile.tsx @@ -8,18 +8,19 @@ import { ArrowDownWideNarrow, ChevronLeft, ChevronRight, - Bookmark, CalendarDays, + CheckCircle2, CreditCard, + Clock3, Eye, EyeOff, ExternalLink, Heart, - MessageSquareText, PencilLine, Share2, SlidersHorizontal, UserRound, + XCircle, } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; @@ -45,6 +46,11 @@ type EventSortBy = "start_time" | "title"; type EventSortOrder = "asc" | "desc"; type EventSortSelection = `${EventSortBy}:${EventSortOrder}`; type BlogSection = "liked" | "saved" | "comments"; +type BlogSortOrder = "asc" | "desc"; +type PaymentStatus = Types.UserPaymentSchema["status"] | "all"; +type PaymentSortBy = "time" | "amount"; +type PaymentSortOrder = "asc" | "desc"; +type PaymentSortSelection = `${PaymentSortBy}:${PaymentSortOrder}`; function readProfileSection(value: string | null): ProfileSection { return value === "events" || value === "blog" || value === "payments" ? value : "overview"; @@ -87,6 +93,33 @@ const paymentStatusLabels: Record = { canceled: "لغو شده", }; +const paymentStatusItems: Array<{ id: PaymentStatus; label: string }> = [ + { id: "all", label: "همه وضعیت‌ها" }, + { id: "paid", label: paymentStatusLabels.paid }, + { id: "pending", label: paymentStatusLabels.pending }, + { id: "initiated", label: paymentStatusLabels.initiated }, + { id: "failed", label: paymentStatusLabels.failed }, + { id: "canceled", label: paymentStatusLabels.canceled }, +]; + +const paymentSortItems: Array<{ id: PaymentSortSelection; label: string; sortBy: PaymentSortBy; sortOrder: PaymentSortOrder }> = [ + { id: "time:desc", label: "زمان: جدیدترین", sortBy: "time", sortOrder: "desc" }, + { id: "time:asc", label: "زمان: قدیمی‌ترین", sortBy: "time", sortOrder: "asc" }, + { id: "amount:desc", label: "مبلغ: بیشترین", sortBy: "amount", sortOrder: "desc" }, + { id: "amount:asc", label: "مبلغ: کمترین", sortBy: "amount", sortOrder: "asc" }, +]; + +const blogSectionItems: Array<{ id: BlogSection; label: string }> = [ + { id: "liked", label: "پسندیده‌ها" }, + { id: "saved", label: "ذخیره‌شده‌ها" }, + { id: "comments", label: "دیدگاه‌ها" }, +]; + +const blogSortItems: Array<{ id: BlogSortOrder; label: string }> = [ + { id: "desc", label: "زمان: جدیدترین" }, + { id: "asc", label: "زمان: قدیمی‌ترین" }, +]; + const refundStatusLabels: Record, string> = { none: "بدون بازگشت وجه", pending: "بازگشت وجه در حال پیگیری", @@ -101,21 +134,30 @@ const sectionItems: Array<{ id: ProfileSection; label: string; icon: typeof Acti { id: "payments", label: "پرداخت‌ها", icon: CreditCard }, ]; -function InfoLine({ label, children }: { label: string; children: React.ReactNode }) { +function InfoLine({ label, children, stacked = false }: { label: string; children: React.ReactNode; stacked?: boolean }) { return ( -
+
{label} - {children || "—"} + {children || "—"}
); } -function EmptyState({ title, description }: { title: string; description: string }) { +function EmptyState({ title, description, imageSrc }: { title: string; description: string; imageSrc?: string }) { return ( -
-