refactor: replace all old router to new router

This commit is contained in:
moonrailgun 2024-04-02 01:12:44 +08:00
parent fa7534a8e0
commit 3bb2cc8715
21 changed files with 298 additions and 105 deletions

View File

@ -1,8 +1,8 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useUserStore } from '../../store/user'; import { useUserStore } from '../../store/user';
import { useEvent } from '../../hooks/useEvent'; import { useEvent } from '../../hooks/useEvent';
import { useNavigate } from 'react-router';
import { clearJWT } from '../auth'; import { clearJWT } from '../auth';
import { useNavigate } from '@tanstack/react-router';
/** /**
* Mock * Mock
@ -18,7 +18,10 @@ export function useLogout() {
const logout = useEvent(() => { const logout = useEvent(() => {
useUserStore.setState({ info: null }); useUserStore.setState({ info: null });
clearJWT(); clearJWT();
navigate('/login'); navigate({
to: '/login',
replace: true,
});
}); });
return logout; return logout;

View File

@ -6,6 +6,7 @@ import { useNavigate, useRouterState } from '@tanstack/react-router';
import { LuSearch } from 'react-icons/lu'; import { LuSearch } from 'react-icons/lu';
import { Input } from './ui/input'; import { Input } from './ui/input';
import { useFuseSearch } from '@/hooks/useFuseSearch'; import { useFuseSearch } from '@/hooks/useFuseSearch';
import { Empty } from 'antd';
export interface CommonListItem { export interface CommonListItem {
id: string; id: string;
@ -65,6 +66,8 @@ export const CommonList: React.FC<CommonListProps> = React.memo((props) => {
<ScrollArea className="flex-1"> <ScrollArea className="flex-1">
<div className="flex flex-col gap-2 p-4"> <div className="flex flex-col gap-2 p-4">
{finalList.length === 0 && <Empty />}
{finalList.map((item) => { {finalList.map((item) => {
const isSelected = item.href === location.pathname; const isSelected = item.href === location.pathname;

View File

@ -5,16 +5,15 @@ import { Loading } from '../../Loading';
import { NotFoundTip } from '../../NotFoundTip'; import { NotFoundTip } from '../../NotFoundTip';
import { WebsiteOverview } from '../../website/WebsiteOverview'; import { WebsiteOverview } from '../../website/WebsiteOverview';
import { Button } from 'antd'; import { Button } from 'antd';
import { useNavigate } from 'react-router';
import { ArrowRightOutlined } from '@ant-design/icons'; import { ArrowRightOutlined } from '@ant-design/icons';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { Link } from '@tanstack/react-router';
export const WebsiteOverviewItem: React.FC<{ export const WebsiteOverviewItem: React.FC<{
websiteId: string; websiteId: string;
}> = React.memo((props) => { }> = React.memo((props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const workspaceId = useCurrentWorkspaceId(); const workspaceId = useCurrentWorkspaceId();
const navigate = useNavigate();
const { data: websiteInfo, isLoading } = trpc.website.info.useQuery({ const { data: websiteInfo, isLoading } = trpc.website.info.useQuery({
workspaceId, workspaceId,
@ -34,15 +33,11 @@ export const WebsiteOverviewItem: React.FC<{
website={websiteInfo} website={websiteInfo}
actions={ actions={
<> <>
<Button <Link to="/website/$websiteId" params={{ websiteId: websiteInfo.id }}>
type="primary" <Button type="primary" size="large">
size="large" {t('View Details')} <ArrowRightOutlined />
onClick={() => { </Button>
navigate(`/website/${websiteInfo.id}`); </Link>
}}
>
{t('View Details')} <ArrowRightOutlined />
</Button>
</> </>
} }
/> />

View File

@ -14,7 +14,6 @@ import { MonitorInfo as MonitorInfoType } from '../../../types';
import { MonitorHealthBar } from './MonitorHealthBar'; import { MonitorHealthBar } from './MonitorHealthBar';
import { last } from 'lodash-es'; import { last } from 'lodash-es';
import { ColorTag } from '../ColorTag'; import { ColorTag } from '../ColorTag';
import { useNavigate } from 'react-router';
import { MonitorEventList } from './MonitorEventList'; import { MonitorEventList } from './MonitorEventList';
import { useEvent } from '../../hooks/useEvent'; import { useEvent } from '../../hooks/useEvent';
import { MonitorDataMetrics } from './MonitorDataMetrics'; import { MonitorDataMetrics } from './MonitorDataMetrics';
@ -22,6 +21,7 @@ import { MonitorDataChart } from './MonitorDataChart';
import { DeleteOutlined, MoreOutlined } from '@ant-design/icons'; import { DeleteOutlined, MoreOutlined } from '@ant-design/icons';
import { MonitorBadgeView } from './MonitorBadgeView'; import { MonitorBadgeView } from './MonitorBadgeView';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { useNavigate } from '@tanstack/react-router';
interface MonitorInfoProps { interface MonitorInfoProps {
monitorId: string; monitorId: string;
@ -109,7 +109,8 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
}); });
await trpcUtils.monitor.all.refetch(); await trpcUtils.monitor.all.refetch();
navigate('/monitor', { navigate({
to: '/monitor',
replace: true, replace: true,
}); });
}, },
@ -198,7 +199,12 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
<Button <Button
type="primary" type="primary"
onClick={() => { onClick={() => {
navigate(`/monitor/${monitorInfo.id}/edit`); navigate({
to: '/monitor/$monitorId/edit',
params: {
monitorId: monitorInfo.id,
},
});
}} }}
> >
{t('Edit')} {t('Edit')}

View File

@ -8,25 +8,26 @@ import {
} from './EditForm'; } from './EditForm';
import clsx from 'clsx'; import clsx from 'clsx';
import { useRequest } from '../../../hooks/useRequest'; import { useRequest } from '../../../hooks/useRequest';
import { useNavigate } from 'react-router';
import { ColorSchemeSwitcher } from '../../ColorSchemeSwitcher'; import { ColorSchemeSwitcher } from '../../ColorSchemeSwitcher';
import { StatusPageServices } from './Services'; import { StatusPageServices } from './Services';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { Link, useNavigate } from '@tanstack/react-router';
interface MonitorStatusPageProps { interface MonitorStatusPageProps {
slug: string; slug: string;
showBackBtn?: boolean;
} }
export const MonitorStatusPage: React.FC<MonitorStatusPageProps> = React.memo( export const MonitorStatusPage: React.FC<MonitorStatusPageProps> = React.memo(
(props) => { (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { slug } = props; const { slug, showBackBtn = true } = props;
const { data: info } = trpc.monitor.getPageInfo.useQuery({ const { data: info } = trpc.monitor.getPageInfo.useQuery({
slug, slug,
}); });
const editPageMutation = trpc.monitor.editPage.useMutation(); const editPageMutation = trpc.monitor.editPage.useMutation();
const trpcUtils = trpc.useContext(); const trpcUtils = trpc.useUtils();
const navigate = useNavigate(); const navigate = useNavigate();
const allowEdit = useAllowEdit(info?.workspaceId); const allowEdit = useAllowEdit(info?.workspaceId);
@ -58,7 +59,13 @@ export const MonitorStatusPage: React.FC<MonitorStatusPageProps> = React.memo(
if (info.slug !== newPageInfo.slug) { if (info.slug !== newPageInfo.slug) {
// if slug is changed, should to navigate to new url // if slug is changed, should to navigate to new url
navigate(`/status/${newPageInfo.slug}`); navigate({
to: '/status/$slug',
params: {
slug: newPageInfo.slug,
},
replace: true,
});
} }
} }
); );
@ -94,9 +101,11 @@ export const MonitorStatusPage: React.FC<MonitorStatusPageProps> = React.memo(
{t('Edit')} {t('Edit')}
</Button> </Button>
<Button type="default" onClick={() => navigate(`/`)}> {showBackBtn && (
{t('Back to Admin')} <Link to="/">
</Button> <Button type="default">{t('Back to Admin')}</Button>
</Link>
)}
</div> </div>
)} )}

View File

@ -3,9 +3,9 @@ import React from 'react';
import { trpc } from '../../api/trpc'; import { trpc } from '../../api/trpc';
import { useCurrentWorkspaceId } from '../../store/user'; import { useCurrentWorkspaceId } from '../../store/user';
import { ColorTag } from '../ColorTag'; import { ColorTag } from '../ColorTag';
import { useNavigate } from 'react-router';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { useNavigate } from '@tanstack/react-router';
interface NotificationPickerProps extends SelectProps<string> {} interface NotificationPickerProps extends SelectProps<string> {}
export const NotificationPicker: React.FC<NotificationPickerProps> = React.memo( export const NotificationPicker: React.FC<NotificationPickerProps> = React.memo(
@ -26,7 +26,11 @@ export const NotificationPicker: React.FC<NotificationPickerProps> = React.memo(
<div className="mb-1">{t('Not found any notification')}</div> <div className="mb-1">{t('Not found any notification')}</div>
<Button <Button
icon={<PlusOutlined />} icon={<PlusOutlined />}
onClick={() => navigate('/settings/notifications')} onClick={() =>
navigate({
to: '/settings/notifications',
})
}
> >
{t('Create Now')} {t('Create Now')}
</Button> </Button>

View File

@ -26,11 +26,10 @@ import {
PlusOutlined, PlusOutlined,
DeleteOutlined, DeleteOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { useNavigate } from 'react-router';
import { PageHeader } from '../PageHeader'; import { PageHeader } from '../PageHeader';
import { useEvent } from '../../hooks/useEvent'; import { useEvent } from '../../hooks/useEvent';
import { TelemetryCounter } from './TelemetryCounter'; import { TelemetryCounter } from './TelemetryCounter';
import { LuDelete, LuTrash } from 'react-icons/lu'; import { useNavigate } from '@tanstack/react-router';
type TelemetryInfo = AppRouterOutput['telemetry']['all'][number]; type TelemetryInfo = AppRouterOutput['telemetry']['all'][number];
@ -276,7 +275,12 @@ const TelemetryListTable: React.FC<{
<Button <Button
icon={<BarChartOutlined />} icon={<BarChartOutlined />}
onClick={() => { onClick={() => {
navigate(`/telemetry/${record.id}`); navigate({
to: '/telemetry/$telemetryId',
params: {
telemetryId: record.id,
},
});
}} }}
> >
{t('View')} {t('View')}

View File

@ -1,6 +1,5 @@
import { Button, Form, Input, message, Popconfirm, Tabs } from 'antd'; import { Button, Form, Input, message, Popconfirm, Tabs } from 'antd';
import React from 'react'; import React from 'react';
import { useNavigate, useParams } from 'react-router';
import { deleteWorkspaceWebsite } from '../../api/model/website'; import { deleteWorkspaceWebsite } from '../../api/model/website';
import { useRequest } from '../../hooks/useRequest'; import { useRequest } from '../../hooks/useRequest';
import { useCurrentWorkspaceId } from '../../store/user'; import { useCurrentWorkspaceId } from '../../store/user';
@ -18,6 +17,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { useEvent } from '../../hooks/useEvent'; import { useEvent } from '../../hooks/useEvent';
import { hostnameValidator } from '../../utils/validator'; import { hostnameValidator } from '../../utils/validator';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { useNavigate } from '@tanstack/react-router';
export const WebsiteConfig: React.FC<{ websiteId: string }> = React.memo( export const WebsiteConfig: React.FC<{ websiteId: string }> = React.memo(
(props) => { (props) => {
@ -57,7 +57,9 @@ export const WebsiteConfig: React.FC<{ websiteId: string }> = React.memo(
message.success(t('Delete Success')); message.success(t('Delete Success'));
navigate('/settings/websites'); navigate({
to: '/website',
});
}); });
if (!workspaceId) { if (!workspaceId) {

View File

@ -10,12 +10,12 @@ import { formatNumber, formatShortTime } from '../../utils/common';
import { WebsiteOnlineCount } from './WebsiteOnlineCount'; import { WebsiteOnlineCount } from './WebsiteOnlineCount';
import { useGlobalRangeDate } from '../../hooks/useGlobalRangeDate'; import { useGlobalRangeDate } from '../../hooks/useGlobalRangeDate';
import { MonitorHealthBar } from '../monitor/MonitorHealthBar'; import { MonitorHealthBar } from '../monitor/MonitorHealthBar';
import { useNavigate } from 'react-router';
import { AppRouterOutput, trpc } from '../../api/trpc'; import { AppRouterOutput, trpc } from '../../api/trpc';
import { getUserTimezone } from '../../api/model/user'; import { getUserTimezone } from '../../api/model/user';
import { useGlobalStateStore } from '../../store/global'; import { useGlobalStateStore } from '../../store/global';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { TimeEventChart } from '../TimeEventChart'; import { TimeEventChart } from '../TimeEventChart';
import { Link } from '@tanstack/react-router';
export const WebsiteOverview: React.FC<{ export const WebsiteOverview: React.FC<{
website: WebsiteInfo; website: WebsiteInfo;
@ -25,7 +25,6 @@ export const WebsiteOverview: React.FC<{
const { t } = useTranslation(); const { t } = useTranslation();
const { website, showDateFilter = false, actions } = props; const { website, showDateFilter = false, actions } = props;
const { startDate, endDate, unit, refresh } = useGlobalRangeDate(); const { startDate, endDate, unit, refresh } = useGlobalRangeDate();
const navigate = useNavigate();
const showPreviousPeriod = useGlobalStateStore( const showPreviousPeriod = useGlobalStateStore(
(state) => state.showPreviousPeriod (state) => state.showPreviousPeriod
); );
@ -91,15 +90,16 @@ export const WebsiteOverview: React.FC<{
</span> </span>
{website.monitorId && ( {website.monitorId && (
<div <Link
className="cursor-pointer" className="cursor-pointer"
onClick={() => navigate(`/monitor/${website.monitorId}`)} to="/monitor/$monitorId"
params={{ monitorId: website.monitorId }}
> >
<MonitorHealthBar <MonitorHealthBar
workspaceId={website.workspaceId} workspaceId={website.workspaceId}
monitorId={website.monitorId} monitorId={website.monitorId}
/> />
</div> </Link>
)} )}
<div className="ml-4 text-base font-normal"> <div className="ml-4 text-base font-normal">

View File

@ -20,7 +20,7 @@ import { WorkspaceSwitcher } from '@/components/WorkspaceSwitcher';
import { UserConfig } from './Layout/UserConfig'; import { UserConfig } from './Layout/UserConfig';
import { Outlet } from '@tanstack/react-router'; import { Outlet } from '@tanstack/react-router';
import { trpc } from '@/api/trpc'; import { trpc } from '@/api/trpc';
import { useCurrentWorkspaceId } from '@/store/user'; import { useCurrentWorkspaceId, useUserStore } from '@/store/user';
const defaultLayout: [number, number, number] = [265, 440, 655]; const defaultLayout: [number, number, number] = [265, 440, 655];
@ -38,10 +38,15 @@ export const LayoutV2: React.FC<LayoutProps> = React.memo((props) => {
defaultValue: false, defaultValue: false,
} }
); );
const workspaceId = useCurrentWorkspaceId(); const workspaceId = useUserStore((state) => state.info?.currentWorkspace?.id);
const { data: serviceCount } = trpc.workspace.getServiceCount.useQuery({ const { data: serviceCount } = trpc.workspace.getServiceCount.useQuery(
workspaceId, {
}); workspaceId: workspaceId!,
},
{
enabled: !!workspaceId,
}
);
return ( return (
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>

View File

@ -25,6 +25,7 @@ import { Route as WebsiteOverviewImport } from './routes/website/overview'
import { Route as WebsiteAddImport } from './routes/website/add' import { Route as WebsiteAddImport } from './routes/website/add'
import { Route as TelemetryAddImport } from './routes/telemetry/add' import { Route as TelemetryAddImport } from './routes/telemetry/add'
import { Route as TelemetryTelemetryIdImport } from './routes/telemetry/$telemetryId' import { Route as TelemetryTelemetryIdImport } from './routes/telemetry/$telemetryId'
import { Route as StatusSlugImport } from './routes/status/$slug'
import { Route as SettingsUsageImport } from './routes/settings/usage' import { Route as SettingsUsageImport } from './routes/settings/usage'
import { Route as SettingsProfileImport } from './routes/settings/profile' import { Route as SettingsProfileImport } from './routes/settings/profile'
import { Route as SettingsNotificationsImport } from './routes/settings/notifications' import { Route as SettingsNotificationsImport } from './routes/settings/notifications'
@ -32,9 +33,10 @@ import { Route as SettingsAuditLogImport } from './routes/settings/auditLog'
import { Route as PageAddImport } from './routes/page/add' import { Route as PageAddImport } from './routes/page/add'
import { Route as PageSlugImport } from './routes/page/$slug' import { Route as PageSlugImport } from './routes/page/$slug'
import { Route as MonitorAddImport } from './routes/monitor/add' import { Route as MonitorAddImport } from './routes/monitor/add'
import { Route as MonitorMonitorIdImport } from './routes/monitor/$monitorId'
import { Route as WebsiteWebsiteIdIndexImport } from './routes/website/$websiteId/index' import { Route as WebsiteWebsiteIdIndexImport } from './routes/website/$websiteId/index'
import { Route as MonitorMonitorIdIndexImport } from './routes/monitor/$monitorId/index'
import { Route as WebsiteWebsiteIdConfigImport } from './routes/website/$websiteId/config' import { Route as WebsiteWebsiteIdConfigImport } from './routes/website/$websiteId/config'
import { Route as MonitorMonitorIdEditImport } from './routes/monitor/$monitorId/edit'
// Create/Update Routes // Create/Update Routes
@ -108,6 +110,11 @@ const TelemetryTelemetryIdRoute = TelemetryTelemetryIdImport.update({
getParentRoute: () => TelemetryRoute, getParentRoute: () => TelemetryRoute,
} as any) } as any)
const StatusSlugRoute = StatusSlugImport.update({
path: '/status/$slug',
getParentRoute: () => rootRoute,
} as any)
const SettingsUsageRoute = SettingsUsageImport.update({ const SettingsUsageRoute = SettingsUsageImport.update({
path: '/usage', path: '/usage',
getParentRoute: () => SettingsRoute, getParentRoute: () => SettingsRoute,
@ -143,21 +150,26 @@ const MonitorAddRoute = MonitorAddImport.update({
getParentRoute: () => MonitorRoute, getParentRoute: () => MonitorRoute,
} as any) } as any)
const MonitorMonitorIdRoute = MonitorMonitorIdImport.update({
path: '/$monitorId',
getParentRoute: () => MonitorRoute,
} as any)
const WebsiteWebsiteIdIndexRoute = WebsiteWebsiteIdIndexImport.update({ const WebsiteWebsiteIdIndexRoute = WebsiteWebsiteIdIndexImport.update({
path: '/$websiteId/', path: '/$websiteId/',
getParentRoute: () => WebsiteRoute, getParentRoute: () => WebsiteRoute,
} as any) } as any)
const MonitorMonitorIdIndexRoute = MonitorMonitorIdIndexImport.update({
path: '/$monitorId/',
getParentRoute: () => MonitorRoute,
} as any)
const WebsiteWebsiteIdConfigRoute = WebsiteWebsiteIdConfigImport.update({ const WebsiteWebsiteIdConfigRoute = WebsiteWebsiteIdConfigImport.update({
path: '/$websiteId/config', path: '/$websiteId/config',
getParentRoute: () => WebsiteRoute, getParentRoute: () => WebsiteRoute,
} as any) } as any)
const MonitorMonitorIdEditRoute = MonitorMonitorIdEditImport.update({
path: '/$monitorId/edit',
getParentRoute: () => MonitorRoute,
} as any)
// Populate the FileRoutesByPath interface // Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' { declare module '@tanstack/react-router' {
@ -202,10 +214,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof WebsiteImport preLoaderRoute: typeof WebsiteImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
'/monitor/$monitorId': {
preLoaderRoute: typeof MonitorMonitorIdImport
parentRoute: typeof MonitorImport
}
'/monitor/add': { '/monitor/add': {
preLoaderRoute: typeof MonitorAddImport preLoaderRoute: typeof MonitorAddImport
parentRoute: typeof MonitorImport parentRoute: typeof MonitorImport
@ -234,6 +242,10 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof SettingsUsageImport preLoaderRoute: typeof SettingsUsageImport
parentRoute: typeof SettingsImport parentRoute: typeof SettingsImport
} }
'/status/$slug': {
preLoaderRoute: typeof StatusSlugImport
parentRoute: typeof rootRoute
}
'/telemetry/$telemetryId': { '/telemetry/$telemetryId': {
preLoaderRoute: typeof TelemetryTelemetryIdImport preLoaderRoute: typeof TelemetryTelemetryIdImport
parentRoute: typeof TelemetryImport parentRoute: typeof TelemetryImport
@ -250,10 +262,18 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof WebsiteOverviewImport preLoaderRoute: typeof WebsiteOverviewImport
parentRoute: typeof WebsiteImport parentRoute: typeof WebsiteImport
} }
'/monitor/$monitorId/edit': {
preLoaderRoute: typeof MonitorMonitorIdEditImport
parentRoute: typeof MonitorImport
}
'/website/$websiteId/config': { '/website/$websiteId/config': {
preLoaderRoute: typeof WebsiteWebsiteIdConfigImport preLoaderRoute: typeof WebsiteWebsiteIdConfigImport
parentRoute: typeof WebsiteImport parentRoute: typeof WebsiteImport
} }
'/monitor/$monitorId/': {
preLoaderRoute: typeof MonitorMonitorIdIndexImport
parentRoute: typeof MonitorImport
}
'/website/$websiteId/': { '/website/$websiteId/': {
preLoaderRoute: typeof WebsiteWebsiteIdIndexImport preLoaderRoute: typeof WebsiteWebsiteIdIndexImport
parentRoute: typeof WebsiteImport parentRoute: typeof WebsiteImport
@ -267,7 +287,11 @@ export const routeTree = rootRoute.addChildren([
IndexRoute, IndexRoute,
DashboardRoute, DashboardRoute,
LoginRoute, LoginRoute,
MonitorRoute.addChildren([MonitorMonitorIdRoute, MonitorAddRoute]), MonitorRoute.addChildren([
MonitorAddRoute,
MonitorMonitorIdEditRoute,
MonitorMonitorIdIndexRoute,
]),
PageRoute.addChildren([PageSlugRoute, PageAddRoute]), PageRoute.addChildren([PageSlugRoute, PageAddRoute]),
RegisterRoute, RegisterRoute,
ServerRoute, ServerRoute,
@ -284,6 +308,7 @@ export const routeTree = rootRoute.addChildren([
WebsiteWebsiteIdConfigRoute, WebsiteWebsiteIdConfigRoute,
WebsiteWebsiteIdIndexRoute, WebsiteWebsiteIdIndexRoute,
]), ]),
StatusSlugRoute,
]) ])
/* prettier-ignore-end */ /* prettier-ignore-end */

View File

@ -1,5 +1,6 @@
import { createRootRouteWithContext, Outlet } from '@tanstack/react-router'; import { createRootRouteWithContext, Outlet } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools'; import { TanStackRouterDevtools } from '@tanstack/router-devtools';
import { Suspense } from 'react';
interface RouterContext { interface RouterContext {
// The ReturnType of your useAuth hook or the value of your AuthContext // The ReturnType of your useAuth hook or the value of your AuthContext
@ -9,10 +10,11 @@ interface RouterContext {
export const Route = createRootRouteWithContext<RouterContext>()({ export const Route = createRootRouteWithContext<RouterContext>()({
component: () => { component: () => {
return ( return (
<> // https://github.com/TanStack/router/issues/857
<Suspense fallback={null}>
<Outlet /> <Outlet />
<TanStackRouterDevtools position="bottom-right" /> <TanStackRouterDevtools position="bottom-right" />
</> </Suspense>
); );
}, },
}); });

View File

@ -1,8 +1,4 @@
import { import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';
createFileRoute,
getRouteApi,
useNavigate,
} from '@tanstack/react-router';
import { useRequest } from '@/hooks/useRequest'; import { useRequest } from '@/hooks/useRequest';
import { setJWT } from '@/api/auth'; import { setJWT } from '@/api/auth';
import { useGlobalConfig } from '@/hooks/useConfig'; import { useGlobalConfig } from '@/hooks/useConfig';
@ -17,16 +13,21 @@ export const Route = createFileRoute('/login')({
// redirect: z.string().catch('/'), // redirect: z.string().catch('/'),
redirect: z.string().optional(), redirect: z.string().optional(),
}), }),
beforeLoad: ({ context }) => {
if (context.userInfo) {
redirect({
to: '/website',
});
}
},
component: LoginComponent, component: LoginComponent,
}); });
const routeApi = getRouteApi('/login');
function LoginComponent() { function LoginComponent() {
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useTranslation(); const { t } = useTranslation();
const loginMutation = trpc.user.login.useMutation(); const loginMutation = trpc.user.login.useMutation();
const search = routeApi.useSearch(); const search = Route.useSearch();
const [{ loading }, handleLogin] = useRequest(async (values: any) => { const [{ loading }, handleLogin] = useRequest(async (values: any) => {
const res = await loginMutation.mutateAsync({ const res = await loginMutation.mutateAsync({
@ -37,16 +38,17 @@ function LoginComponent() {
setJWT(res.token); setJWT(res.token);
setUserInfo(res.info); setUserInfo(res.info);
navigate({ navigate({
to: search.redirect ?? '/dashboard', to: search.redirect ?? '/',
replace: true,
}); });
}); });
const { allowRegister } = useGlobalConfig(); const { allowRegister } = useGlobalConfig();
return ( return (
<div className="w-full h-full flex justify-center items-center dark:bg-gray-900"> <div className="flex h-full w-full items-center justify-center dark:bg-gray-900">
<div className="w-80 -translate-y-1/4"> <div className="w-80 -translate-y-1/4">
<div className="text-center"> <div className="text-center">
<img className="w-24 h-24" src="/icon.svg" /> <img className="m-auto h-24 w-24" src="/icon.svg" />
</div> </div>
<Typography.Title className="text-center" level={2}> <Typography.Title className="text-center" level={2}>
Tianji Tianji

View File

@ -0,0 +1,79 @@
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { t, useTranslation } from '@i18next-toolkit/react';
import { Button } from '@/components/ui/button';
import { useEvent } from '@/hooks/useEvent';
import { useCurrentWorkspaceId } from '@/store/user';
import { trpc } from '@/api/trpc';
import { Card, CardContent, CardFooter } from '@/components/ui/card';
import { CommonWrapper } from '@/components/CommonWrapper';
import {
MonitorInfoEditor,
MonitorInfoEditorValues,
} from '@/components/monitor/MonitorInfoEditor';
import { routeAuthBeforeLoad } from '@/utils/route';
import { useMonitorUpsert } from '@/api/model/monitor';
import { Loading } from '@/components/Loading';
import { ErrorTip } from '@/components/ErrorTip';
import { CommonHeader } from '@/components/CommonHeader';
export const Route = createFileRoute('/monitor/$monitorId/edit')({
beforeLoad: routeAuthBeforeLoad,
component: PageComponent,
});
function PageComponent() {
const { t } = useTranslation();
const { monitorId } = Route.useParams<{ monitorId: string }>();
const workspaceId = useCurrentWorkspaceId();
const navigate = useNavigate();
const mutation = useMonitorUpsert();
const { data: monitor, isLoading } = trpc.monitor.get.useQuery({
monitorId,
workspaceId,
});
const handleSubmit = useEvent(async (values: MonitorInfoEditorValues) => {
const res = await mutation.mutateAsync({
...values,
workspaceId,
});
navigate({
to: '/monitor/$monitorId',
params: {
monitorId: res.id,
},
replace: true,
});
});
if (isLoading) {
return <Loading />;
}
if (!monitor) {
return <ErrorTip />;
}
return (
<CommonWrapper
header={<CommonHeader title={monitor.name} desc={t('Edit')} />}
>
<div className="p-4">
<Card>
<CardContent className="pt-4">
<MonitorInfoEditor
initialValues={
{
...monitor,
notificationIds: monitor.notifications.map((n) => n.id),
} as MonitorInfoEditorValues
}
onSave={handleSubmit}
/>
</CardContent>
</Card>
</div>
</CommonWrapper>
);
}

View File

@ -10,7 +10,7 @@ import { useCurrentWorkspaceId } from '@/store/user';
import { routeAuthBeforeLoad } from '@/utils/route'; import { routeAuthBeforeLoad } from '@/utils/route';
import { createFileRoute } from '@tanstack/react-router'; import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/monitor/$monitorId')({ export const Route = createFileRoute('/monitor/$monitorId/')({
beforeLoad: routeAuthBeforeLoad, beforeLoad: routeAuthBeforeLoad,
component: MonitorDetailComponent, component: MonitorDetailComponent,
}); });

View File

@ -12,6 +12,7 @@ import {
} from '@/components/monitor/MonitorInfoEditor'; } from '@/components/monitor/MonitorInfoEditor';
import { routeAuthBeforeLoad } from '@/utils/route'; import { routeAuthBeforeLoad } from '@/utils/route';
import { useMonitorUpsert } from '@/api/model/monitor'; import { useMonitorUpsert } from '@/api/model/monitor';
import { CommonHeader } from '@/components/CommonHeader';
export const Route = createFileRoute('/monitor/add')({ export const Route = createFileRoute('/monitor/add')({
beforeLoad: routeAuthBeforeLoad, beforeLoad: routeAuthBeforeLoad,
@ -23,7 +24,6 @@ function MonitorAddComponent() {
const workspaceId = useCurrentWorkspaceId(); const workspaceId = useCurrentWorkspaceId();
const navigate = useNavigate(); const navigate = useNavigate();
const mutation = useMonitorUpsert(); const mutation = useMonitorUpsert();
const utils = trpc.useUtils();
const handleSubmit = useEvent(async (values: MonitorInfoEditorValues) => { const handleSubmit = useEvent(async (values: MonitorInfoEditorValues) => {
const res = await mutation.mutateAsync({ const res = await mutation.mutateAsync({
@ -31,8 +31,6 @@ function MonitorAddComponent() {
workspaceId, workspaceId,
}); });
utils.monitor.all.refetch();
navigate({ navigate({
to: '/monitor/$monitorId', to: '/monitor/$monitorId',
params: { params: {
@ -42,9 +40,7 @@ function MonitorAddComponent() {
}); });
return ( return (
<CommonWrapper <CommonWrapper header={<CommonHeader title={t('Add Monitor')} />}>
header={<h1 className="text-xl font-bold">{t('Add Monitor')}</h1>}
>
<div className="p-4"> <div className="p-4">
<Card> <Card>
<CardContent className="pt-4"> <CardContent className="pt-4">

View File

@ -5,17 +5,21 @@ import { ErrorTip } from '@/components/ErrorTip';
import { Loading } from '@/components/Loading'; import { Loading } from '@/components/Loading';
import { NotFoundTip } from '@/components/NotFoundTip'; import { NotFoundTip } from '@/components/NotFoundTip';
import { MonitorStatusPage } from '@/components/monitor/StatusPage'; import { MonitorStatusPage } from '@/components/monitor/StatusPage';
import { ScrollArea } from '@/components/ui/scroll-area'; import { Button } from '@/components/ui/button';
import { routeAuthBeforeLoad } from '@/utils/route'; import { routeAuthBeforeLoad } from '@/utils/route';
import { createFileRoute } from '@tanstack/react-router'; import { useTranslation } from '@i18next-toolkit/react';
import { Link, createFileRoute, useNavigate } from '@tanstack/react-router';
import { LuEye } from 'react-icons/lu';
export const Route = createFileRoute('/page/$slug')({ export const Route = createFileRoute('/page/$slug')({
beforeLoad: routeAuthBeforeLoad, beforeLoad: routeAuthBeforeLoad,
component: PageDetailComponent, component: PageComponent,
}); });
function PageDetailComponent() { function PageComponent() {
const { slug } = Route.useParams<{ slug: string }>(); const { slug } = Route.useParams<{ slug: string }>();
const { t } = useTranslation();
const navigate = useNavigate();
const { data: pageInfo, isLoading } = trpc.monitor.getPageInfo.useQuery({ const { data: pageInfo, isLoading } = trpc.monitor.getPageInfo.useQuery({
slug, slug,
}); });
@ -33,10 +37,21 @@ function PageDetailComponent() {
} }
return ( return (
<CommonWrapper header={<CommonHeader title={pageInfo.title} />}> <CommonWrapper
{/* <ScrollArea className="h-full overflow-hidden"> */} header={
<MonitorStatusPage slug={slug} /> <CommonHeader
{/* </ScrollArea> */} title={pageInfo.title}
actions={
<Link to="/status/$slug" params={{ slug }} target="_blank">
<Button variant="outline" Icon={LuEye}>
{t('Preview')}
</Button>
</Link>
}
/>
}
>
<MonitorStatusPage slug={slug} showBackBtn={false} />
</CommonWrapper> </CommonWrapper>
); );
} }

View File

@ -1,11 +1,10 @@
import { Button, Form, Input, Typography } from 'antd'; import { Button, Form, Input, Typography } from 'antd';
import { useNavigate } from 'react-router';
import { useRequest } from '../hooks/useRequest'; import { useRequest } from '../hooks/useRequest';
import { trpc } from '../api/trpc'; import { trpc } from '../api/trpc';
import { setJWT } from '../api/auth'; import { setJWT } from '../api/auth';
import { setUserInfo } from '../store/user'; import { setUserInfo } from '../store/user';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { createFileRoute } from '@tanstack/react-router'; import { createFileRoute, useNavigate } from '@tanstack/react-router';
export const Route = createFileRoute('/register')({ export const Route = createFileRoute('/register')({
component: RegisterComponent, component: RegisterComponent,
@ -25,14 +24,17 @@ function RegisterComponent() {
setJWT(res.token); setJWT(res.token);
setUserInfo(res.info); setUserInfo(res.info);
navigate('/dashboard'); navigate({
to: '/',
replace: true,
});
}); });
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center dark:bg-gray-900">
<div className="w-80 -translate-y-1/4"> <div className="w-80 -translate-y-1/4">
<div className="text-center"> <div className="text-center">
<img className="h-24 w-24" src="/icon.svg" /> <img className="m-auto h-24 w-24 " src="/icon.svg" />
</div> </div>
<Typography.Title className="text-center" level={2}> <Typography.Title className="text-center" level={2}>
{t('Register Account')} {t('Register Account')}

View File

@ -3,12 +3,14 @@ import { createFileRoute } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { CommonWrapper } from '@/components/CommonWrapper'; import { CommonWrapper } from '@/components/CommonWrapper';
import { ScrollArea } from '@/components/ui/scroll-area'; import { ScrollArea } from '@/components/ui/scroll-area';
import { Button, Card, Form, Input, Modal, Typography } from 'antd'; import { Card, Form, Input, Modal, Popconfirm, Typography } from 'antd';
import { useLogout } from '@/api/model/user'; import { useLogout } from '@/api/model/user';
import { trpc, defaultSuccessHandler, defaultErrorHandler } from '@/api/trpc'; import { trpc, defaultSuccessHandler, defaultErrorHandler } from '@/api/trpc';
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
import { useState } from 'react'; import { useState } from 'react';
import { CommonHeader } from '@/components/CommonHeader'; import { CommonHeader } from '@/components/CommonHeader';
import { Separator } from '@/components/ui/separator';
import { Button } from '@/components/ui/button';
export const Route = createFileRoute('/settings/profile')({ export const Route = createFileRoute('/settings/profile')({
beforeLoad: routeAuthBeforeLoad, beforeLoad: routeAuthBeforeLoad,
@ -45,13 +47,22 @@ function PageComponent() {
</Form.Item> </Form.Item>
<Form.Item label={t('Password')}> <Form.Item label={t('Password')}>
<Button <Button
danger={true} variant="destructive"
onClick={() => setOpenChangePassword(true)} onClick={() => setOpenChangePassword(true)}
> >
{t('Change Password')} {t('Change Password')}
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
<Separator className="mb-4" />
<Popconfirm
title={t('Confirm to logout')}
onConfirm={() => logout()}
>
<Button variant="destructive">{t('Logout')}</Button>
</Popconfirm>
</Card> </Card>
<Modal <Modal

View File

@ -0,0 +1,33 @@
import { trpc } from '@/api/trpc';
import { ErrorTip } from '@/components/ErrorTip';
import { Loading } from '@/components/Loading';
import { NotFoundTip } from '@/components/NotFoundTip';
import { MonitorStatusPage } from '@/components/monitor/StatusPage';
import { Button } from '@/components/ui/button';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/status/$slug')({
component: PageComponent,
});
function PageComponent() {
const { slug } = Route.useParams<{ slug: string }>();
const { data: pageInfo, isLoading } = trpc.monitor.getPageInfo.useQuery({
slug,
});
if (!slug) {
return <ErrorTip />;
}
if (isLoading) {
return <Loading />;
}
if (!pageInfo) {
return <NotFoundTip />;
}
return <MonitorStatusPage slug={slug} />;
}

View File

@ -1,29 +1,13 @@
import { routeAuthBeforeLoad } from '@/utils/route'; import { routeAuthBeforeLoad } from '@/utils/route';
import { createFileRoute, useNavigate } from '@tanstack/react-router'; import { Link, createFileRoute, useNavigate } from '@tanstack/react-router';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { useEvent } from '@/hooks/useEvent';
import { Input } from '@/components/ui/input';
import { useCurrentWorkspaceId } from '@/store/user'; import { useCurrentWorkspaceId } from '@/store/user';
import { trpc } from '@/api/trpc'; import { trpc } from '@/api/trpc';
import { hostnameRegex } from '@tianji/shared';
import { Card, CardContent, CardFooter } from '@/components/ui/card';
import { CommonWrapper } from '@/components/CommonWrapper'; import { CommonWrapper } from '@/components/CommonWrapper';
import { WebsiteOverview } from '@/components/website/WebsiteOverview'; import { WebsiteOverview } from '@/components/website/WebsiteOverview';
import { Empty } from 'antd'; import { Empty } from 'antd';
import { LuPlus } from 'react-icons/lu'; import { LuArrowRight, LuPlus } from 'react-icons/lu';
import { ScrollArea } from '@/components/ui/scroll-area'; import { ScrollArea } from '@/components/ui/scroll-area';
export const Route = createFileRoute('/website/overview')({ export const Route = createFileRoute('/website/overview')({
@ -46,6 +30,7 @@ function WebsiteOverviewComponent() {
<ScrollArea className="h-full overflow-hidden p-4"> <ScrollArea className="h-full overflow-hidden p-4">
{websites.length === 0 && isLoading === false && ( {websites.length === 0 && isLoading === false && (
<Empty <Empty
className="pt-8"
description={ description={
<div className="py-2"> <div className="py-2">
<div className="mb-1"> <div className="mb-1">
@ -68,7 +53,19 @@ function WebsiteOverviewComponent() {
<div className="space-y-10 p-4"> <div className="space-y-10 p-4">
{websites.map((website) => ( {websites.map((website) => (
<WebsiteOverview website={website} /> <WebsiteOverview
website={website}
actions={
<Link
to="/website/$websiteId"
params={{ websiteId: website.id }}
>
<Button size="lg">
{t('View Details')} <LuArrowRight className="ml-2" />
</Button>
</Link>
}
/>
))} ))}
</div> </div>
</ScrollArea> </ScrollArea>