feat(supply-chain): add new product data views
This commit is contained in:
parent
88fbdbacd2
commit
ceda595797
@ -65,7 +65,7 @@ type FilterOption = {
|
||||
label: string
|
||||
}
|
||||
|
||||
type OaBoardViewTab =
|
||||
export type OaBoardViewTab =
|
||||
| 'summary'
|
||||
| 'daily'
|
||||
| 'monthly'
|
||||
@ -3310,12 +3310,16 @@ export default function OaBoardClient({
|
||||
sampleProgressItems = [],
|
||||
currentSampleStageOpeningCodes = [],
|
||||
currentUserName = '',
|
||||
embeddedTab,
|
||||
embeddedTitle,
|
||||
}: {
|
||||
dashboard: OaBoardDashboardDTO
|
||||
loadErrorMessage?: string | null
|
||||
sampleProgressItems?: SampleProgressDTO[]
|
||||
currentSampleStageOpeningCodes?: string[]
|
||||
currentUserName?: string
|
||||
embeddedTab?: 'detail'
|
||||
embeddedTitle?: string
|
||||
}) {
|
||||
const { items: allItems, stageGraph, dailyReport, monthlyReport } = dashboard
|
||||
const canViewDailyReport = Boolean(dailyReport)
|
||||
@ -3330,7 +3334,7 @@ export default function OaBoardClient({
|
||||
return params.toString()
|
||||
}, [searchParamsKey])
|
||||
const [isPending, startTransition] = useTransition()
|
||||
const [showTimingColumns, setShowTimingColumns] = useState(false)
|
||||
const [showTimingColumns, setShowTimingColumns] = useState(Boolean(embeddedTab === 'detail'))
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [previewImage, setPreviewImage] = useState<StyleImagePreview | null>(null)
|
||||
const [timeoutFollowups, setTimeoutFollowups] = useState<OaBoardTimeoutFollowup[]>([])
|
||||
@ -3377,6 +3381,7 @@ export default function OaBoardClient({
|
||||
)
|
||||
const [filters, setFilters] = useState<FilterState>(appliedFilters)
|
||||
const [activeTab, setActiveTab] = useState<OaBoardViewTab>(() => {
|
||||
if (embeddedTab) return embeddedTab
|
||||
const requestedTab = parseOaBoardViewTab(searchParams.get('tab'))
|
||||
if (requestedTab === 'daily' && !canViewDailyReport) return 'summary'
|
||||
if (requestedTab === 'monthly' && !canViewMonthlyReport) return 'summary'
|
||||
@ -3384,7 +3389,7 @@ export default function OaBoardClient({
|
||||
})
|
||||
const showingDailyReport = canViewDailyReport && activeTab === 'daily'
|
||||
const showingMonthlyReport = canViewMonthlyReport && activeTab === 'monthly'
|
||||
const activeTabLabel =
|
||||
const activeTabLabel = embeddedTitle || (
|
||||
activeTab === 'summary'
|
||||
? '新品-进度汇总'
|
||||
: showingDailyReport
|
||||
@ -3401,7 +3406,7 @@ export default function OaBoardClient({
|
||||
? '新品-超时分析'
|
||||
: activeTab === 'shipment'
|
||||
? '新品-出货进度(供应链中心)'
|
||||
: '新品-上架数据'
|
||||
: '新品-上架数据')
|
||||
const sideNavSections = [
|
||||
{
|
||||
title: '产品开发',
|
||||
@ -3966,12 +3971,14 @@ export default function OaBoardClient({
|
||||
<>
|
||||
<div
|
||||
className={
|
||||
activeTab === 'summary' || showingDailyReport || showingMonthlyReport
|
||||
embeddedTab
|
||||
? 'flex h-full min-h-0 flex-col overflow-hidden bg-[#f5f7f8]'
|
||||
: activeTab === 'summary' || showingDailyReport || showingMonthlyReport
|
||||
? 'min-h-full bg-[#f5f7f8]'
|
||||
: 'flex h-[calc(100vh-72px)] min-h-0 flex-col overflow-hidden bg-[#f5f7f8]'
|
||||
}
|
||||
>
|
||||
<div className="flex min-h-0 gap-4 px-4 py-4 xl:px-6 2xl:px-8">
|
||||
<div className={`flex min-h-0 gap-4 px-4 py-4 xl:px-6 2xl:px-8 ${embeddedTab ? 'flex-1' : ''}`}>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-3">
|
||||
{loadErrorMessage ? (
|
||||
<section className="rounded-[2rem] border border-amber-200 bg-amber-50/88 px-5 py-4 shadow-apple-sm">
|
||||
@ -4512,7 +4519,7 @@ export default function OaBoardClient({
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
<aside className="hidden w-[248px] shrink-0 lg:block">
|
||||
{!embeddedTab ? <aside className="hidden w-[248px] shrink-0 lg:block">
|
||||
<div className="flex h-full flex-col gap-3">
|
||||
{sideNavSections.map((section) => (
|
||||
<section key={section.title} className={WORKBENCH_SIDE_SECTION_CLASS}>
|
||||
@ -4555,7 +4562,7 @@ export default function OaBoardClient({
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
</aside> : null}
|
||||
</div>
|
||||
</div>
|
||||
{selectedTimeoutFollowupItem ? (
|
||||
|
||||
@ -12,11 +12,20 @@ import { sampleProgressService } from '@/server/services/sample-progress.service
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export default async function OaBoardPage({
|
||||
searchParams,
|
||||
}: {
|
||||
type OaBoardPageProps = {
|
||||
searchParams?: Promise<Record<string, string | string[] | undefined>>
|
||||
}) {
|
||||
}
|
||||
|
||||
type OaBoardPageContentProps = OaBoardPageProps & {
|
||||
embeddedTab?: 'detail'
|
||||
embeddedTitle?: string
|
||||
}
|
||||
|
||||
export async function OaBoardPageContent({
|
||||
searchParams,
|
||||
embeddedTab,
|
||||
embeddedTitle,
|
||||
}: OaBoardPageContentProps) {
|
||||
const allowLocalPreview = process.env.OA_BOARD_LOCAL_PREVIEW === '1'
|
||||
const session = allowLocalPreview ? null : await requireSession('/login')
|
||||
const canViewDailyReport = canViewOaBoardDailyReport(session?.authUser)
|
||||
@ -106,6 +115,7 @@ export default async function OaBoardPage({
|
||||
: await resolveDashboard()
|
||||
|
||||
const pageStateKey = JSON.stringify({
|
||||
embeddedTab,
|
||||
keyword,
|
||||
approvalNumber,
|
||||
overallStatus,
|
||||
@ -128,9 +138,13 @@ export default async function OaBoardPage({
|
||||
const dashboard = canViewDailyReport
|
||||
? result.dashboard
|
||||
: { ...result.dashboard, dailyReport: undefined }
|
||||
if (!embeddedTab) {
|
||||
await sampleProgressService.syncFromOaDashboard()
|
||||
const sampleProgressItems = await sampleProgressService.listStored()
|
||||
const currentSampleStageOpeningCodes = await sampleProgressService.listCurrentSampleStageOpeningCodes()
|
||||
}
|
||||
const sampleProgressItems = embeddedTab ? [] : await sampleProgressService.listStored()
|
||||
const currentSampleStageOpeningCodes = embeddedTab
|
||||
? []
|
||||
: await sampleProgressService.listCurrentSampleStageOpeningCodes()
|
||||
|
||||
return (
|
||||
<OaBoardClient
|
||||
@ -140,6 +154,12 @@ export default async function OaBoardPage({
|
||||
sampleProgressItems={sampleProgressItems}
|
||||
currentSampleStageOpeningCodes={currentSampleStageOpeningCodes}
|
||||
currentUserName={session?.user.name ?? ''}
|
||||
embeddedTab={embeddedTab}
|
||||
embeddedTitle={embeddedTitle}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default async function OaBoardPage(props: OaBoardPageProps) {
|
||||
return OaBoardPageContent(props)
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { usePathname } from 'next/navigation'
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Database,
|
||||
PackagePlus,
|
||||
ShoppingCart,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react'
|
||||
@ -13,6 +14,7 @@ import { SUPPLY_CHAIN_MENU, type SupplyChainMenuModule } from '../config/supply-
|
||||
const ICON_MAP: Record<string, LucideIcon> = {
|
||||
LayoutDashboard,
|
||||
Database,
|
||||
PackagePlus,
|
||||
ShoppingCart,
|
||||
}
|
||||
|
||||
|
||||
@ -84,6 +84,8 @@ export const SUPPLY_CHAIN_ROUTE_TITLES: Record<string, string> = {
|
||||
'/supply-chain/database/product-archive': '产品全量档案',
|
||||
'/supply-chain/database/category-config': '类目配置',
|
||||
'/supply-chain/database/warehouse-config': '仓库配置',
|
||||
'/supply-chain/new-products/unified-table': '新品统一宽表',
|
||||
'/supply-chain/new-products/sample-progress': '新品打样明细宽表',
|
||||
'/supply-chain/purchase/daily-affairs': '日常事项',
|
||||
'/supply-chain/purchase/price-change': '产品变价',
|
||||
}
|
||||
|
||||
@ -1,6 +1,25 @@
|
||||
import { flattenSupplyChainMenu } from './supply-chain-menu'
|
||||
|
||||
describe('supply chain menu', () => {
|
||||
it('exposes the new product shared datasets', () => {
|
||||
const items = flattenSupplyChainMenu()
|
||||
|
||||
expect(items).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
label: '新品统一宽表',
|
||||
module: '新品管理',
|
||||
route: '/supply-chain/new-products/unified-table',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
label: '新品打样明细宽表',
|
||||
module: '新品管理',
|
||||
route: '/supply-chain/new-products/sample-progress',
|
||||
}),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
||||
it('does not expose the administrator operation log through menu search', () => {
|
||||
expect(flattenSupplyChainMenu().map((item) => item.route)).not.toContain(
|
||||
'/supply-chain/operation-logs',
|
||||
|
||||
@ -45,6 +45,20 @@ export const SUPPLY_CHAIN_MENU: SupplyChainMenuModule[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'new-products',
|
||||
label: '新品管理',
|
||||
icon: 'PackagePlus',
|
||||
sections: [
|
||||
{
|
||||
title: '公共数据源',
|
||||
items: [
|
||||
{ label: '新品统一宽表', route: '/supply-chain/new-products/unified-table' },
|
||||
{ label: '新品打样明细宽表', route: '/supply-chain/new-products/sample-progress' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'purchase',
|
||||
label: '采购管理',
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import SampleProgressClient from '@/app/intake/sample-progress/SampleProgressClient'
|
||||
import { requireSession } from '@/lib/server-session'
|
||||
import { sampleProgressService } from '@/server/services/sample-progress.service'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const metadata = { title: '新品打样明细宽表 · 供应链中心' }
|
||||
|
||||
export default async function NewProductSampleProgressPage() {
|
||||
const { user } = await requireSession('/login')
|
||||
const items = await sampleProgressService.list()
|
||||
const currentSampleStageOpeningCodes =
|
||||
await sampleProgressService.listCurrentSampleStageOpeningCodes()
|
||||
|
||||
return (
|
||||
<SampleProgressClient
|
||||
initialItems={items}
|
||||
currentUserName={user.name}
|
||||
currentSampleStageOpeningCodes={currentSampleStageOpeningCodes}
|
||||
embedded
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import { OaBoardPageContent } from '@/app/intake/oa-board/page'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
export const metadata = { title: '新品统一宽表 · 供应链中心' }
|
||||
|
||||
export default async function NewProductUnifiedTablePage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams?: Promise<Record<string, string | string[] | undefined>>
|
||||
}) {
|
||||
return (
|
||||
<OaBoardPageContent
|
||||
searchParams={searchParams}
|
||||
embeddedTab="detail"
|
||||
embeddedTitle="新品统一宽表"
|
||||
/>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user