feat: group imported threads into project archives

This commit is contained in:
kris
2026-03-30 13:50:26 +08:00
parent 98dd0e3cd5
commit 03ac40f427
23 changed files with 1207 additions and 83 deletions

View File

@@ -0,0 +1,21 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import { getConversationFolderView } from "@/lib/boss-projections";
import { readState } from "@/lib/boss-data";
export async function GET(
request: NextRequest,
context: { params: Promise<{ folderKey: string }> },
) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const { folderKey } = await context.params;
const state = await readState();
const folder = getConversationFolderView(state, decodeURIComponent(folderKey));
if (!folder) {
return NextResponse.json({ ok: false, message: "FOLDER_NOT_FOUND" }, { status: 404 });
}
return NextResponse.json({ ok: true, folder });
}

View File

@@ -0,0 +1,16 @@
import { NextRequest, NextResponse } from "next/server";
import { requireRequestSession } from "@/lib/boss-auth";
import { getConversationHomeItems } from "@/lib/boss-projections";
import { readState } from "@/lib/boss-data";
export async function GET(request: NextRequest) {
const session = await requireRequestSession(request);
if (!session) {
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const state = await readState();
return NextResponse.json({
ok: true,
conversations: getConversationHomeItems(state),
});
}

View File

@@ -0,0 +1,46 @@
import {
AppShell,
ConversationList,
PageNav,
StatusBar,
} from "@/components/app-ui";
import { requirePageSession } from "@/lib/boss-auth";
import { getConversationFolderView } from "@/lib/boss-projections";
import { readState } from "@/lib/boss-data";
export const dynamic = "force-dynamic";
export default async function ConversationFolderPage({
params,
}: {
params: Promise<{ folderKey: string }>;
}) {
await requirePageSession();
const { folderKey } = await params;
const state = await readState();
const folder = getConversationFolderView(state, decodeURIComponent(folderKey));
return (
<AppShell bottomNav={false}>
<StatusBar />
<PageNav title={folder?.folderLabel ?? "项目线程"} backHref="/conversations" />
<div className="space-y-3 px-[18px] pb-6">
<div className="rounded-2xl border border-[#E5E5EA] bg-white px-4 py-4">
<div className="text-[16px] font-semibold text-[#111111]">
{folder?.folderLabel ?? "未命名项目"}
</div>
<div className="mt-2 text-[13px] leading-6 text-[#57606A]">
{folder
? `${folder.deviceName ?? "当前设备"} · ${folder.threadCount} 个线程`
: "当前项目下没有可显示的线程。"}
</div>
</div>
</div>
{folder ? (
<ConversationList conversations={folder.threads} />
) : (
<div className="px-[18px] pb-6 text-[13px] text-[#8C8C8C]">线</div>
)}
</AppShell>
);
}

View File

@@ -7,7 +7,7 @@ import {
StatusBar,
} from "@/components/app-ui";
import { requirePageSession } from "@/lib/boss-auth";
import { getConversationItems } from "@/lib/boss-projections";
import { getConversationHomeItems } from "@/lib/boss-projections";
import { readState } from "@/lib/boss-data";
export const dynamic = "force-dynamic";
@@ -15,7 +15,7 @@ export const dynamic = "force-dynamic";
export default async function ConversationsPage() {
await requirePageSession();
const state = await readState();
const conversations = getConversationItems(state);
const conversations = getConversationHomeItems(state);
return (
<AppShell>

View File

@@ -7,6 +7,7 @@ import {
HeaderTitle,
StatusBar,
} from "@/components/app-ui";
import { DeviceImportDraftManager } from "@/components/device-import-draft-manager";
import { requirePageSession } from "@/lib/boss-auth";
import { getDeviceWorkspaceView } from "@/lib/boss-projections";
import { readState } from "@/lib/boss-data";
@@ -55,6 +56,12 @@ export default async function DevicesPage({
relatedThreads={workspace.relatedThreads}
activeEnrollment={workspace.activeEnrollment}
/>
<div className="mt-3">
<DeviceImportDraftManager
deviceId={workspace.selectedDevice.id}
deviceName={workspace.selectedDevice.name}
/>
</div>
</div>
) : null}
</AppShell>