Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(console): report list card #2600

Merged
merged 2 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions console/packages/starwhale-ui/src/IconFont/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export type IconTypesT =
| 'a-Versionhistory'
| 'fold21'
| 'unfold21'
| 'a-onlineevaluation'

interface IIconFontProps {
style?: React.CSSProperties
Expand Down
1 change: 1 addition & 0 deletions console/packages/starwhale-ui/src/Input/QueryInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export function QueryInput(props: any) {
onChange={(event) => setValue((event.target as HTMLInputElement).value)}
value={value}
clearable
placeholder={props.placeholder}
/>
)
}
7 changes: 7 additions & 0 deletions console/src/domain/report/hooks/useReport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useQuery } from 'react-query'
import { listReports } from '@/domain/report/services/report'
import { IListQuerySchema } from '@base/schemas/list'

export function useFetchReports(projectId: string, query: IListQuerySchema) {
return useQuery(['useFetchReports', projectId, query], () => listReports(projectId, query))
}
16 changes: 16 additions & 0 deletions console/src/domain/report/schemas/report.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { IUserSchema } from '@user/schemas/user'

export interface IReportSchema {
id: number
uuid: string
title: string
description?: string
createdTime: number
modifiedTime: number
owner: IUserSchema
shared: boolean
}

export interface IReportDetailSchema extends IReportSchema {
content: string
}
33 changes: 33 additions & 0 deletions console/src/domain/report/services/report.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import axios from 'axios'
import { IListSchema } from '@starwhale/core'
import { IReportDetailSchema, IReportSchema } from '@/domain/report/schemas/report'
import { IListQuerySchema } from '@base/schemas/list'

export async function listReports(project: string, query: IListQuerySchema): Promise<IListSchema<IReportSchema>> {
const { data } = await axios.get<IListSchema<IReportSchema>>(`/api/v1/project/${project}/report`, {
params: query,
})
return data
}

export async function fetchReport(project: string, reportId: string): Promise<IReportDetailSchema> {
const { data } = await axios.get<IReportDetailSchema>(`/api/v1/project/${project}/report/${reportId}`)
return data
}

export async function createReport(project: string, report: IReportDetailSchema): Promise<IReportDetailSchema> {
const { data } = await axios.post<IReportDetailSchema>(`/api/v1/project/${project}/report`, report)
return data
}

export async function removeReport(projectId: string, reportId: number): Promise<string> {
const { data } = await axios.delete<string>(`/api/v1/project/${projectId}/report/${reportId}`)
return data
}

export async function updateReportShared(projectId: string, reportId: number, shared: boolean): Promise<string> {
const { data } = await axios.put<string>(`/api/v1/project/${projectId}/report/${reportId}/shared`, null, {
params: { shared },
})
return data
}
16 changes: 16 additions & 0 deletions console/src/i18n/locales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,21 @@ const evaluation = {
},
}

const report = {
'Reports': {
en: 'Reports',
zh: '报告',
},
'report.title': {
en: 'Title',
zh: '标题',
},
'report.search.by.title': {
en: 'Search by title',
zh: '按标题搜索',
},
}

const table = {
'table.column.manage': {
en: 'Manage Columns',
Expand Down Expand Up @@ -1652,6 +1667,7 @@ const locales0 = {
...trash,
...project,
...job,
...report,
...table,
...evaluation,
...widget,
Expand Down
6 changes: 6 additions & 0 deletions console/src/pages/Project/ProjectSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export default function ProjectSidebar({ style }: IComposedSidebarProps) {
activePathPattern: /\/(runtimes|new_runtime)\/?/,
icon: <IconFont type='runtime' size={16} />,
},
{
title: t('Reports'),
path: `/projects/${projectId}/reports`,
activePathPattern: /\/(reports)\/?/,
icon: <IconFont type='a-onlineevaluation' size={16} />,
},
isPrivileged
? {
title: t('trash.title'),
Expand Down
12 changes: 12 additions & 0 deletions console/src/pages/Report/ReportEdit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import { useParams } from 'react-router-dom'

export default function ReportEdit() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { projectId, reportId } = useParams<{
projectId: string
reportId: string
}>()

return <></>
}
104 changes: 104 additions & 0 deletions console/src/pages/Report/ReportListCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react'
import { useFetchReports } from '@/domain/report/hooks/useReport'
import { useParams } from 'react-router-dom'
import Card from '@/components/Card'
import Table from '@/components/Table'
import useTranslation from '@/hooks/useTranslation'
import { IReportSchema } from '@/domain/report/schemas/report'
import { TextLink } from '@/components/Link'
import { Button, QueryInput, Toggle, useConfirmCtx } from '@starwhale/ui'
import { toaster } from 'baseui/toast'
import { removeReport, updateReportShared } from '@/domain/report/services/report'
import Text from '@starwhale/ui/Text'
import Copy from 'react-copy-to-clipboard'
import { usePage } from '@/hooks/usePage'
import { formatTimestampDateTime } from '@/utils/datetime'

export default function ReportListCard() {
const [t] = useTranslation()
const [page] = usePage()
const { projectId } = useParams<{ projectId: string }>()
const [filter, setFilter] = React.useState('')
const reports = useFetchReports(projectId, { ...page, search: filter })
const confirmCtx = useConfirmCtx()

const handleDelete = async (id: number, title: string) => {
const ok = await confirmCtx.show({ title: t('Confirm'), content: t('delete sth confirm', [title]) })
if (!ok) {
return
}
await removeReport(projectId, id)
await reports.refetch()
}

const renderRow = (report: IReportSchema) => {
return [
<TextLink key='title' to={`/projects/${projectId}/reports/${report.id}`}>
{report.title}
</TextLink>,
<Toggle
key='shared'
value={report.shared}
onChange={async (share) => {
try {
await updateReportShared(projectId, report.id, share)
await reports.refetch()
toaster.positive(t('dataset.overview.shared.success'))
} catch (e) {
toaster.negative(t('dataset.overview.shared.fail'))
}
}}
/>,
<Text key='desc'>{report.description}</Text>,
report.owner.name,
report.createdTime ? formatTimestampDateTime(report.createdTime) : '',
report.modifiedTime ? formatTimestampDateTime(report.modifiedTime) : '',
<div key='action' style={{ display: 'flex', gap: '5px' }}>
{/* TODO: get link from the server */}
<Copy
text={`${window.location.origin}/projects/${projectId}/reports/${report.id}`}
onCopy={() => {
toaster.positive(t('Copied'), { autoHideDuration: 1000 })
}}
>
<Button as='link' icon='a-copylink' onClick={() => {}} />
</Copy>
<Button as='link' icon='delete' onClick={() => handleDelete(report.id, report.title)} />
</div>,
]
}

return (
<Card title={t('Reports')}>
<div style={{ maxWidth: '280px', paddingBottom: '10px' }}>
<QueryInput
placeholder={t('report.search.by.title')}
onChange={(val: string) => {
setFilter(val)
}}
/>
</div>
<Table
isLoading={reports.isLoading}
columns={[
t('report.title'),
t('Shared'),
t('Description'),
t('Owner'),
t('Created'),
t('Update'),
t('Action'),
]}
data={reports.data?.list.map(renderRow)}
paginationProps={{
start: reports.data?.pageNum,
count: reports.data?.pageSize,
total: reports.data?.total,
afterPageChange: async () => {
await reports.refetch()
},
}}
/>
</Card>
)
}
6 changes: 6 additions & 0 deletions console/src/pages/Report/ReportOverviewLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react'
import BaseSubLayout from '@/pages/BaseSubLayout'

export default function ReportOverviewLayout({ children }: { children: React.ReactNode }) {
return <BaseSubLayout breadcrumbItems={[]}>{children}</BaseSubLayout>
}
16 changes: 16 additions & 0 deletions console/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ import { getAuthedRoutes, getUnauthedRoutes } from './routesUtils'
import EvaluationListResult from './pages/Evaluation/EvaluationListResult'
import DatasetBuildListCard from './pages/Dataset/DatasetBuildListCard'
import ModelReadmeOverview from './pages/Model/ModelReadmeOverview'
import ReportOverviewLayout from '@/pages/Report/ReportOverviewLayout'
import ReportListCard from '@/pages/Report/ReportListCard'
import ReportEdit from '@/pages/Report/ReportEdit'

const useStyles = createUseStyles({
root: ({ theme }: IThemedStyleProps) => ({
Expand Down Expand Up @@ -286,6 +289,19 @@ const Routes = () => {
</Switch>
</ModelOverviewLayout>
</Route>
<Route exact path='/projects/:projectId/reports/:reportId?'>
<ReportOverviewLayout>
<Switch>
<Route exact path='/projects/:projectId/reports' component={ReportListCard} />
<Route
exact
path='/projects/:projectId/reports/:reportId'
component={ReportEdit}
/>
<Redirect to='/projects/:projectId/reports' />
</Switch>
</ReportOverviewLayout>
</Route>

{/* trash */}
<Route exact path='/projects/:projectId/trashes'>
Expand Down
Loading