refactor(v2): login view and register and default error handler and more

This commit is contained in:
moonrailgun 2024-04-05 15:29:47 +08:00
parent af4c6f6bd1
commit 4d260dc45e
11 changed files with 99 additions and 45 deletions

View File

@ -25,6 +25,7 @@ import { routeTree } from './routeTree.gen';
import { DefaultNotFound } from './components/DefaultNotFound';
import { TooltipProvider } from './components/ui/tooltip';
import { Toaster } from './components/ui/sonner';
import { DefaultError } from './components/DefaultError';
const router = createRouter({
routeTree,
@ -32,6 +33,7 @@ const router = createRouter({
userInfo: undefined,
},
defaultNotFoundComponent: DefaultNotFound,
defaultErrorComponent: DefaultError,
});
// Register the router instance for type safety

View File

@ -2,7 +2,6 @@ import dayjs from 'dayjs';
import { useUserStore } from '../../store/user';
import { useEvent } from '../../hooks/useEvent';
import { clearJWT } from '../auth';
import { useNavigate } from '@tanstack/react-router';
/**
* Mock
@ -13,15 +12,11 @@ export function getUserTimezone(): string {
}
export function useLogout() {
const navigate = useNavigate();
const logout = useEvent(() => {
window.location.href = '/login'; // not good, need to invest to find better way.
useUserStore.setState({ info: null });
clearJWT();
navigate({
to: '/login',
replace: true,
});
});
return logout;

View File

@ -26,8 +26,12 @@ import { useTranslation } from '@i18next-toolkit/react';
import { trpc } from '@/api/trpc';
import { useCurrentWorkspaceId } from '@/store/user';
import { Button } from './ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
export const CommandPanel: React.FC = React.memo(() => {
interface CommandPanelProps {
isCollapsed: boolean;
}
export const CommandPanel: React.FC<CommandPanelProps> = React.memo((props) => {
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const { t } = useTranslation();
@ -53,15 +57,34 @@ export const CommandPanel: React.FC = React.memo(() => {
return (
<>
<Button
className="w-full !justify-between"
variant="secondary"
size="sm"
Icon={LuSearch}
onClick={() => setOpen(true)}
>
<span className="rounded bg-black/10 px-1 py-0.5">ctrl + k</span>
</Button>
{props.isCollapsed ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
size="icon"
Icon={LuSearch}
onClick={() => setOpen(true)}
/>
</TooltipTrigger>
<TooltipContent side="right" className="flex items-center gap-4">
{t('Search and quick jump')}
<span className="ml-1 rounded bg-black/10 px-1 py-0.5">
ctrl + k
</span>
</TooltipContent>
</Tooltip>
) : (
<Button
className="w-full !justify-between"
variant="secondary"
size="sm"
Icon={LuSearch}
onClick={() => setOpen(true)}
>
<span className="rounded bg-black/10 px-1 py-0.5">ctrl + k</span>
</Button>
)}
<CommandDialog open={open} onOpenChange={setOpen}>
<Command loop={true}>

View File

@ -0,0 +1,30 @@
import React from 'react';
import { Button } from './ui/button';
import { Link } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react';
import { Card, CardContent, CardFooter, CardHeader } from './ui/card';
export const DefaultError: React.FC = React.memo(() => {
const { t } = useTranslation();
return (
<div className="flex h-full w-full items-center justify-center">
<Card className="min-w-[320px] bg-zinc-900">
<CardHeader>
<div className="text-center">
<img className="m-auto h-24 w-24" src="/icon.svg" />
</div>
</CardHeader>
<CardContent className="text-center">
<div>{t('Sorry, but something went wrong')}</div>
</CardContent>
<CardFooter>
<Link className="ml-auto" to="/">
<Button>{t('Back to Homepage')}</Button>
</Link>
</CardFooter>
</Card>
</div>
);
});
DefaultError.displayName = 'DefaultError';

View File

@ -61,7 +61,7 @@ export const DesktopLayout: React.FC<LayoutProps> = React.memo((props) => {
</div>
<Separator />
<div className="p-2">
<CommandPanel />
<CommandPanel isCollapsed={isCollapsed} />
</div>
<Separator />
<Nav

View File

@ -3,10 +3,12 @@ import { useRequest } from '@/hooks/useRequest';
import { setJWT } from '@/api/auth';
import { useGlobalConfig } from '@/hooks/useConfig';
import { trpc } from '@/api/trpc';
import { Button, Form, Input, Typography } from 'antd';
import { Form, Typography } from 'antd';
import { useTranslation } from '@i18next-toolkit/react';
import { setUserInfo } from '@/store/user';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
export const Route = createFileRoute('/login')({
validateSearch: z.object({
@ -59,21 +61,20 @@ function LoginComponent() {
name="username"
rules={[{ required: true }]}
>
<Input size="large" />
<Input />
</Form.Item>
<Form.Item
label={t('Password')}
name="password"
rules={[{ required: true }]}
>
<Input.Password size="large" />
<Input type="password" />
</Form.Item>
<Form.Item>
<Button
type="primary"
size="large"
htmlType="submit"
block={true}
size="lg"
type="submit"
className="w-full"
loading={loading}
>
{t('Login')}
@ -83,9 +84,10 @@ function LoginComponent() {
{allowRegister && (
<Form.Item>
<Button
size="large"
htmlType="button"
block={true}
variant="secondary"
size="lg"
type="button"
className="w-full"
onClick={() => {
navigate({
to: '/register',

View File

@ -15,6 +15,7 @@ import {
useNavigate,
useRouterState,
} from '@tanstack/react-router';
import { compact } from 'lodash-es';
import { LuPlus } from 'react-icons/lu';
export const Route = createFileRoute('/monitor')({
@ -44,7 +45,7 @@ function MonitorComponent() {
showCurrentStatus={true}
/>
),
tags: [item.type],
tags: compact([item.type, item.active ? false : t('Stopped')]),
href: `/monitor/${item.id}`,
}));

View File

@ -1,10 +1,12 @@
import { Button, Form, Input, Typography } from 'antd';
import { Form, Typography } from 'antd';
import { useRequest } from '../hooks/useRequest';
import { trpc } from '../api/trpc';
import { setJWT } from '../api/auth';
import { setUserInfo } from '../store/user';
import { useTranslation } from '@i18next-toolkit/react';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
export const Route = createFileRoute('/register')({
component: RegisterComponent,
@ -31,7 +33,7 @@ function RegisterComponent() {
});
return (
<div className="flex h-full w-full items-center justify-center dark:bg-gray-900">
<div className="flex h-full w-full items-center justify-center dark:bg-zinc-900">
<div className="w-80 -translate-y-1/4">
<div className="text-center">
<img className="m-auto h-24 w-24 " src="/icon.svg" />
@ -45,21 +47,20 @@ function RegisterComponent() {
name="username"
rules={[{ required: true }]}
>
<Input size="large" />
<Input />
</Form.Item>
<Form.Item
label={t('Password')}
name="password"
rules={[{ required: true }]}
>
<Input.Password size="large" />
<Input type="password" />
</Form.Item>
<Form.Item>
<Button
type="primary"
size="large"
htmlType="submit"
block={true}
size="lg"
type="submit"
className="w-full"
loading={loading}
>
{t('Register')}

View File

@ -1,14 +1,9 @@
import { trpc } from '@/api/trpc';
import { CommonHeader } from '@/components/CommonHeader';
import { CommonList } from '@/components/CommonList';
import { CommonWrapper } from '@/components/CommonWrapper';
import { Button } from '@/components/ui/button';
import { useDataReady } from '@/hooks/useDataReady';
import { useEvent } from '@/hooks/useEvent';
import { LayoutV2 } from '@/pages/LayoutV2';
import { useCurrentWorkspaceId } from '@/store/user';
import { routeAuthBeforeLoad } from '@/utils/route';
import { Trans, useTranslation } from '@i18next-toolkit/react';
import { useTranslation } from '@i18next-toolkit/react';
import {
createFileRoute,
useNavigate,
@ -18,10 +13,10 @@ import { useEffect } from 'react';
export const Route = createFileRoute('/settings')({
beforeLoad: routeAuthBeforeLoad,
component: TelemetryComponent,
component: PageComponent,
});
function TelemetryComponent() {
function PageComponent() {
const { t } = useTranslation();
const navigate = useNavigate();
const pathname = useRouterState({

View File

@ -8,6 +8,7 @@ import { useEvent } from '@/hooks/useEvent';
import { LayoutV2 } from '@/pages/LayoutV2';
import { useCurrentWorkspaceId } from '@/store/user';
import { routeAuthBeforeLoad } from '@/utils/route';
import { cn } from '@/utils/style';
import { useTranslation } from '@i18next-toolkit/react';
import {
createFileRoute,
@ -70,6 +71,9 @@ function WebsiteComponent() {
actions={
<div className="space-x-2">
<Button
className={cn(
pathname === '/website/overview' && '!bg-muted'
)}
variant="outline"
Icon={LuEye}
onClick={handleClickOverview}

View File

@ -54,6 +54,7 @@ function WebsiteOverviewComponent() {
<div className="space-y-10 p-4">
{websites.map((website) => (
<WebsiteOverview
key={website.id}
website={website}
actions={
<Link