From f29b161cf4f33cbb3b66e1112e155eed170b281e Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:41:19 +0400 Subject: [PATCH] feat: profile sorting --- src/components/profile-data-table.tsx | 101 +++++++++++++++++++++---- src/components/profile-info-dialog.tsx | 11 +++ src/i18n/locales/en.json | 12 ++- src/i18n/locales/es.json | 12 ++- src/i18n/locales/fr.json | 12 ++- src/i18n/locales/ja.json | 12 ++- src/i18n/locales/ko.json | 12 ++- src/i18n/locales/pt.json | 12 ++- src/i18n/locales/ru.json | 12 ++- src/i18n/locales/vi.json | 12 ++- src/i18n/locales/zh.json | 12 ++- src/types.ts | 3 + 12 files changed, 189 insertions(+), 34 deletions(-) diff --git a/src/components/profile-data-table.tsx b/src/components/profile-data-table.tsx index 00521bb..2ff9e06 100644 --- a/src/components/profile-data-table.tsx +++ b/src/components/profile-data-table.tsx @@ -51,6 +51,12 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Popover, PopoverContent, @@ -2381,28 +2387,89 @@ export function ProfilesDataTable({ ); }, }, + { + // Hidden, sort-only column so profiles can be sorted by creation date + // without showing a Created column in the table (issue #454). Kept + // hidden via columnVisibility; sorting still works on hidden columns. + id: "created_at", + accessorFn: (row) => row.created_at ?? 0, + enableSorting: true, + enableHiding: true, + sortingFn: "basic", + header: () => null, + cell: () => null, + }, { accessorKey: "name", // The only column without a fixed width: table-fixed hands it all // remaining space as the window grows or shrinks. meta: { flexWidth: true }, - header: ({ column, table }) => { + // The Name header doubles as the sort control: clicking opens a menu to + // sort by name (A–Z / Z–A) or by creation date (newest / oldest), so + // creation-date sorting needs no visible column. + header: ({ table }) => { const meta = table.options.meta as TableMeta; + const sort = table.getState().sorting[0]; + const isActive = (id: string, desc: boolean) => + sort?.id === id && !!sort.desc === desc; return ( - + + + + + + + table.setSorting([{ id: "name", desc: false }]) + } + > + {isActive("name", false) && ( + + )} + {meta.t("profiles.sort.nameAsc")} + + table.setSorting([{ id: "name", desc: true }])} + > + {isActive("name", true) && ( + + )} + {meta.t("profiles.sort.nameDesc")} + + + table.setSorting([{ id: "created_at", desc: true }]) + } + > + {isActive("created_at", true) && ( + + )} + {meta.t("profiles.sort.newest")} + + + table.setSorting([{ id: "created_at", desc: false }]) + } + > + {isActive("created_at", false) && ( + + )} + {meta.t("profiles.sort.oldest")} + + + ); }, enableSorting: true, @@ -2934,7 +3001,7 @@ export function ProfilesDataTable({ // expendable first); their data stays reachable via the profile info // dialog. Visibility (not CSS hiding) so table-fixed reclaims the width. const [columnVisibility, setColumnVisibility] = - React.useState({}); + React.useState({ created_at: false }); // Content columns grow proportionally with the container but never drop // below the compact-layout floor; the name column takes the remainder. @@ -2994,6 +3061,8 @@ export function ProfilesDataTable({ setContainerWidth(Math.round(w / 8) * 8); setColumnVisibility((prev) => { const next: VisibilityState = { + // Always hidden — sort-only column (issue #454). + created_at: false, dns: w >= 768, ext: w >= 672, note: w >= 576, diff --git a/src/components/profile-info-dialog.tsx b/src/components/profile-info-dialog.tsx index 9bebda5..d756f19 100644 --- a/src/components/profile-info-dialog.tsx +++ b/src/components/profile-info-dialog.tsx @@ -878,6 +878,17 @@ function ProfileInfoLayout({ {t("profileInfo.sections.activity")}
+