Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,9 @@ coverage/

# Vitest
.vitest/

# worktrees
.claude/worktrees
.opencode/worktrees
.cursor/worktrees
.vscode/worktrees
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit "${1}"
7 changes: 7 additions & 0 deletions .sisyphus/boulder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"active_plan": "/Users/liangshiquan/sqliang-github/full-stack-workplace/react-playround/.sisyphus/plans/git-worktree-commit.md",
"started_at": "2026-03-02T15:19:17Z",
"session_ids": [],
"plan_name": "git-worktree-commit",
"worktree_path": "/Users/liangshiquan/.cursor/worktrees/react-playround/uip"
}
124 changes: 124 additions & 0 deletions .sisyphus/plans/git-worktree-commit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# 代码提交计划 - react-playround/uip worktree

## 任务概述

使用 git-workflow 流程将 worktree 中的代码变更进行本地提交。

## 当前状态

| 项目 | 值 |
|------|-----|
| Worktree 路径 | /Users/liangshiquan/.cursor/worktrees/react-playround/uip |
| 当前分支 | feat/bookkeeping |
| 提交类型 | 本地提交 (不推送到远程) |

## 提交信息

### 提交消息 (Conventional Commits)

feat(bookkeeping): add bookkeeping feature with record management

Add complete bookkeeping module with:
- AddRecordForm for creating new financial records
- RecordTable for displaying and managing records
- SummaryCards for financial overview
- Full test coverage for all components
- Update App routing and navigation to include bookkeeping page

### 提交范围

将提交以下文件:

已跟踪文件的修改:
- package.json - 依赖更新
- pnpm-lock.yaml - 锁文件更新
- src/App.tsx - 路由配置更新
- src/config/navigation.tsx - 导航配置更新

新增目录/文件:
- src/pages/Bookkeeping/ - 完整的新功能模块
- AddRecordForm.tsx
- AddRecordForm.test.tsx
- RecordTable.tsx
- RecordTable.test.tsx
- SummaryCards.tsx
- SummaryCards.test.tsx
- index.tsx
- index.test.tsx
- types.ts
- README.md

### 不提交的文件

- .pnpm-store/ - pnpm 缓存目录 (已在 .gitignore 中)

## 执行步骤

### 步骤 0: 验证 Husky Hooks 状态

检查当前 hooks 配置状态:

git -C /Users/liangshiquan/.cursor/worktrees/react-playround/uip ls-hooks

当前状态分析:
- .husky/pre-commit 已存在 (运行 lint-staged)
- .husky/commit-msg 不存在 (commitlint 不会在提交时检查)

### 步骤 1: 创建 commit-msg Hook

创建 commit-msg hook 以启用 commitlint 验证:

cat > /Users/liangshiquan/.cursor/worktrees/react-playround/uip/.husky/commit-msg << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit "${1}"
EOF

chmod +x /Users/liangshiquan/.cursor/worktrees/react-playround/uip/.husky/commit-msg

### 步骤 2: 添加所有变更到暂存区

git -C /Users/liangshiquan/.cursor/worktrees/react-playround/uip add -A

### 步骤 3: 验证暂存内容

git -C /Users/liangshiquan/.cursor/worktrees/react-playround/uip status

预期输出:所有变更已暂存,无未跟踪文件(除 .pnpm-store/)

### 步骤 4: 执行提交

git -C /Users/liangshiquan/.cursor/worktrees/react-playround/uip commit -m "feat(bookkeeping): add bookkeeping feature with record management

Add complete bookkeeping module with:
- AddRecordForm for creating new financial records
- RecordTable for displaying and managing records
- SummaryCards for financial overview
- Full test coverage for all components
- Update App routing and navigation to include bookkeeping page"

此时 commit-msg hook 会自动运行 commitlint 验证提交消息

### 步骤 5: 验证提交结果

git -C /Users/liangshiquan/.cursor/worktrees/react-playround/uip log -1 --stat

预期输出:显示最新的提交包含所有预期文件

## 验证清单

- [ ] commit-msg hook 已创建并有执行权限
- [ ] 所有代码变更已暂存
- [ ] 提交消息符合 Conventional Commits 格式
- [ ] commitlint 验证通过
- [ ] 提交成功无错误
- [ ] git status 显示工作区干净
- [ ] git log 显示新提交在分支顶部

## 注意事项

1. 不推送: 用户明确要求仅本地提交,不推送到远程仓库
2. 单一提交: 所有变更合并为一个提交
3. 使用 git-workflow 流程: 遵循 Conventional Commits 规范
4. Hook 验证: commit-msg hook 会在提交时验证消息格式
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@ant-design/icons": "^6.1.0",
"@rsbuild/plugin-sass": "^1.4.0",
"antd": "^6.1.0",
"dayjs": "^1.11.19",
"graphql": "^16.9.0",
"react": "^19.2.1",
"react-dom": "^19.2.1",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 46 additions & 15 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import Posts from './pages/Posts';
import { ShoppingCart } from './pages/ShoppingCart';
import RelayExample from './pages/RelayExample';
import Todo from './pages/Todo';
import Bookkeeping from './pages/Bookkeeping';
import AppExample from './pages/AppExample';
import './index.css';

import { Loading } from './components/Loading';
Expand All @@ -27,11 +29,38 @@ import {

import type { MenuProps } from 'antd';

// 获取匹配路径
const matchActiveNavKey = (navKeys: string[], pathname: string) => {
if (!navKeys.length) {
return '';
}
return navKeys
.filter(key => {
if (key === '/') {
return pathname === '/';
}
// 匹配路径是否完全相等或以指定路径开头
return pathname === key || pathname.startsWith(key + '/');
})
// 按路径长度降序排序,返回最长的匹配路径
// 如果没有任何匹配,则返回空字符串
.sort((a, b) => b.length - a.length)[0] || '';
};

const AppContent = () => {
const location = useLocation();
const [collapsed, setCollapsed] = useState(false);

const secondaryNavItems = sideNavItems[location.pathname] || [];
// 获取主导航的匹配路径
const activeMainNavKey = matchActiveNavKey(
mainNavItems.map(item => item.key as string),
location.pathname
) || location.pathname;

// 获取二级导航的匹配路径
const matchingKey = matchActiveNavKey(Object.keys(sideNavItems), location.pathname);
// 获取二级导航的菜单项
const secondaryNavItems = matchingKey ? sideNavItems[matchingKey] : [];

return (
<Suspense fallback={<Loading />}>
Expand All @@ -52,17 +81,17 @@ const AppContent = () => {
<p className="text-xs text-gray-400 -mt-1">Build & Experiment</p>
</div>
</div>

{/* Main navigation */}
<Menu
mode="horizontal"
selectedKeys={[location.pathname]}
selectedKeys={[activeMainNavKey]}
items={mainNavItems as MenuProps['items']}
className="border-0 bg-transparent flex-1 justify-end min-w-0"
style={{ background: 'transparent' }}
/>
</Header>

<Layout className="mt-0">
{/* Sidebar with improved styling */}
<Sider
Expand All @@ -83,7 +112,7 @@ const AppContent = () => {
</h2>
</div>
)}

{/* Sidebar menu */}
<div className="flex-1 overflow-auto py-2">
<Menu
Expand All @@ -94,24 +123,24 @@ const AppContent = () => {
defaultOpenKeys={['/']}
/>
</div>

{/* Collapse toggle button */}
<div
<div
className="p-3 border-t border-gray-100 flex justify-center cursor-pointer hover:bg-gray-50 transition-colors"
onClick={() => setCollapsed(!collapsed)}
>
<svg
className={`w-5 h-5 text-gray-400 transition-transform duration-300 ${collapsed ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
<svg
className={`w-5 h-5 text-gray-400 transition-transform duration-300 ${collapsed ? 'rotate-180' : ''}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
</div>
</div>
</Sider>

{/* Main content area */}
<Content className="m-4 p-6 bg-white/60 backdrop-blur-sm rounded-2xl shadow-sm border border-gray-100 min-h-[calc(100vh-8rem)]">
<div className="h-full">
Expand All @@ -120,9 +149,11 @@ const AppContent = () => {
<Routes>
<Route path="/" element={<Home />} />
<Route path="/hooks" element={<Posts />} />
<Route path="/shopping-cart" element={<ShoppingCart />} />
<Route path="/relay-example" element={<RelayExample />} />
<Route path="/todo" element={<Todo />} />
<Route path="/app-example" element={<AppExample />} />
<Route path="/app-example/shopping-cart" element={<ShoppingCart />} />
<Route path="/app-example/relay-example" element={<RelayExample />} />
<Route path="/app-example/todo" element={<Todo />} />
<Route path="/app-example/bookkeeping" element={<Bookkeeping />} />
</Routes>
</AppErrorBoundary>
</Suspense>
Expand Down
42 changes: 25 additions & 17 deletions src/config/navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ enum NavPathKey {
Hooks = '/hooks',
useState = '/hooks/useState',
useEffect = '/hooks/useEffect',
ShoppingCart = '/shopping-cart',
RelayExample = '/relay-example',
Todo = '/todo',
AppExample = '/app-example',
ShoppingCart = '/app-example/shopping-cart',
RelayExample = '/app-example/relay-example',
Todo = '/app-example/todo',
Bookkeeping = '/app-example/bookkeeping',
};

export const mainNavItems: NavItem[] = [
Expand All @@ -34,21 +36,9 @@ export const mainNavItems: NavItem[] = [
),
},
{
key: NavPathKey.ShoppingCart,
key: NavPathKey.AppExample,
label: (
<a href="/shopping-cart">Shopping Cart</a>
),
},
{
key: NavPathKey.RelayExample,
label: (
<a href="/relay-example">Relay Example</a>
),
},
{
key: NavPathKey.Todo,
label: (
<a href="/todo">待办事项清单</a>
<a href="/app-example">App Example</a>
),
},
];
Expand All @@ -62,4 +52,22 @@ export const sideNavItems: Record<string, NavItem[]> = {
{ key: NavPathKey.useState, label: 'useState' },
{ key: NavPathKey.useEffect, label: 'useEffect' },
],
[NavPathKey.AppExample]: [
{
key: NavPathKey.Todo,
label: (<a href="/app-example/todo">待办事项清单</a>),
},
{
key: NavPathKey.Bookkeeping,
label: (<a href="/app-example/bookkeeping">记账本</a>),
},
{
key: NavPathKey.ShoppingCart,
label: (<a href="/app-example/shopping-cart">购物车</a>),
},
{
key: NavPathKey.RelayExample,
label: (<a href="/app-example/relay-example">Relay Example</a>),
},
],
};
Loading