From efd06c720e4a523e92c795450a8583430e0436be Mon Sep 17 00:00:00 2001 From: d-kimsuon Date: Sat, 30 Aug 2025 15:16:53 +0900 Subject: [PATCH] feat: add filter option only having user messages --- package.json | 2 + pnpm-lock.yaml | 73 +++++++++++++++++++ .../[projectId]/components/ProjectPage.tsx | 48 ++++++++++-- .../conversationList/ConversationItem.tsx | 6 +- .../projects/[projectId]/store/filterAtoms.ts | 24 ++++++ src/components/ui/checkbox.tsx | 32 ++++++++ src/server/service/session/getSessionMeta.ts | 29 +++++--- 7 files changed, 195 insertions(+), 19 deletions(-) create mode 100644 src/app/projects/[projectId]/store/filterAtoms.ts create mode 100644 src/components/ui/checkbox.tsx diff --git a/package.json b/package.json index b08f1e5..4273256 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@hono/auth-js": "^1.1.0", "@hono/zod-validator": "^0.7.2", "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-hover-card": "^1.1.15", @@ -31,6 +32,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "hono": "^4.9.5", + "jotai": "^2.13.1", "lucide-react": "^0.542.0", "next": "15.5.2", "react": "^19.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8898c7e..f173efb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: '@radix-ui/react-avatar': specifier: ^1.1.10 version: 1.1.10(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-checkbox': + specifier: ^1.3.3 + version: 1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@radix-ui/react-collapsible': specifier: ^1.1.12 version: 1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -44,6 +47,9 @@ importers: hono: specifier: ^4.9.5 version: 4.9.5 + jotai: + specifier: ^2.13.1 + version: 2.13.1(@types/react@19.1.12)(react@19.1.1) lucide-react: specifier: ^0.542.0 version: 0.542.0(react@19.1.1) @@ -611,6 +617,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-collapsible@1.1.12': resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} peerDependencies: @@ -827,6 +846,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.1.1': resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: @@ -1403,6 +1431,24 @@ packages: jose@6.1.0: resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==} + jotai@2.13.1: + resolution: {integrity: sha512-cRsw6kFeGC9Z/D3egVKrTXRweycZ4z/k7i2MrfCzPYsL9SIWcPXTyqv258/+Ay8VUEcihNiE/coBLE6Kic6b8A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@babel/core': '>=7.0.0' + '@babel/template': '>=7.0.0' + '@types/react': '>=17.0.0' + react: '>=17.0.0' + peerDependenciesMeta: + '@babel/core': + optional: true + '@babel/template': + optional: true + '@types/react': + optional: true + react: + optional: true + js-tokens@9.0.1: resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} @@ -2456,6 +2502,22 @@ snapshots: '@types/react': 19.1.12 '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.12)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.12)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.12 + '@types/react-dom': 19.1.9(@types/react@19.1.12) + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.1.9(@types/react@19.1.12))(@types/react@19.1.12)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -2655,6 +2717,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.12 + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.12)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.12 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.12)(react@19.1.1)': dependencies: '@radix-ui/rect': 1.1.1 @@ -3171,6 +3239,11 @@ snapshots: jose@6.1.0: {} + jotai@2.13.1(@types/react@19.1.12)(react@19.1.1): + optionalDependencies: + '@types/react': 19.1.12 + react: 19.1.1 + js-tokens@9.0.1: {} json-parse-even-better-errors@4.0.0: {} diff --git a/src/app/projects/[projectId]/components/ProjectPage.tsx b/src/app/projects/[projectId]/components/ProjectPage.tsx index 081e677..a59314b 100644 --- a/src/app/projects/[projectId]/components/ProjectPage.tsx +++ b/src/app/projects/[projectId]/components/ProjectPage.tsx @@ -1,7 +1,9 @@ "use client"; +import { useAtom } from "jotai"; import { ArrowLeftIcon, FolderIcon, MessageSquareIcon } from "lucide-react"; import Link from "next/link"; +import { useId } from "react"; import { Button } from "@/components/ui/button"; import { Card, @@ -10,14 +12,24 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; +import { Checkbox } from "@/components/ui/checkbox"; +import { hideSessionsWithoutUserMessagesAtom } from "../store/filterAtoms"; import { pagesPath } from "../../../../lib/$path"; import { useProject } from "../hooks/useProject"; import { firstCommandToTitle } from "../services/firstCommandToTitle"; export const ProjectPageContent = ({ projectId }: { projectId: string }) => { + const checkboxId = useId(); const { data: { project, sessions }, } = useProject(projectId); + const [hideSessionsWithoutUserMessages, setHideSessionsWithoutUserMessages] = + useAtom(hideSessionsWithoutUserMessagesAtom); + + // Apply filtering + const filteredSessions = hideSessionsWithoutUserMessages + ? sessions.filter((session) => session.meta.firstCommand !== null) + : sessions; return (
@@ -47,10 +59,36 @@ export const ProjectPageContent = ({ projectId }: { projectId: string }) => {

Conversation Sessions{" "} - {project.meta.sessionCount ? `(${project.meta.sessionCount})` : ""} + {filteredSessions.length > 0 ? `(${filteredSessions.length})` : ""} + {hideSessionsWithoutUserMessages && + filteredSessions.length !== sessions.length && ( + + of {sessions.length} total + + )}

- {sessions.length === 0 ? ( + {/* Filter Controls */} +
+
+ + +
+

+ Only show sessions that contain user commands or messages +

+
+ + {filteredSessions.length === 0 ? ( @@ -64,7 +102,7 @@ export const ProjectPageContent = ({ projectId }: { projectId: string }) => { ) : (
- {sessions.map((session) => ( + {filteredSessions.map((session) => ( { Last modified:{" "} {session.meta.lastModifiedAt ? new Date( - session.meta.lastModifiedAt, + session.meta.lastModifiedAt ).toLocaleDateString() : ""}

@@ -101,7 +139,7 @@ export const ProjectPageContent = ({ projectId }: { projectId: string }) => {