This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user