feat: enhance project directory resolution and auto-refresh in MemberLogsTab
- Updated `TeamMemberLogsFinder` to improve project directory resolution by falling back to `leadSessionId` if the encoded directory does not exist, enhancing reliability in locating project logs. - Refactored `MemberLogsTab` to implement an initial loading state and auto-refresh functionality for ongoing tasks, improving user experience during log retrieval. - Adjusted error handling and loading indicators to provide clearer feedback based on task status.
This commit is contained in:
parent
c024f5bc78
commit
d60ac09925
2 changed files with 58 additions and 9 deletions
|
|
@ -257,9 +257,45 @@ export class TeamMemberLogsFinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizedProjectPath = trimTrailingSlashes(config.projectPath);
|
const normalizedProjectPath = trimTrailingSlashes(config.projectPath);
|
||||||
const projectId = encodePath(normalizedProjectPath);
|
let projectId = encodePath(normalizedProjectPath);
|
||||||
const baseDir = extractBaseDir(projectId);
|
let baseDir = extractBaseDir(projectId);
|
||||||
const projectDir = path.join(getProjectsBasePath(), baseDir);
|
let projectDir = path.join(getProjectsBasePath(), baseDir);
|
||||||
|
|
||||||
|
// If the encoded directory doesn't exist (symlink/cwd mismatch), fall back to locating
|
||||||
|
// the project directory by leadSessionId which is unique and reliable.
|
||||||
|
try {
|
||||||
|
const stat = await fs.stat(projectDir);
|
||||||
|
if (!stat.isDirectory()) {
|
||||||
|
throw new Error('not a directory');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
const leadSessionId =
|
||||||
|
typeof config.leadSessionId === 'string' && config.leadSessionId.trim().length > 0
|
||||||
|
? config.leadSessionId.trim()
|
||||||
|
: null;
|
||||||
|
if (leadSessionId) {
|
||||||
|
const projectsBase = getProjectsBasePath();
|
||||||
|
try {
|
||||||
|
const entries = await fs.readdir(projectsBase, { withFileTypes: true });
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (!entry.isDirectory()) continue;
|
||||||
|
const candidateDir = path.join(projectsBase, entry.name);
|
||||||
|
const leadPath = path.join(candidateDir, `${leadSessionId}.jsonl`);
|
||||||
|
try {
|
||||||
|
await fs.access(leadPath);
|
||||||
|
projectDir = candidateDir;
|
||||||
|
projectId = entry.name;
|
||||||
|
baseDir = entry.name;
|
||||||
|
break;
|
||||||
|
} catch {
|
||||||
|
// not this project
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const knownSessionIds = new Set<string>();
|
const knownSessionIds = new Set<string>();
|
||||||
if (config.leadSessionId) {
|
if (config.leadSessionId) {
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,20 @@ export const MemberLogsTab = ({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setLoading(true);
|
let isInitial = true;
|
||||||
setError(null);
|
const shouldAutoRefresh = taskId != null && taskStatus === 'in_progress';
|
||||||
|
|
||||||
void (async () => {
|
const load = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (taskId == null && !memberName) {
|
if (taskId == null && !memberName) {
|
||||||
if (!cancelled) setLogs([]);
|
if (!cancelled) setLogs([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isInitial) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
setError(null);
|
||||||
|
|
||||||
const result =
|
const result =
|
||||||
taskId != null
|
taskId != null
|
||||||
? await api.teams.getLogsForTask(teamName, taskId, {
|
? await api.teams.getLogsForTask(teamName, taskId, {
|
||||||
|
|
@ -65,14 +70,20 @@ export const MemberLogsTab = ({
|
||||||
setError(e instanceof Error ? e.message : 'Unknown error');
|
setError(e instanceof Error ? e.message : 'Unknown error');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (!cancelled) {
|
if (!cancelled && isInitial) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
isInitial = false;
|
||||||
}
|
}
|
||||||
})();
|
};
|
||||||
|
|
||||||
|
void load();
|
||||||
|
|
||||||
|
const interval = shouldAutoRefresh ? setInterval(() => void load(), 5000) : null;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
if (interval) clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, [teamName, memberName, taskId, taskOwner, taskStatus]);
|
}, [teamName, memberName, taskId, taskOwner, taskStatus]);
|
||||||
|
|
||||||
|
|
@ -133,7 +144,9 @@ export const MemberLogsTab = ({
|
||||||
No logs found
|
No logs found
|
||||||
<p className="mt-1 text-[10px] opacity-60">
|
<p className="mt-1 text-[10px] opacity-60">
|
||||||
{taskId != null
|
{taskId != null
|
||||||
? 'No session activity for this task yet'
|
? taskStatus === 'in_progress'
|
||||||
|
? 'Task is in progress — waiting for session activity (auto-refreshing)...'
|
||||||
|
: 'No session activity for this task yet'
|
||||||
: 'This member has no recorded session activity yet'}
|
: 'This member has no recorded session activity yet'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue