sync: update from private repo (5b6df2f)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-06-10 08:40:41 +00:00
parent dfc5950117
commit 5502478636
43 changed files with 6452 additions and 18 deletions
+60 -1
View File
@@ -1,6 +1,7 @@
import { LocalTask } from '../../api';
import { matchText } from '../../lib/utils';
import { COLUMN_LIST, SortMode, StatusColumn } from '../../lib/urlState';
import { filterTasksByScope, type TaskScope } from '../../lib/taskScope';
import { FilterBar } from './FilterBar';
import { LocalTaskListItem } from './TaskListItem';
import { RailPanel } from './RailPanel';
@@ -16,14 +17,56 @@ interface TaskListPanelProps {
onSearchChange: (q: string) => void;
onSelectTask: (id: number) => void;
onOpenCreate: () => void;
/**
* Owner scope (mine/all). Only meaningful when scopeEnabled — auth must be
* on and the viewer known; otherwise everything is owner 'local' and the
* control is hidden.
*/
scope?: TaskScope;
onScopeChange?: (scope: TaskScope) => void;
currentUserId?: string | null;
scopeEnabled?: boolean;
/** 'rail' 時は RailPanel を render する。default 'list'。 */
mode?: 'list' | 'rail';
/** rail mode 時の「リストに戻る」ボタンで呼ばれる。 */
onExitFocused?: () => void;
}
function ScopeToggle({
scope,
mineCount,
allCount,
onScopeChange,
}: {
scope: TaskScope;
mineCount: number;
allCount: number;
onScopeChange: (scope: TaskScope) => void;
}) {
const seg = (value: TaskScope, label: string, count: number) => (
<button
type="button"
onClick={() => onScopeChange(value)}
aria-pressed={scope === value}
className={`flex-1 px-2 py-1 rounded text-[11px] font-medium transition-colors tabular-nums ${
scope === value
? 'bg-surface text-slate-900 shadow-sm'
: 'text-slate-500 hover:text-slate-700'
}`}
>
{label} <span className={scope === value ? 'text-slate-500' : 'text-slate-400'}>{count}</span>
</button>
);
return (
<div className="flex gap-0.5 p-0.5 mb-2 rounded-md bg-canvas border border-hairline" role="group" aria-label="タスクの表示範囲">
{seg('mine', '自分', mineCount)}
{seg('all', 'すべて', allCount)}
</div>
);
}
export function TaskListPanel({
localTasks,
localTasks: allTasks,
selectedStatus,
sortMode,
searchQuery,
@@ -33,9 +76,17 @@ export function TaskListPanel({
onSearchChange,
onSelectTask,
onOpenCreate,
scope = 'mine',
onScopeChange,
currentUserId = null,
scopeEnabled = false,
mode = 'list',
onExitFocused,
}: TaskListPanelProps) {
// Owner scope is the outermost filter: status counts / search / sort all
// operate on the scoped list so "自分" mode never counts others' tasks.
const effectiveScope: TaskScope = scopeEnabled ? scope : 'all';
const localTasks = filterTasksByScope(allTasks, effectiveScope, currentUserId);
if (mode === 'rail') {
const localColumnsRail: Record<string, LocalTask[]> = COLUMN_LIST.reduce((acc, s) => {
acc[s] = localTasks.filter(t => (t.latestJob?.status ?? 'queued') === s);
@@ -111,6 +162,14 @@ export function TaskListPanel({
</svg>
</button>
{scopeEnabled && onScopeChange && (
<ScopeToggle
scope={scope}
mineCount={filterTasksByScope(allTasks, 'mine', currentUserId).length}
allCount={allTasks.length}
onScopeChange={onScopeChange}
/>
)}
<div className="flex items-center gap-3 text-[10px] text-slate-500 px-0.5 pb-2.5 font-mono tabular-nums">
<span><span className="font-semibold text-slate-700">{totalCount}</span> </span>
<span aria-hidden="true" className="text-slate-300">·</span>