feature: add subscription manage
This commit is contained in:
40
apps/webui/src/presentation/hooks/use-debounded-skeleton.ts
Normal file
40
apps/webui/src/presentation/hooks/use-debounded-skeleton.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useStateRef } from './use-state-ref.ts';
|
||||
export interface UseDebouncedSkeletonProps {
|
||||
minSkeletonDuration?: number;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export function useDebouncedSkeleton({
|
||||
minSkeletonDuration = 100,
|
||||
loading,
|
||||
}: UseDebouncedSkeletonProps) {
|
||||
const [showSkeleton, setShowSkeleton, showSkeletonRef] = useStateRef(loading);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading && !showSkeleton) {
|
||||
setShowSkeleton(true);
|
||||
}
|
||||
if (!loading && showSkeleton) {
|
||||
const timeout = setTimeout(() => {
|
||||
if (showSkeletonRef.current) {
|
||||
setShowSkeleton(false);
|
||||
}
|
||||
}, minSkeletonDuration);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}
|
||||
}, [
|
||||
loading,
|
||||
showSkeleton,
|
||||
setShowSkeleton,
|
||||
minSkeletonDuration,
|
||||
showSkeletonRef,
|
||||
]);
|
||||
|
||||
return {
|
||||
showSkeleton,
|
||||
};
|
||||
}
|
||||
18
apps/webui/src/presentation/hooks/use-event.ts
Normal file
18
apps/webui/src/presentation/hooks/use-event.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useCallback, useInsertionEffect, useRef } from 'react';
|
||||
|
||||
export function useEvent<
|
||||
const T extends (
|
||||
...args: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
any[]
|
||||
) => void,
|
||||
>(fn: T): T {
|
||||
const ref = useRef<T | null>(fn);
|
||||
useInsertionEffect(() => {
|
||||
ref.current = fn;
|
||||
}, [fn]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return useCallback((...args: any) => {
|
||||
const latestFn = ref.current!;
|
||||
return latestFn(...args);
|
||||
}, []) as unknown as T;
|
||||
}
|
||||
19
apps/webui/src/presentation/hooks/use-mobile.ts
Normal file
19
apps/webui/src/presentation/hooks/use-mobile.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import * as React from "react"
|
||||
|
||||
const MOBILE_BREAKPOINT = 768
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
}
|
||||
mql.addEventListener("change", onChange)
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
return () => mql.removeEventListener("change", onChange)
|
||||
}, [])
|
||||
|
||||
return !!isMobile
|
||||
}
|
||||
28
apps/webui/src/presentation/hooks/use-state-ref.ts
Normal file
28
apps/webui/src/presentation/hooks/use-state-ref.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import {
|
||||
type Dispatch,
|
||||
type RefObject,
|
||||
type SetStateAction,
|
||||
useCallback,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
export function useStateRef<T>(
|
||||
initialValue: T
|
||||
): [T, Dispatch<SetStateAction<T>>, RefObject<T>] {
|
||||
const [state, _setState] = useState(initialValue);
|
||||
const ref = useRef(initialValue);
|
||||
|
||||
const setState = useCallback((value: T | ((prev: T) => T)) => {
|
||||
let nextValue: T;
|
||||
if (typeof value === 'function') {
|
||||
nextValue = (value as (prev: T) => T)(ref.current);
|
||||
} else {
|
||||
nextValue = value;
|
||||
}
|
||||
ref.current = nextValue;
|
||||
_setState(nextValue);
|
||||
}, []);
|
||||
|
||||
return [state, setState, ref] as const;
|
||||
}
|
||||
663
apps/webui/src/presentation/routeTree.gen.ts
Normal file
663
apps/webui/src/presentation/routeTree.gen.ts
Normal file
@@ -0,0 +1,663 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
// Import Routes
|
||||
|
||||
import { Route as R404Import } from './routes/404.tsx';
|
||||
import { Route as rootRoute } from './routes/__root.tsx';
|
||||
import { Route as AppExploreExploreImport } from './routes/_app/_explore/explore.tsx';
|
||||
import { Route as AppExploreFeedImport } from './routes/_app/_explore/feed.tsx';
|
||||
import { Route as AppBangumiManageImport } from './routes/_app/bangumi/manage.tsx';
|
||||
import { Route as AppBangumiRouteImport } from './routes/_app/bangumi/route.tsx';
|
||||
import { Route as AppPlaygroundGraphqlApiImport } from './routes/_app/playground/graphql-api.tsx';
|
||||
import { Route as AppPlaygroundRouteImport } from './routes/_app/playground/route.tsx';
|
||||
import { Route as AppRouteImport } from './routes/_app/route.tsx';
|
||||
import { Route as AppSettingsDownloaderImport } from './routes/_app/settings/downloader.tsx';
|
||||
import { Route as AppSettingsRouteImport } from './routes/_app/settings/route.tsx';
|
||||
import { Route as AppSubscriptionsCreateImport } from './routes/_app/subscriptions/create.tsx';
|
||||
import { Route as AppSubscriptionsDetailSubscriptionIdImport } from './routes/_app/subscriptions/detail.$subscriptionId.tsx';
|
||||
import { Route as AppSubscriptionsEditSubscriptionIdImport } from './routes/_app/subscriptions/edit.$subscriptionId.tsx';
|
||||
import { Route as AppSubscriptionsManageImport } from './routes/_app/subscriptions/manage.tsx';
|
||||
import { Route as AppSubscriptionsRouteImport } from './routes/_app/subscriptions/route.tsx';
|
||||
import { Route as AboutImport } from './routes/about.tsx';
|
||||
import { Route as AuthOidcCallbackImport } from './routes/auth/oidc/callback.tsx';
|
||||
import { Route as AuthSignInImport } from './routes/auth/sign-in.tsx';
|
||||
import { Route as AuthSignUpImport } from './routes/auth/sign-up.tsx';
|
||||
import { Route as IndexImport } from './routes/index.tsx';
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const AboutRoute = AboutImport.update({
|
||||
id: '/about',
|
||||
path: '/about',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const R404Route = R404Import.update({
|
||||
id: '/404',
|
||||
path: '/404',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const AppRouteRoute = AppRouteImport.update({
|
||||
id: '/_app',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const AuthSignUpRoute = AuthSignUpImport.update({
|
||||
id: '/auth/sign-up',
|
||||
path: '/auth/sign-up',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const AuthSignInRoute = AuthSignInImport.update({
|
||||
id: '/auth/sign-in',
|
||||
path: '/auth/sign-in',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const AppSubscriptionsRouteRoute = AppSubscriptionsRouteImport.update({
|
||||
id: '/subscriptions',
|
||||
path: '/subscriptions',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppSettingsRouteRoute = AppSettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
path: '/settings',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppPlaygroundRouteRoute = AppPlaygroundRouteImport.update({
|
||||
id: '/playground',
|
||||
path: '/playground',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppBangumiRouteRoute = AppBangumiRouteImport.update({
|
||||
id: '/bangumi',
|
||||
path: '/bangumi',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AuthOidcCallbackRoute = AuthOidcCallbackImport.update({
|
||||
id: '/auth/oidc/callback',
|
||||
path: '/auth/oidc/callback',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any);
|
||||
|
||||
const AppSubscriptionsManageRoute = AppSubscriptionsManageImport.update({
|
||||
id: '/manage',
|
||||
path: '/manage',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppSubscriptionsCreateRoute = AppSubscriptionsCreateImport.update({
|
||||
id: '/create',
|
||||
path: '/create',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppSettingsDownloaderRoute = AppSettingsDownloaderImport.update({
|
||||
id: '/downloader',
|
||||
path: '/downloader',
|
||||
getParentRoute: () => AppSettingsRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppPlaygroundGraphqlApiRoute = AppPlaygroundGraphqlApiImport.update({
|
||||
id: '/graphql-api',
|
||||
path: '/graphql-api',
|
||||
getParentRoute: () => AppPlaygroundRouteRoute,
|
||||
} as any).lazy(() =>
|
||||
import('./routes/_app/playground/graphql-api.lazy.tsx').then((d) => d.Route)
|
||||
);
|
||||
|
||||
const AppBangumiManageRoute = AppBangumiManageImport.update({
|
||||
id: '/manage',
|
||||
path: '/manage',
|
||||
getParentRoute: () => AppBangumiRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppExploreFeedRoute = AppExploreFeedImport.update({
|
||||
id: '/_explore/feed',
|
||||
path: '/feed',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppExploreExploreRoute = AppExploreExploreImport.update({
|
||||
id: '/_explore/explore',
|
||||
path: '/explore',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppSubscriptionsEditSubscriptionIdRoute =
|
||||
AppSubscriptionsEditSubscriptionIdImport.update({
|
||||
id: '/edit/$subscriptionId',
|
||||
path: '/edit/$subscriptionId',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any);
|
||||
|
||||
const AppSubscriptionsDetailSubscriptionIdRoute =
|
||||
AppSubscriptionsDetailSubscriptionIdImport.update({
|
||||
id: '/detail/$subscriptionId',
|
||||
path: '/detail/$subscriptionId',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any);
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/': {
|
||||
id: '/';
|
||||
path: '/';
|
||||
fullPath: '/';
|
||||
preLoaderRoute: typeof IndexImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/_app': {
|
||||
id: '/_app';
|
||||
path: '';
|
||||
fullPath: '';
|
||||
preLoaderRoute: typeof AppRouteImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/404': {
|
||||
id: '/404';
|
||||
path: '/404';
|
||||
fullPath: '/404';
|
||||
preLoaderRoute: typeof R404Import;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/about': {
|
||||
id: '/about';
|
||||
path: '/about';
|
||||
fullPath: '/about';
|
||||
preLoaderRoute: typeof AboutImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/_app/bangumi': {
|
||||
id: '/_app/bangumi';
|
||||
path: '/bangumi';
|
||||
fullPath: '/bangumi';
|
||||
preLoaderRoute: typeof AppBangumiRouteImport;
|
||||
parentRoute: typeof AppRouteImport;
|
||||
};
|
||||
'/_app/playground': {
|
||||
id: '/_app/playground';
|
||||
path: '/playground';
|
||||
fullPath: '/playground';
|
||||
preLoaderRoute: typeof AppPlaygroundRouteImport;
|
||||
parentRoute: typeof AppRouteImport;
|
||||
};
|
||||
'/_app/settings': {
|
||||
id: '/_app/settings';
|
||||
path: '/settings';
|
||||
fullPath: '/settings';
|
||||
preLoaderRoute: typeof AppSettingsRouteImport;
|
||||
parentRoute: typeof AppRouteImport;
|
||||
};
|
||||
'/_app/subscriptions': {
|
||||
id: '/_app/subscriptions';
|
||||
path: '/subscriptions';
|
||||
fullPath: '/subscriptions';
|
||||
preLoaderRoute: typeof AppSubscriptionsRouteImport;
|
||||
parentRoute: typeof AppRouteImport;
|
||||
};
|
||||
'/auth/sign-in': {
|
||||
id: '/auth/sign-in';
|
||||
path: '/auth/sign-in';
|
||||
fullPath: '/auth/sign-in';
|
||||
preLoaderRoute: typeof AuthSignInImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/auth/sign-up': {
|
||||
id: '/auth/sign-up';
|
||||
path: '/auth/sign-up';
|
||||
fullPath: '/auth/sign-up';
|
||||
preLoaderRoute: typeof AuthSignUpImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/_app/_explore/explore': {
|
||||
id: '/_app/_explore/explore';
|
||||
path: '/explore';
|
||||
fullPath: '/explore';
|
||||
preLoaderRoute: typeof AppExploreExploreImport;
|
||||
parentRoute: typeof AppRouteImport;
|
||||
};
|
||||
'/_app/_explore/feed': {
|
||||
id: '/_app/_explore/feed';
|
||||
path: '/feed';
|
||||
fullPath: '/feed';
|
||||
preLoaderRoute: typeof AppExploreFeedImport;
|
||||
parentRoute: typeof AppRouteImport;
|
||||
};
|
||||
'/_app/bangumi/manage': {
|
||||
id: '/_app/bangumi/manage';
|
||||
path: '/manage';
|
||||
fullPath: '/bangumi/manage';
|
||||
preLoaderRoute: typeof AppBangumiManageImport;
|
||||
parentRoute: typeof AppBangumiRouteImport;
|
||||
};
|
||||
'/_app/playground/graphql-api': {
|
||||
id: '/_app/playground/graphql-api';
|
||||
path: '/graphql-api';
|
||||
fullPath: '/playground/graphql-api';
|
||||
preLoaderRoute: typeof AppPlaygroundGraphqlApiImport;
|
||||
parentRoute: typeof AppPlaygroundRouteImport;
|
||||
};
|
||||
'/_app/settings/downloader': {
|
||||
id: '/_app/settings/downloader';
|
||||
path: '/downloader';
|
||||
fullPath: '/settings/downloader';
|
||||
preLoaderRoute: typeof AppSettingsDownloaderImport;
|
||||
parentRoute: typeof AppSettingsRouteImport;
|
||||
};
|
||||
'/_app/subscriptions/create': {
|
||||
id: '/_app/subscriptions/create';
|
||||
path: '/create';
|
||||
fullPath: '/subscriptions/create';
|
||||
preLoaderRoute: typeof AppSubscriptionsCreateImport;
|
||||
parentRoute: typeof AppSubscriptionsRouteImport;
|
||||
};
|
||||
'/_app/subscriptions/manage': {
|
||||
id: '/_app/subscriptions/manage';
|
||||
path: '/manage';
|
||||
fullPath: '/subscriptions/manage';
|
||||
preLoaderRoute: typeof AppSubscriptionsManageImport;
|
||||
parentRoute: typeof AppSubscriptionsRouteImport;
|
||||
};
|
||||
'/auth/oidc/callback': {
|
||||
id: '/auth/oidc/callback';
|
||||
path: '/auth/oidc/callback';
|
||||
fullPath: '/auth/oidc/callback';
|
||||
preLoaderRoute: typeof AuthOidcCallbackImport;
|
||||
parentRoute: typeof rootRoute;
|
||||
};
|
||||
'/_app/subscriptions/detail/$subscriptionId': {
|
||||
id: '/_app/subscriptions/detail/$subscriptionId';
|
||||
path: '/detail/$subscriptionId';
|
||||
fullPath: '/subscriptions/detail/$subscriptionId';
|
||||
preLoaderRoute: typeof AppSubscriptionsDetailSubscriptionIdImport;
|
||||
parentRoute: typeof AppSubscriptionsRouteImport;
|
||||
};
|
||||
'/_app/subscriptions/edit/$subscriptionId': {
|
||||
id: '/_app/subscriptions/edit/$subscriptionId';
|
||||
path: '/edit/$subscriptionId';
|
||||
fullPath: '/subscriptions/edit/$subscriptionId';
|
||||
preLoaderRoute: typeof AppSubscriptionsEditSubscriptionIdImport;
|
||||
parentRoute: typeof AppSubscriptionsRouteImport;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export the route tree
|
||||
|
||||
interface AppBangumiRouteRouteChildren {
|
||||
AppBangumiManageRoute: typeof AppBangumiManageRoute;
|
||||
}
|
||||
|
||||
const AppBangumiRouteRouteChildren: AppBangumiRouteRouteChildren = {
|
||||
AppBangumiManageRoute: AppBangumiManageRoute,
|
||||
};
|
||||
|
||||
const AppBangumiRouteRouteWithChildren = AppBangumiRouteRoute._addFileChildren(
|
||||
AppBangumiRouteRouteChildren
|
||||
);
|
||||
|
||||
interface AppPlaygroundRouteRouteChildren {
|
||||
AppPlaygroundGraphqlApiRoute: typeof AppPlaygroundGraphqlApiRoute;
|
||||
}
|
||||
|
||||
const AppPlaygroundRouteRouteChildren: AppPlaygroundRouteRouteChildren = {
|
||||
AppPlaygroundGraphqlApiRoute: AppPlaygroundGraphqlApiRoute,
|
||||
};
|
||||
|
||||
const AppPlaygroundRouteRouteWithChildren =
|
||||
AppPlaygroundRouteRoute._addFileChildren(AppPlaygroundRouteRouteChildren);
|
||||
|
||||
interface AppSettingsRouteRouteChildren {
|
||||
AppSettingsDownloaderRoute: typeof AppSettingsDownloaderRoute;
|
||||
}
|
||||
|
||||
const AppSettingsRouteRouteChildren: AppSettingsRouteRouteChildren = {
|
||||
AppSettingsDownloaderRoute: AppSettingsDownloaderRoute,
|
||||
};
|
||||
|
||||
const AppSettingsRouteRouteWithChildren =
|
||||
AppSettingsRouteRoute._addFileChildren(AppSettingsRouteRouteChildren);
|
||||
|
||||
interface AppSubscriptionsRouteRouteChildren {
|
||||
AppSubscriptionsCreateRoute: typeof AppSubscriptionsCreateRoute;
|
||||
AppSubscriptionsManageRoute: typeof AppSubscriptionsManageRoute;
|
||||
AppSubscriptionsDetailSubscriptionIdRoute: typeof AppSubscriptionsDetailSubscriptionIdRoute;
|
||||
AppSubscriptionsEditSubscriptionIdRoute: typeof AppSubscriptionsEditSubscriptionIdRoute;
|
||||
}
|
||||
|
||||
const AppSubscriptionsRouteRouteChildren: AppSubscriptionsRouteRouteChildren = {
|
||||
AppSubscriptionsCreateRoute: AppSubscriptionsCreateRoute,
|
||||
AppSubscriptionsManageRoute: AppSubscriptionsManageRoute,
|
||||
AppSubscriptionsDetailSubscriptionIdRoute:
|
||||
AppSubscriptionsDetailSubscriptionIdRoute,
|
||||
AppSubscriptionsEditSubscriptionIdRoute:
|
||||
AppSubscriptionsEditSubscriptionIdRoute,
|
||||
};
|
||||
|
||||
const AppSubscriptionsRouteRouteWithChildren =
|
||||
AppSubscriptionsRouteRoute._addFileChildren(
|
||||
AppSubscriptionsRouteRouteChildren
|
||||
);
|
||||
|
||||
interface AppRouteRouteChildren {
|
||||
AppBangumiRouteRoute: typeof AppBangumiRouteRouteWithChildren;
|
||||
AppPlaygroundRouteRoute: typeof AppPlaygroundRouteRouteWithChildren;
|
||||
AppSettingsRouteRoute: typeof AppSettingsRouteRouteWithChildren;
|
||||
AppSubscriptionsRouteRoute: typeof AppSubscriptionsRouteRouteWithChildren;
|
||||
AppExploreExploreRoute: typeof AppExploreExploreRoute;
|
||||
AppExploreFeedRoute: typeof AppExploreFeedRoute;
|
||||
}
|
||||
|
||||
const AppRouteRouteChildren: AppRouteRouteChildren = {
|
||||
AppBangumiRouteRoute: AppBangumiRouteRouteWithChildren,
|
||||
AppPlaygroundRouteRoute: AppPlaygroundRouteRouteWithChildren,
|
||||
AppSettingsRouteRoute: AppSettingsRouteRouteWithChildren,
|
||||
AppSubscriptionsRouteRoute: AppSubscriptionsRouteRouteWithChildren,
|
||||
AppExploreExploreRoute: AppExploreExploreRoute,
|
||||
AppExploreFeedRoute: AppExploreFeedRoute,
|
||||
};
|
||||
|
||||
const AppRouteRouteWithChildren = AppRouteRoute._addFileChildren(
|
||||
AppRouteRouteChildren
|
||||
);
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute;
|
||||
'': typeof AppRouteRouteWithChildren;
|
||||
'/404': typeof R404Route;
|
||||
'/about': typeof AboutRoute;
|
||||
'/bangumi': typeof AppBangumiRouteRouteWithChildren;
|
||||
'/playground': typeof AppPlaygroundRouteRouteWithChildren;
|
||||
'/settings': typeof AppSettingsRouteRouteWithChildren;
|
||||
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren;
|
||||
'/auth/sign-in': typeof AuthSignInRoute;
|
||||
'/auth/sign-up': typeof AuthSignUpRoute;
|
||||
'/explore': typeof AppExploreExploreRoute;
|
||||
'/feed': typeof AppExploreFeedRoute;
|
||||
'/bangumi/manage': typeof AppBangumiManageRoute;
|
||||
'/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute;
|
||||
'/settings/downloader': typeof AppSettingsDownloaderRoute;
|
||||
'/subscriptions/create': typeof AppSubscriptionsCreateRoute;
|
||||
'/subscriptions/manage': typeof AppSubscriptionsManageRoute;
|
||||
'/auth/oidc/callback': typeof AuthOidcCallbackRoute;
|
||||
'/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute;
|
||||
'/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute;
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute;
|
||||
'': typeof AppRouteRouteWithChildren;
|
||||
'/404': typeof R404Route;
|
||||
'/about': typeof AboutRoute;
|
||||
'/bangumi': typeof AppBangumiRouteRouteWithChildren;
|
||||
'/playground': typeof AppPlaygroundRouteRouteWithChildren;
|
||||
'/settings': typeof AppSettingsRouteRouteWithChildren;
|
||||
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren;
|
||||
'/auth/sign-in': typeof AuthSignInRoute;
|
||||
'/auth/sign-up': typeof AuthSignUpRoute;
|
||||
'/explore': typeof AppExploreExploreRoute;
|
||||
'/feed': typeof AppExploreFeedRoute;
|
||||
'/bangumi/manage': typeof AppBangumiManageRoute;
|
||||
'/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute;
|
||||
'/settings/downloader': typeof AppSettingsDownloaderRoute;
|
||||
'/subscriptions/create': typeof AppSubscriptionsCreateRoute;
|
||||
'/subscriptions/manage': typeof AppSubscriptionsManageRoute;
|
||||
'/auth/oidc/callback': typeof AuthOidcCallbackRoute;
|
||||
'/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute;
|
||||
'/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute;
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute;
|
||||
'/': typeof IndexRoute;
|
||||
'/_app': typeof AppRouteRouteWithChildren;
|
||||
'/404': typeof R404Route;
|
||||
'/about': typeof AboutRoute;
|
||||
'/_app/bangumi': typeof AppBangumiRouteRouteWithChildren;
|
||||
'/_app/playground': typeof AppPlaygroundRouteRouteWithChildren;
|
||||
'/_app/settings': typeof AppSettingsRouteRouteWithChildren;
|
||||
'/_app/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren;
|
||||
'/auth/sign-in': typeof AuthSignInRoute;
|
||||
'/auth/sign-up': typeof AuthSignUpRoute;
|
||||
'/_app/_explore/explore': typeof AppExploreExploreRoute;
|
||||
'/_app/_explore/feed': typeof AppExploreFeedRoute;
|
||||
'/_app/bangumi/manage': typeof AppBangumiManageRoute;
|
||||
'/_app/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute;
|
||||
'/_app/settings/downloader': typeof AppSettingsDownloaderRoute;
|
||||
'/_app/subscriptions/create': typeof AppSubscriptionsCreateRoute;
|
||||
'/_app/subscriptions/manage': typeof AppSubscriptionsManageRoute;
|
||||
'/auth/oidc/callback': typeof AuthOidcCallbackRoute;
|
||||
'/_app/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute;
|
||||
'/_app/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute;
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath;
|
||||
fullPaths:
|
||||
| '/'
|
||||
| ''
|
||||
| '/404'
|
||||
| '/about'
|
||||
| '/bangumi'
|
||||
| '/playground'
|
||||
| '/settings'
|
||||
| '/subscriptions'
|
||||
| '/auth/sign-in'
|
||||
| '/auth/sign-up'
|
||||
| '/explore'
|
||||
| '/feed'
|
||||
| '/bangumi/manage'
|
||||
| '/playground/graphql-api'
|
||||
| '/settings/downloader'
|
||||
| '/subscriptions/create'
|
||||
| '/subscriptions/manage'
|
||||
| '/auth/oidc/callback'
|
||||
| '/subscriptions/detail/$subscriptionId'
|
||||
| '/subscriptions/edit/$subscriptionId';
|
||||
fileRoutesByTo: FileRoutesByTo;
|
||||
to:
|
||||
| '/'
|
||||
| ''
|
||||
| '/404'
|
||||
| '/about'
|
||||
| '/bangumi'
|
||||
| '/playground'
|
||||
| '/settings'
|
||||
| '/subscriptions'
|
||||
| '/auth/sign-in'
|
||||
| '/auth/sign-up'
|
||||
| '/explore'
|
||||
| '/feed'
|
||||
| '/bangumi/manage'
|
||||
| '/playground/graphql-api'
|
||||
| '/settings/downloader'
|
||||
| '/subscriptions/create'
|
||||
| '/subscriptions/manage'
|
||||
| '/auth/oidc/callback'
|
||||
| '/subscriptions/detail/$subscriptionId'
|
||||
| '/subscriptions/edit/$subscriptionId';
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/_app'
|
||||
| '/404'
|
||||
| '/about'
|
||||
| '/_app/bangumi'
|
||||
| '/_app/playground'
|
||||
| '/_app/settings'
|
||||
| '/_app/subscriptions'
|
||||
| '/auth/sign-in'
|
||||
| '/auth/sign-up'
|
||||
| '/_app/_explore/explore'
|
||||
| '/_app/_explore/feed'
|
||||
| '/_app/bangumi/manage'
|
||||
| '/_app/playground/graphql-api'
|
||||
| '/_app/settings/downloader'
|
||||
| '/_app/subscriptions/create'
|
||||
| '/_app/subscriptions/manage'
|
||||
| '/auth/oidc/callback'
|
||||
| '/_app/subscriptions/detail/$subscriptionId'
|
||||
| '/_app/subscriptions/edit/$subscriptionId';
|
||||
fileRoutesById: FileRoutesById;
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute;
|
||||
AppRouteRoute: typeof AppRouteRouteWithChildren;
|
||||
R404Route: typeof R404Route;
|
||||
AboutRoute: typeof AboutRoute;
|
||||
AuthSignInRoute: typeof AuthSignInRoute;
|
||||
AuthSignUpRoute: typeof AuthSignUpRoute;
|
||||
AuthOidcCallbackRoute: typeof AuthOidcCallbackRoute;
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AppRouteRoute: AppRouteRouteWithChildren,
|
||||
R404Route: R404Route,
|
||||
AboutRoute: AboutRoute,
|
||||
AuthSignInRoute: AuthSignInRoute,
|
||||
AuthSignUpRoute: AuthSignUpRoute,
|
||||
AuthOidcCallbackRoute: AuthOidcCallbackRoute,
|
||||
};
|
||||
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>();
|
||||
|
||||
/* ROUTE_MANIFEST_START
|
||||
{
|
||||
"routes": {
|
||||
"__root__": {
|
||||
"filePath": "__root.tsx",
|
||||
"children": [
|
||||
"/",
|
||||
"/_app",
|
||||
"/404",
|
||||
"/about",
|
||||
"/auth/sign-in",
|
||||
"/auth/sign-up",
|
||||
"/auth/oidc/callback"
|
||||
]
|
||||
},
|
||||
"/": {
|
||||
"filePath": "index.tsx"
|
||||
},
|
||||
"/_app": {
|
||||
"filePath": "_app/route.tsx",
|
||||
"children": [
|
||||
"/_app/bangumi",
|
||||
"/_app/playground",
|
||||
"/_app/settings",
|
||||
"/_app/subscriptions",
|
||||
"/_app/_explore/explore",
|
||||
"/_app/_explore/feed"
|
||||
]
|
||||
},
|
||||
"/404": {
|
||||
"filePath": "404.tsx"
|
||||
},
|
||||
"/about": {
|
||||
"filePath": "about.tsx"
|
||||
},
|
||||
"/_app/bangumi": {
|
||||
"filePath": "_app/bangumi/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/bangumi/manage"
|
||||
]
|
||||
},
|
||||
"/_app/playground": {
|
||||
"filePath": "_app/playground/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/playground/graphql-api"
|
||||
]
|
||||
},
|
||||
"/_app/settings": {
|
||||
"filePath": "_app/settings/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/settings/downloader"
|
||||
]
|
||||
},
|
||||
"/_app/subscriptions": {
|
||||
"filePath": "_app/subscriptions/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/subscriptions/create",
|
||||
"/_app/subscriptions/manage",
|
||||
"/_app/subscriptions/detail/$subscriptionId",
|
||||
"/_app/subscriptions/edit/$subscriptionId"
|
||||
]
|
||||
},
|
||||
"/auth/sign-in": {
|
||||
"filePath": "auth/sign-in.tsx"
|
||||
},
|
||||
"/auth/sign-up": {
|
||||
"filePath": "auth/sign-up.tsx"
|
||||
},
|
||||
"/_app/_explore/explore": {
|
||||
"filePath": "_app/_explore/explore.tsx",
|
||||
"parent": "/_app"
|
||||
},
|
||||
"/_app/_explore/feed": {
|
||||
"filePath": "_app/_explore/feed.tsx",
|
||||
"parent": "/_app"
|
||||
},
|
||||
"/_app/bangumi/manage": {
|
||||
"filePath": "_app/bangumi/manage.tsx",
|
||||
"parent": "/_app/bangumi"
|
||||
},
|
||||
"/_app/playground/graphql-api": {
|
||||
"filePath": "_app/playground/graphql-api.tsx",
|
||||
"parent": "/_app/playground"
|
||||
},
|
||||
"/_app/settings/downloader": {
|
||||
"filePath": "_app/settings/downloader.tsx",
|
||||
"parent": "/_app/settings"
|
||||
},
|
||||
"/_app/subscriptions/create": {
|
||||
"filePath": "_app/subscriptions/create.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
},
|
||||
"/_app/subscriptions/manage": {
|
||||
"filePath": "_app/subscriptions/manage.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
},
|
||||
"/auth/oidc/callback": {
|
||||
"filePath": "auth/oidc/callback.tsx"
|
||||
},
|
||||
"/_app/subscriptions/detail/$subscriptionId": {
|
||||
"filePath": "_app/subscriptions/detail.$subscriptionId.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
},
|
||||
"/_app/subscriptions/edit/$subscriptionId": {
|
||||
"filePath": "_app/subscriptions/edit.$subscriptionId.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
}
|
||||
}
|
||||
}
|
||||
ROUTE_MANIFEST_END */
|
||||
6
apps/webui/src/presentation/routes/404.tsx
Normal file
6
apps/webui/src/presentation/routes/404.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import { AppNotFoundComponent } from '@/components/layout/app-not-found';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/404')({
|
||||
component: AppNotFoundComponent,
|
||||
});
|
||||
26
apps/webui/src/presentation/routes/__root.tsx
Normal file
26
apps/webui/src/presentation/routes/__root.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import type {
|
||||
RouteStateDataOption,
|
||||
RouterContext,
|
||||
} from '@/infra/routes/traits';
|
||||
import { Outlet, createRootRouteWithContext } from '@tanstack/react-router';
|
||||
import { Home } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { Toaster } from 'sonner';
|
||||
|
||||
export const RootRouteComponent = memo(() => {
|
||||
return (
|
||||
<>
|
||||
<Outlet />
|
||||
<Toaster position="top-right" />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export const Route = createRootRouteWithContext<RouterContext>()({
|
||||
component: RootRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: {
|
||||
icon: Home,
|
||||
},
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
15
apps/webui/src/presentation/routes/_app/_explore/explore.tsx
Normal file
15
apps/webui/src/presentation/routes/_app/_explore/explore.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/_explore/explore')({
|
||||
component: ExploreRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: {
|
||||
label: 'Explore',
|
||||
},
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
function ExploreRouteComponent() {
|
||||
return <div>Hello "/_app/explore"!</div>;
|
||||
}
|
||||
15
apps/webui/src/presentation/routes/_app/_explore/feed.tsx
Normal file
15
apps/webui/src/presentation/routes/_app/_explore/feed.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/_explore/feed')({
|
||||
component: FeedRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: {
|
||||
label: 'Feed',
|
||||
},
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
function FeedRouteComponent() {
|
||||
return <div>Hello "/_app/feed"!</div>;
|
||||
}
|
||||
13
apps/webui/src/presentation/routes/_app/bangumi/manage.tsx
Normal file
13
apps/webui/src/presentation/routes/_app/bangumi/manage.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/bangumi/manage')({
|
||||
component: BangumiManageRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: { label: 'Manage' },
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
function BangumiManageRouteComponent() {
|
||||
return <div>Hello "/_app/bangumi/manage"!</div>;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { buildVirtualBranchRouteOptions } from '@/infra/routes/utils';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/bangumi')(
|
||||
buildVirtualBranchRouteOptions({
|
||||
title: 'Bangumi',
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useAuth } from '@/app/auth/hooks';
|
||||
import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit';
|
||||
import { createLazyFileRoute } from '@tanstack/react-router';
|
||||
import { GraphiQL } from 'graphiql';
|
||||
import { useCallback } from 'react';
|
||||
import 'graphiql/graphiql.css';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
export const Route = createLazyFileRoute('/_app/playground/graphql-api')({
|
||||
component: PlaygroundGraphQLApiRouteComponent,
|
||||
});
|
||||
|
||||
function PlaygroundGraphQLApiRouteComponent() {
|
||||
const { authProvider } = useAuth();
|
||||
|
||||
const fetcher: Fetcher = useCallback(
|
||||
async (props) => {
|
||||
const authHeaders = await firstValueFrom(authProvider.getAuthHeaders());
|
||||
return createGraphiQLFetcher({
|
||||
url: '/api/graphql',
|
||||
headers: authHeaders,
|
||||
})(props);
|
||||
},
|
||||
[authProvider]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-id="graphiql-playground-container"
|
||||
className="h-full overflow-hidden rounded-lg"
|
||||
>
|
||||
<GraphiQL fetcher={fetcher} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { AppSkeleton } from '@/components/layout/app-skeleton';
|
||||
import { buildLeafRouteStaticData } from '@/infra/routes/utils';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/playground/graphql-api')({
|
||||
staticData: buildLeafRouteStaticData({ title: 'GraphQL Api' }),
|
||||
pendingComponent: AppSkeleton,
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import { buildVirtualBranchRouteOptions } from '@/infra/routes/utils';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/playground')(
|
||||
buildVirtualBranchRouteOptions({
|
||||
title: 'Playground',
|
||||
})
|
||||
);
|
||||
16
apps/webui/src/presentation/routes/_app/route.tsx
Normal file
16
apps/webui/src/presentation/routes/_app/route.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { beforeLoadGuard } from '@/app/auth/guard';
|
||||
import { AppAside } from '@/components/layout/app-layout';
|
||||
import { Outlet, createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app')({
|
||||
component: AppLayoutRoute,
|
||||
beforeLoad: beforeLoadGuard,
|
||||
});
|
||||
|
||||
function AppLayoutRoute() {
|
||||
return (
|
||||
<AppAside extractBreadcrumbFromRoutes>
|
||||
<Outlet />
|
||||
</AppAside>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { buildLeafRouteStaticData } from '@/infra/routes/utils';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/settings/downloader')({
|
||||
component: SettingsDownloaderRouteComponent,
|
||||
staticData: buildLeafRouteStaticData({
|
||||
title: 'Downloader',
|
||||
}),
|
||||
});
|
||||
|
||||
function SettingsDownloaderRouteComponent() {
|
||||
return <div>Hello "/_app/settings/downloader"!</div>;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { buildVirtualBranchRouteOptions } from '@/infra/routes/utils';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/settings')(
|
||||
buildVirtualBranchRouteOptions({
|
||||
title: 'Settings',
|
||||
})
|
||||
);
|
||||
108
apps/webui/src/presentation/routes/_app/subscriptions/-defs.ts
Normal file
108
apps/webui/src/presentation/routes/_app/subscriptions/-defs.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import type {
|
||||
GetSubscriptionDetailQuery,
|
||||
GetSubscriptionsQuery,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
|
||||
export const GET_SUBSCRIPTIONS = gql`
|
||||
query GetSubscriptions(
|
||||
$page: PageInput!,
|
||||
$filters: SubscriptionsFilterInput!,
|
||||
$orderBy: SubscriptionsOrderInput!
|
||||
) {
|
||||
subscriptions(
|
||||
pagination: {
|
||||
page: $page
|
||||
}
|
||||
filters: $filters
|
||||
orderBy: $orderBy
|
||||
) {
|
||||
nodes {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
displayName
|
||||
category
|
||||
sourceUrl
|
||||
enabled
|
||||
}
|
||||
paginationInfo {
|
||||
total
|
||||
pages
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type SubscriptionDto =
|
||||
GetSubscriptionsQuery['subscriptions']['nodes'][number];
|
||||
|
||||
export const UPDATE_SUBSCRIPTIONS = gql`
|
||||
mutation UpdateSubscriptions(
|
||||
$data: SubscriptionsUpdateInput!,
|
||||
$filters: SubscriptionsFilterInput!,
|
||||
) {
|
||||
subscriptionsUpdate (
|
||||
data: $data
|
||||
filter: $filters
|
||||
) {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
displayName
|
||||
category
|
||||
sourceUrl
|
||||
enabled
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_SUBSCRIPTIONS = gql`
|
||||
mutation DeleteSubscriptions($filters: SubscriptionsFilterInput) {
|
||||
subscriptionsDelete(filter: $filters)
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_SUBSCRIPTION_DETAIL = gql`
|
||||
query GetSubscriptionDetail ($id: Int!) {
|
||||
subscriptions(filters: { id: {
|
||||
eq: $id
|
||||
} }) {
|
||||
nodes {
|
||||
id
|
||||
displayName
|
||||
createdAt
|
||||
updatedAt
|
||||
category
|
||||
sourceUrl
|
||||
enabled
|
||||
bangumi {
|
||||
nodes {
|
||||
createdAt
|
||||
updatedAt
|
||||
id
|
||||
mikanBangumiId
|
||||
displayName
|
||||
rawName
|
||||
season
|
||||
seasonRaw
|
||||
fansub
|
||||
mikanFansubId
|
||||
rssLink
|
||||
posterLink
|
||||
savePath
|
||||
deleted
|
||||
homepage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export type SubscriptionDetailDto =
|
||||
GetSubscriptionDetailQuery['subscriptions']['nodes'][number];
|
||||
|
||||
export type SubscriptionDetailBangumiDto =
|
||||
SubscriptionDetailDto['bangumi']['nodes'][number];
|
||||
234
apps/webui/src/presentation/routes/_app/subscriptions/create.tsx
Normal file
234
apps/webui/src/presentation/routes/_app/subscriptions/create.tsx
Normal file
@@ -0,0 +1,234 @@
|
||||
import { useAuth } from '@/app/auth/hooks';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { gql, useMutation } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import { useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
export const Route = createFileRoute('/_app/subscriptions/create')({
|
||||
component: SubscriptionCreateRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: { label: 'Create' },
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
type SubscriptionFormValues = {
|
||||
displayName: string;
|
||||
sourceUrl: string;
|
||||
category: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
const CREATE_SUBSCRIPTION_MUTATION = gql`
|
||||
mutation CreateSubscription($input: SubscriptionsInsertInput!) {
|
||||
subscriptionsCreateOne(data: $input) {
|
||||
id
|
||||
displayName
|
||||
sourceUrl
|
||||
enabled
|
||||
category
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function SubscriptionCreateRouteComponent() {
|
||||
const { authData } = useAuth();
|
||||
console.log(JSON.stringify(authData, null, 2));
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const form = useForm<SubscriptionFormValues>({
|
||||
defaultValues: {
|
||||
displayName: '',
|
||||
sourceUrl: '',
|
||||
category: 'mikan',
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
const [createSubscription] = useMutation(CREATE_SUBSCRIPTION_MUTATION);
|
||||
|
||||
const onSubmit = async (data: SubscriptionFormValues) => {
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
const response = await createSubscription({
|
||||
variables: {
|
||||
input: {
|
||||
category: data.category,
|
||||
displayName: data.displayName,
|
||||
sourceUrl: data.sourceUrl,
|
||||
enabled: data.enabled,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (response.errors) {
|
||||
throw new Error(
|
||||
response.errors[0]?.message || 'Failed to create subscription'
|
||||
);
|
||||
}
|
||||
|
||||
toast.success('Subscription created successfully');
|
||||
navigate({ to: '/subscriptions/manage' });
|
||||
} catch (error) {
|
||||
console.error('Failed to create subscription:', error);
|
||||
toast.error(
|
||||
`Subscription creation failed: ${
|
||||
error instanceof Error ? error.message : 'Unknown error'
|
||||
}`
|
||||
);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Create Bangumi Subscription</CardTitle>
|
||||
<CardDescription>Add a new bangumi subscription source</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="category"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Source Type</FormLabel>
|
||||
<Select
|
||||
disabled
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
defaultValue="mikan"
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select source type" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="mikan">mikan</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
Currently only mikan source is supported
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="displayName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Display Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Enter subscription display name"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Set an easily recognizable name for this subscription
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sourceUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Source URL</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Enter subscription source URL"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Copy the RSS subscription link from the source website, e.g.
|
||||
https://mikanani.me/RSS/Bangumi?bangumiId=3141&subgroupid=370
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel className="text-base">
|
||||
Enable Subscription
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
Enable this subscription immediately after creation
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
<CardFooter className="flex justify-between">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate({ to: '/subscriptions/manage' })}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
onClick={form.handleSubmit(onSubmit)}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? 'Creating...' : 'Create Subscription'}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { GetSubscriptionDetailQuery } from '@/infra/graphql/gql/graphql';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { GET_SUBSCRIPTION_DETAIL } from './-defs.ts';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/subscriptions/detail/$subscriptionId'
|
||||
)({
|
||||
component: DetailRouteComponent,
|
||||
});
|
||||
|
||||
function DetailRouteComponent() {
|
||||
const { subscriptionId } = Route.useParams();
|
||||
const { data, loading, error } = useQuery<GetSubscriptionDetailQuery>(
|
||||
GET_SUBSCRIPTION_DETAIL,
|
||||
{
|
||||
variables: {
|
||||
id: Number.parseInt(subscriptionId),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <div>Error: {error.message}</div>;
|
||||
}
|
||||
|
||||
const detail = data?.subscriptions?.nodes?.[0];
|
||||
|
||||
return (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(detail, null, 2) }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute(
|
||||
'/_app/subscriptions/edit/$subscriptionId'
|
||||
)({
|
||||
component: RouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: { label: 'Edit' },
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/subscriptions/edit/$subscription-id"!</div>;
|
||||
}
|
||||
289
apps/webui/src/presentation/routes/_app/subscriptions/manage.tsx
Normal file
289
apps/webui/src/presentation/routes/_app/subscriptions/manage.tsx
Normal file
@@ -0,0 +1,289 @@
|
||||
import { DataTablePagination } from '@/components/ui/data-table-pagination';
|
||||
import { DataTableViewOptions } from '@/components/ui/data-table-view-options';
|
||||
import { QueryErrorView } from '@/components/ui/query-error-view.tsx';
|
||||
import { Skeleton } from '@/components/ui/skeleton.tsx';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import type {
|
||||
GetSubscriptionsQuery,
|
||||
SubscriptionsUpdateInput,
|
||||
} from '@/infra/graphql/gql/graphql';
|
||||
import type { RouteStateDataOption } from '@/infra/routes/traits';
|
||||
import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton.ts';
|
||||
import { useEvent } from '@/presentation/hooks/use-event.ts';
|
||||
import { useMutation, useQuery } from '@apollo/client';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
import { useNavigate } from '@tanstack/react-router';
|
||||
import {
|
||||
type ColumnDef,
|
||||
type PaginationState,
|
||||
type Row,
|
||||
type SortingState,
|
||||
type VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { DataTableRowActions } from '../../../../components/ui/data-table-row-actions.tsx';
|
||||
import {
|
||||
DELETE_SUBSCRIPTIONS,
|
||||
GET_SUBSCRIPTIONS,
|
||||
type SubscriptionDto,
|
||||
UPDATE_SUBSCRIPTIONS,
|
||||
} from './-defs.ts';
|
||||
|
||||
export const Route = createFileRoute('/_app/subscriptions/manage')({
|
||||
component: SubscriptionManageRouteComponent,
|
||||
staticData: {
|
||||
breadcrumb: { label: 'Manage' },
|
||||
} satisfies RouteStateDataOption,
|
||||
});
|
||||
|
||||
function SubscriptionManageRouteComponent() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const { loading, error, data, refetch } = useQuery<GetSubscriptionsQuery>(
|
||||
GET_SUBSCRIPTIONS,
|
||||
{
|
||||
variables: {
|
||||
page: {
|
||||
page: pagination.pageIndex,
|
||||
limit: pagination.pageSize,
|
||||
},
|
||||
filters: {},
|
||||
orderBy: {},
|
||||
},
|
||||
refetchWritePolicy: 'overwrite',
|
||||
nextFetchPolicy: 'network-only',
|
||||
}
|
||||
);
|
||||
const [updateSubscription] = useMutation(UPDATE_SUBSCRIPTIONS);
|
||||
const [deleteSubscription] = useMutation(DELETE_SUBSCRIPTIONS);
|
||||
const { showSkeleton } = useDebouncedSkeleton({ loading });
|
||||
|
||||
const subscriptions = data?.subscriptions;
|
||||
|
||||
const handleUpdateRecord = useEvent(
|
||||
(row: Row<SubscriptionDto>) => async (data: SubscriptionsUpdateInput) => {
|
||||
const result = await updateSubscription({
|
||||
variables: {
|
||||
data,
|
||||
filters: {
|
||||
id: {
|
||||
eq: row.original.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (result.errors) {
|
||||
toast.error(result.errors[0].message);
|
||||
return;
|
||||
}
|
||||
const refetchResult = await refetch();
|
||||
if (refetchResult.errors) {
|
||||
toast.error(refetchResult.errors[0].message);
|
||||
return;
|
||||
}
|
||||
toast.success('Subscription updated');
|
||||
}
|
||||
);
|
||||
|
||||
const handleDeleteRecord = useEvent(
|
||||
(row: Row<SubscriptionDto>) => async () => {
|
||||
const result = await deleteSubscription({
|
||||
variables: { filters: { id: { eq: row.original.id } } },
|
||||
});
|
||||
if (result.errors) {
|
||||
toast.error(result.errors[0].message);
|
||||
return;
|
||||
}
|
||||
const refetchResult = await refetch();
|
||||
if (refetchResult.errors) {
|
||||
toast.error(refetchResult.errors[0].message);
|
||||
return;
|
||||
}
|
||||
toast.success('Subscription deleted');
|
||||
}
|
||||
);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
const cs: ColumnDef<SubscriptionDto>[] = [
|
||||
{
|
||||
header: 'Enabled',
|
||||
accessorKey: 'enabled',
|
||||
cell: ({ row }) => {
|
||||
const enabled = row.original.enabled;
|
||||
return (
|
||||
<div className="px-1">
|
||||
<Switch
|
||||
checked={enabled}
|
||||
onCheckedChange={(enabled) =>
|
||||
handleUpdateRecord(row)({ enabled: enabled })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
enableResizing: true,
|
||||
},
|
||||
{
|
||||
header: 'Name',
|
||||
accessorKey: 'displayName',
|
||||
cell: ({ row }) => {
|
||||
const displayName = row.original.displayName;
|
||||
return (
|
||||
<div className="whitespace-normal break-words">{displayName}</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Category',
|
||||
accessorKey: 'category',
|
||||
},
|
||||
{
|
||||
header: 'Source URL',
|
||||
accessorKey: 'sourceUrl',
|
||||
cell: ({ row }) => {
|
||||
const sourceUrl = row.original.sourceUrl;
|
||||
return (
|
||||
<div className="whitespace-normal break-words">{sourceUrl}</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
cell: ({ row }) => (
|
||||
<DataTableRowActions
|
||||
row={row}
|
||||
getId={(row) => row.original.id}
|
||||
showDetail
|
||||
showEdit
|
||||
showDelete
|
||||
onDetail={() => {
|
||||
navigate({
|
||||
to: '/subscriptions/detail/$subscriptionId',
|
||||
params: { subscriptionId: `${row.original.id}` },
|
||||
});
|
||||
}}
|
||||
onEdit={() => {
|
||||
navigate({
|
||||
to: '/subscriptions/edit/$subscriptionId',
|
||||
params: { subscriptionId: `${row.original.id}` },
|
||||
});
|
||||
}}
|
||||
onDelete={handleDeleteRecord(row)}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
return cs;
|
||||
}, [handleUpdateRecord, handleDeleteRecord, navigate]);
|
||||
|
||||
const table = useReactTable({
|
||||
data: data?.subscriptions?.nodes ?? [],
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onSortingChange: setSorting,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
pageCount: subscriptions?.paginationInfo?.pages,
|
||||
rowCount: subscriptions?.paginationInfo?.total,
|
||||
state: {
|
||||
pagination,
|
||||
sorting,
|
||||
columnVisibility,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return <QueryErrorView message={error.message} onRetry={refetch} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container mx-auto space-y-4 rounded-md">
|
||||
<div className="flex items-center py-4">
|
||||
<DataTableViewOptions table={table} />
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{showSkeleton &&
|
||||
Array.from(new Array(pagination.pageSize)).map((_, index) => (
|
||||
<TableRow key={index}>
|
||||
{table.getVisibleLeafColumns().map((column) => (
|
||||
<TableCell key={column.id}>
|
||||
<Skeleton className="h-8" />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
{!showSkeleton &&
|
||||
(table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && 'selected'}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<DataTablePagination table={table} showSelectedRowCount={false} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { buildVirtualBranchRouteOptions } from '@/infra/routes/utils';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/_app/subscriptions')(
|
||||
buildVirtualBranchRouteOptions({
|
||||
title: 'Subscriptions',
|
||||
})
|
||||
);
|
||||
9
apps/webui/src/presentation/routes/about.tsx
Normal file
9
apps/webui/src/presentation/routes/about.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/about')({
|
||||
component: About,
|
||||
});
|
||||
|
||||
function About() {
|
||||
return <div class="p-2">Hello from About!</div>;
|
||||
}
|
||||
94
apps/webui/src/presentation/routes/auth/oidc/callback.tsx
Normal file
94
apps/webui/src/presentation/routes/auth/oidc/callback.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import { authContextFromInjector } from '@/app/auth/context';
|
||||
import { useAuth } from '@/app/auth/hooks';
|
||||
import { ProLink } from '@/components/ui/pro-link';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { AUTH_METHOD } from '@/infra/auth/defs';
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
import { useAtom } from 'jotai/react';
|
||||
import { atomWithObservable } from 'jotai/utils';
|
||||
import { EventTypes } from 'oidc-client-rx';
|
||||
import { useMemo } from 'react';
|
||||
import { filter, map } from 'rxjs';
|
||||
|
||||
export const Route = createFileRoute('/auth/oidc/callback')({
|
||||
component: OidcCallbackRouteComponent,
|
||||
beforeLoad: ({ context }) => {
|
||||
const { authProvider } = authContextFromInjector(context.injector);
|
||||
if (authProvider.authMethod !== AUTH_METHOD.OIDC) {
|
||||
throw redirect({ to: '/' });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function OidcCallbackRouteComponent() {
|
||||
const { authService } = useAuth();
|
||||
|
||||
const isLoading = useAtom(
|
||||
useMemo(
|
||||
() =>
|
||||
atomWithObservable(() =>
|
||||
authService.checkAuthResultEvent$.pipe(map(Boolean))
|
||||
),
|
||||
[authService.checkAuthResultEvent$]
|
||||
)
|
||||
);
|
||||
|
||||
const checkAuthResultError = useAtom(
|
||||
useMemo(
|
||||
() =>
|
||||
atomWithObservable(() =>
|
||||
authService.checkAuthResultEvent$.pipe(
|
||||
filter(
|
||||
(
|
||||
e
|
||||
): e is {
|
||||
type: EventTypes.CheckingAuthFinishedWithError;
|
||||
value: string;
|
||||
} => e.type === EventTypes.CheckingAuthFinishedWithError
|
||||
),
|
||||
map((e) => e.value)
|
||||
)
|
||||
),
|
||||
[authService.checkAuthResultEvent$]
|
||||
)
|
||||
);
|
||||
|
||||
const renderBackToHome = () => {
|
||||
return (
|
||||
<ProLink
|
||||
to="/"
|
||||
className="inline-flex h-10 items-center rounded-md border border-gray-200 bg-white px-8 font-medium text-sm shadow-sm transition-colors hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:focus-visible:ring-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-50"
|
||||
>
|
||||
Return to website
|
||||
</ProLink>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-svh items-center px-4 py-12 sm:px-6 md:px-8 lg:px-12 xl:px-16">
|
||||
<div className="w-full space-y-6 text-center">
|
||||
<div className="space-y-6">
|
||||
<div className="flex justify-center font-bold text-4xl tracking-tighter sm:text-5xl">
|
||||
<Spinner variant="circle-filled" size="48" />
|
||||
</div>
|
||||
{isLoading && (
|
||||
<p className="text-gray-500">
|
||||
Processing OIDC authentication callback...
|
||||
</p>
|
||||
)}
|
||||
{checkAuthResultError && (
|
||||
<p className="text-gray-500">
|
||||
Failed to handle OIDC callback: {checkAuthResultError}
|
||||
</p>
|
||||
)}
|
||||
{!isLoading && !checkAuthResultError && (
|
||||
<p className="text-gray-500">
|
||||
Succeed to handle OIDC authentication callback.
|
||||
</p>
|
||||
)}
|
||||
{renderBackToHome()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
apps/webui/src/presentation/routes/auth/sign-in.tsx
Normal file
9
apps/webui/src/presentation/routes/auth/sign-in.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/auth/sign-in')({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/auth/sign-in"!</div>;
|
||||
}
|
||||
9
apps/webui/src/presentation/routes/auth/sign-up.tsx
Normal file
9
apps/webui/src/presentation/routes/auth/sign-up.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/auth/sign-up')({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/auth/sign-up"!</div>;
|
||||
}
|
||||
15
apps/webui/src/presentation/routes/index.tsx
Normal file
15
apps/webui/src/presentation/routes/index.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import { AppAside } from '@/components/layout/app-layout';
|
||||
import { AppSkeleton } from '@/components/layout/app-skeleton';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
component: HomeRouteComponent,
|
||||
});
|
||||
|
||||
function HomeRouteComponent() {
|
||||
return (
|
||||
<AppAside extractBreadcrumbFromRoutes>
|
||||
<AppSkeleton />
|
||||
</AppAside>
|
||||
);
|
||||
}
|
||||
689
apps/webui/src/presentation/routes/routeTree.gen.ts
Normal file
689
apps/webui/src/presentation/routes/routeTree.gen.ts
Normal file
@@ -0,0 +1,689 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
// Import Routes
|
||||
|
||||
import { Route as rootRoute } from './__root'
|
||||
import { Route as AboutImport } from './about'
|
||||
import { Route as R404Import } from './404'
|
||||
import { Route as AppRouteImport } from './_app/route'
|
||||
import { Route as IndexImport } from './index'
|
||||
import { Route as RouteTreeGenImport } from './routeTree.gen'
|
||||
import { Route as AuthSignUpImport } from './auth/sign-up'
|
||||
import { Route as AuthSignInImport } from './auth/sign-in'
|
||||
import { Route as AppSubscriptionsRouteImport } from './_app/subscriptions/route'
|
||||
import { Route as AppSettingsRouteImport } from './_app/settings/route'
|
||||
import { Route as AppPlaygroundRouteImport } from './_app/playground/route'
|
||||
import { Route as AppBangumiRouteImport } from './_app/bangumi/route'
|
||||
import { Route as AuthOidcCallbackImport } from './auth/oidc/callback'
|
||||
import { Route as AppSubscriptionsManageImport } from './_app/subscriptions/manage'
|
||||
import { Route as AppSubscriptionsCreateImport } from './_app/subscriptions/create'
|
||||
import { Route as AppSettingsDownloaderImport } from './_app/settings/downloader'
|
||||
import { Route as AppPlaygroundGraphqlApiImport } from './_app/playground/graphql-api'
|
||||
import { Route as AppBangumiManageImport } from './_app/bangumi/manage'
|
||||
import { Route as AppExploreFeedImport } from './_app/_explore/feed'
|
||||
import { Route as AppExploreExploreImport } from './_app/_explore/explore'
|
||||
import { Route as AppSubscriptionsEditSubscriptionIdImport } from './_app/subscriptions/edit.$subscriptionId'
|
||||
import { Route as AppSubscriptionsDetailSubscriptionIdImport } from './_app/subscriptions/detail.$subscriptionId'
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const AboutRoute = AboutImport.update({
|
||||
id: '/about',
|
||||
path: '/about',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const R404Route = R404Import.update({
|
||||
id: '/404',
|
||||
path: '/404',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const AppRouteRoute = AppRouteImport.update({
|
||||
id: '/_app',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const RouteTreeGenRoute = RouteTreeGenImport.update({
|
||||
id: '/routeTree/gen',
|
||||
path: '/routeTree/gen',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const AuthSignUpRoute = AuthSignUpImport.update({
|
||||
id: '/auth/sign-up',
|
||||
path: '/auth/sign-up',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const AuthSignInRoute = AuthSignInImport.update({
|
||||
id: '/auth/sign-in',
|
||||
path: '/auth/sign-in',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const AppSubscriptionsRouteRoute = AppSubscriptionsRouteImport.update({
|
||||
id: '/subscriptions',
|
||||
path: '/subscriptions',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppSettingsRouteRoute = AppSettingsRouteImport.update({
|
||||
id: '/settings',
|
||||
path: '/settings',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppPlaygroundRouteRoute = AppPlaygroundRouteImport.update({
|
||||
id: '/playground',
|
||||
path: '/playground',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppBangumiRouteRoute = AppBangumiRouteImport.update({
|
||||
id: '/bangumi',
|
||||
path: '/bangumi',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AuthOidcCallbackRoute = AuthOidcCallbackImport.update({
|
||||
id: '/auth/oidc/callback',
|
||||
path: '/auth/oidc/callback',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const AppSubscriptionsManageRoute = AppSubscriptionsManageImport.update({
|
||||
id: '/manage',
|
||||
path: '/manage',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppSubscriptionsCreateRoute = AppSubscriptionsCreateImport.update({
|
||||
id: '/create',
|
||||
path: '/create',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppSettingsDownloaderRoute = AppSettingsDownloaderImport.update({
|
||||
id: '/downloader',
|
||||
path: '/downloader',
|
||||
getParentRoute: () => AppSettingsRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppPlaygroundGraphqlApiRoute = AppPlaygroundGraphqlApiImport.update({
|
||||
id: '/graphql-api',
|
||||
path: '/graphql-api',
|
||||
getParentRoute: () => AppPlaygroundRouteRoute,
|
||||
} as any).lazy(() =>
|
||||
import('./_app/playground/graphql-api.lazy').then((d) => d.Route),
|
||||
)
|
||||
|
||||
const AppBangumiManageRoute = AppBangumiManageImport.update({
|
||||
id: '/manage',
|
||||
path: '/manage',
|
||||
getParentRoute: () => AppBangumiRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppExploreFeedRoute = AppExploreFeedImport.update({
|
||||
id: '/_explore/feed',
|
||||
path: '/feed',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppExploreExploreRoute = AppExploreExploreImport.update({
|
||||
id: '/_explore/explore',
|
||||
path: '/explore',
|
||||
getParentRoute: () => AppRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppSubscriptionsEditSubscriptionIdRoute =
|
||||
AppSubscriptionsEditSubscriptionIdImport.update({
|
||||
id: '/edit/$subscriptionId',
|
||||
path: '/edit/$subscriptionId',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any)
|
||||
|
||||
const AppSubscriptionsDetailSubscriptionIdRoute =
|
||||
AppSubscriptionsDetailSubscriptionIdImport.update({
|
||||
id: '/detail/$subscriptionId',
|
||||
path: '/detail/$subscriptionId',
|
||||
getParentRoute: () => AppSubscriptionsRouteRoute,
|
||||
} as any)
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/_app': {
|
||||
id: '/_app'
|
||||
path: ''
|
||||
fullPath: ''
|
||||
preLoaderRoute: typeof AppRouteImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/404': {
|
||||
id: '/404'
|
||||
path: '/404'
|
||||
fullPath: '/404'
|
||||
preLoaderRoute: typeof R404Import
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/about': {
|
||||
id: '/about'
|
||||
path: '/about'
|
||||
fullPath: '/about'
|
||||
preLoaderRoute: typeof AboutImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/_app/bangumi': {
|
||||
id: '/_app/bangumi'
|
||||
path: '/bangumi'
|
||||
fullPath: '/bangumi'
|
||||
preLoaderRoute: typeof AppBangumiRouteImport
|
||||
parentRoute: typeof AppRouteImport
|
||||
}
|
||||
'/_app/playground': {
|
||||
id: '/_app/playground'
|
||||
path: '/playground'
|
||||
fullPath: '/playground'
|
||||
preLoaderRoute: typeof AppPlaygroundRouteImport
|
||||
parentRoute: typeof AppRouteImport
|
||||
}
|
||||
'/_app/settings': {
|
||||
id: '/_app/settings'
|
||||
path: '/settings'
|
||||
fullPath: '/settings'
|
||||
preLoaderRoute: typeof AppSettingsRouteImport
|
||||
parentRoute: typeof AppRouteImport
|
||||
}
|
||||
'/_app/subscriptions': {
|
||||
id: '/_app/subscriptions'
|
||||
path: '/subscriptions'
|
||||
fullPath: '/subscriptions'
|
||||
preLoaderRoute: typeof AppSubscriptionsRouteImport
|
||||
parentRoute: typeof AppRouteImport
|
||||
}
|
||||
'/auth/sign-in': {
|
||||
id: '/auth/sign-in'
|
||||
path: '/auth/sign-in'
|
||||
fullPath: '/auth/sign-in'
|
||||
preLoaderRoute: typeof AuthSignInImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/auth/sign-up': {
|
||||
id: '/auth/sign-up'
|
||||
path: '/auth/sign-up'
|
||||
fullPath: '/auth/sign-up'
|
||||
preLoaderRoute: typeof AuthSignUpImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/routeTree/gen': {
|
||||
id: '/routeTree/gen'
|
||||
path: '/routeTree/gen'
|
||||
fullPath: '/routeTree/gen'
|
||||
preLoaderRoute: typeof RouteTreeGenImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/_app/_explore/explore': {
|
||||
id: '/_app/_explore/explore'
|
||||
path: '/explore'
|
||||
fullPath: '/explore'
|
||||
preLoaderRoute: typeof AppExploreExploreImport
|
||||
parentRoute: typeof AppRouteImport
|
||||
}
|
||||
'/_app/_explore/feed': {
|
||||
id: '/_app/_explore/feed'
|
||||
path: '/feed'
|
||||
fullPath: '/feed'
|
||||
preLoaderRoute: typeof AppExploreFeedImport
|
||||
parentRoute: typeof AppRouteImport
|
||||
}
|
||||
'/_app/bangumi/manage': {
|
||||
id: '/_app/bangumi/manage'
|
||||
path: '/manage'
|
||||
fullPath: '/bangumi/manage'
|
||||
preLoaderRoute: typeof AppBangumiManageImport
|
||||
parentRoute: typeof AppBangumiRouteImport
|
||||
}
|
||||
'/_app/playground/graphql-api': {
|
||||
id: '/_app/playground/graphql-api'
|
||||
path: '/graphql-api'
|
||||
fullPath: '/playground/graphql-api'
|
||||
preLoaderRoute: typeof AppPlaygroundGraphqlApiImport
|
||||
parentRoute: typeof AppPlaygroundRouteImport
|
||||
}
|
||||
'/_app/settings/downloader': {
|
||||
id: '/_app/settings/downloader'
|
||||
path: '/downloader'
|
||||
fullPath: '/settings/downloader'
|
||||
preLoaderRoute: typeof AppSettingsDownloaderImport
|
||||
parentRoute: typeof AppSettingsRouteImport
|
||||
}
|
||||
'/_app/subscriptions/create': {
|
||||
id: '/_app/subscriptions/create'
|
||||
path: '/create'
|
||||
fullPath: '/subscriptions/create'
|
||||
preLoaderRoute: typeof AppSubscriptionsCreateImport
|
||||
parentRoute: typeof AppSubscriptionsRouteImport
|
||||
}
|
||||
'/_app/subscriptions/manage': {
|
||||
id: '/_app/subscriptions/manage'
|
||||
path: '/manage'
|
||||
fullPath: '/subscriptions/manage'
|
||||
preLoaderRoute: typeof AppSubscriptionsManageImport
|
||||
parentRoute: typeof AppSubscriptionsRouteImport
|
||||
}
|
||||
'/auth/oidc/callback': {
|
||||
id: '/auth/oidc/callback'
|
||||
path: '/auth/oidc/callback'
|
||||
fullPath: '/auth/oidc/callback'
|
||||
preLoaderRoute: typeof AuthOidcCallbackImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/_app/subscriptions/detail/$subscriptionId': {
|
||||
id: '/_app/subscriptions/detail/$subscriptionId'
|
||||
path: '/detail/$subscriptionId'
|
||||
fullPath: '/subscriptions/detail/$subscriptionId'
|
||||
preLoaderRoute: typeof AppSubscriptionsDetailSubscriptionIdImport
|
||||
parentRoute: typeof AppSubscriptionsRouteImport
|
||||
}
|
||||
'/_app/subscriptions/edit/$subscriptionId': {
|
||||
id: '/_app/subscriptions/edit/$subscriptionId'
|
||||
path: '/edit/$subscriptionId'
|
||||
fullPath: '/subscriptions/edit/$subscriptionId'
|
||||
preLoaderRoute: typeof AppSubscriptionsEditSubscriptionIdImport
|
||||
parentRoute: typeof AppSubscriptionsRouteImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export the route tree
|
||||
|
||||
interface AppBangumiRouteRouteChildren {
|
||||
AppBangumiManageRoute: typeof AppBangumiManageRoute
|
||||
}
|
||||
|
||||
const AppBangumiRouteRouteChildren: AppBangumiRouteRouteChildren = {
|
||||
AppBangumiManageRoute: AppBangumiManageRoute,
|
||||
}
|
||||
|
||||
const AppBangumiRouteRouteWithChildren = AppBangumiRouteRoute._addFileChildren(
|
||||
AppBangumiRouteRouteChildren,
|
||||
)
|
||||
|
||||
interface AppPlaygroundRouteRouteChildren {
|
||||
AppPlaygroundGraphqlApiRoute: typeof AppPlaygroundGraphqlApiRoute
|
||||
}
|
||||
|
||||
const AppPlaygroundRouteRouteChildren: AppPlaygroundRouteRouteChildren = {
|
||||
AppPlaygroundGraphqlApiRoute: AppPlaygroundGraphqlApiRoute,
|
||||
}
|
||||
|
||||
const AppPlaygroundRouteRouteWithChildren =
|
||||
AppPlaygroundRouteRoute._addFileChildren(AppPlaygroundRouteRouteChildren)
|
||||
|
||||
interface AppSettingsRouteRouteChildren {
|
||||
AppSettingsDownloaderRoute: typeof AppSettingsDownloaderRoute
|
||||
}
|
||||
|
||||
const AppSettingsRouteRouteChildren: AppSettingsRouteRouteChildren = {
|
||||
AppSettingsDownloaderRoute: AppSettingsDownloaderRoute,
|
||||
}
|
||||
|
||||
const AppSettingsRouteRouteWithChildren =
|
||||
AppSettingsRouteRoute._addFileChildren(AppSettingsRouteRouteChildren)
|
||||
|
||||
interface AppSubscriptionsRouteRouteChildren {
|
||||
AppSubscriptionsCreateRoute: typeof AppSubscriptionsCreateRoute
|
||||
AppSubscriptionsManageRoute: typeof AppSubscriptionsManageRoute
|
||||
AppSubscriptionsDetailSubscriptionIdRoute: typeof AppSubscriptionsDetailSubscriptionIdRoute
|
||||
AppSubscriptionsEditSubscriptionIdRoute: typeof AppSubscriptionsEditSubscriptionIdRoute
|
||||
}
|
||||
|
||||
const AppSubscriptionsRouteRouteChildren: AppSubscriptionsRouteRouteChildren = {
|
||||
AppSubscriptionsCreateRoute: AppSubscriptionsCreateRoute,
|
||||
AppSubscriptionsManageRoute: AppSubscriptionsManageRoute,
|
||||
AppSubscriptionsDetailSubscriptionIdRoute:
|
||||
AppSubscriptionsDetailSubscriptionIdRoute,
|
||||
AppSubscriptionsEditSubscriptionIdRoute:
|
||||
AppSubscriptionsEditSubscriptionIdRoute,
|
||||
}
|
||||
|
||||
const AppSubscriptionsRouteRouteWithChildren =
|
||||
AppSubscriptionsRouteRoute._addFileChildren(
|
||||
AppSubscriptionsRouteRouteChildren,
|
||||
)
|
||||
|
||||
interface AppRouteRouteChildren {
|
||||
AppBangumiRouteRoute: typeof AppBangumiRouteRouteWithChildren
|
||||
AppPlaygroundRouteRoute: typeof AppPlaygroundRouteRouteWithChildren
|
||||
AppSettingsRouteRoute: typeof AppSettingsRouteRouteWithChildren
|
||||
AppSubscriptionsRouteRoute: typeof AppSubscriptionsRouteRouteWithChildren
|
||||
AppExploreExploreRoute: typeof AppExploreExploreRoute
|
||||
AppExploreFeedRoute: typeof AppExploreFeedRoute
|
||||
}
|
||||
|
||||
const AppRouteRouteChildren: AppRouteRouteChildren = {
|
||||
AppBangumiRouteRoute: AppBangumiRouteRouteWithChildren,
|
||||
AppPlaygroundRouteRoute: AppPlaygroundRouteRouteWithChildren,
|
||||
AppSettingsRouteRoute: AppSettingsRouteRouteWithChildren,
|
||||
AppSubscriptionsRouteRoute: AppSubscriptionsRouteRouteWithChildren,
|
||||
AppExploreExploreRoute: AppExploreExploreRoute,
|
||||
AppExploreFeedRoute: AppExploreFeedRoute,
|
||||
}
|
||||
|
||||
const AppRouteRouteWithChildren = AppRouteRoute._addFileChildren(
|
||||
AppRouteRouteChildren,
|
||||
)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'': typeof AppRouteRouteWithChildren
|
||||
'/404': typeof R404Route
|
||||
'/about': typeof AboutRoute
|
||||
'/bangumi': typeof AppBangumiRouteRouteWithChildren
|
||||
'/playground': typeof AppPlaygroundRouteRouteWithChildren
|
||||
'/settings': typeof AppSettingsRouteRouteWithChildren
|
||||
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren
|
||||
'/auth/sign-in': typeof AuthSignInRoute
|
||||
'/auth/sign-up': typeof AuthSignUpRoute
|
||||
'/routeTree/gen': typeof RouteTreeGenRoute
|
||||
'/explore': typeof AppExploreExploreRoute
|
||||
'/feed': typeof AppExploreFeedRoute
|
||||
'/bangumi/manage': typeof AppBangumiManageRoute
|
||||
'/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute
|
||||
'/settings/downloader': typeof AppSettingsDownloaderRoute
|
||||
'/subscriptions/create': typeof AppSubscriptionsCreateRoute
|
||||
'/subscriptions/manage': typeof AppSubscriptionsManageRoute
|
||||
'/auth/oidc/callback': typeof AuthOidcCallbackRoute
|
||||
'/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute
|
||||
'/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'': typeof AppRouteRouteWithChildren
|
||||
'/404': typeof R404Route
|
||||
'/about': typeof AboutRoute
|
||||
'/bangumi': typeof AppBangumiRouteRouteWithChildren
|
||||
'/playground': typeof AppPlaygroundRouteRouteWithChildren
|
||||
'/settings': typeof AppSettingsRouteRouteWithChildren
|
||||
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren
|
||||
'/auth/sign-in': typeof AuthSignInRoute
|
||||
'/auth/sign-up': typeof AuthSignUpRoute
|
||||
'/routeTree/gen': typeof RouteTreeGenRoute
|
||||
'/explore': typeof AppExploreExploreRoute
|
||||
'/feed': typeof AppExploreFeedRoute
|
||||
'/bangumi/manage': typeof AppBangumiManageRoute
|
||||
'/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute
|
||||
'/settings/downloader': typeof AppSettingsDownloaderRoute
|
||||
'/subscriptions/create': typeof AppSubscriptionsCreateRoute
|
||||
'/subscriptions/manage': typeof AppSubscriptionsManageRoute
|
||||
'/auth/oidc/callback': typeof AuthOidcCallbackRoute
|
||||
'/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute
|
||||
'/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/_app': typeof AppRouteRouteWithChildren
|
||||
'/404': typeof R404Route
|
||||
'/about': typeof AboutRoute
|
||||
'/_app/bangumi': typeof AppBangumiRouteRouteWithChildren
|
||||
'/_app/playground': typeof AppPlaygroundRouteRouteWithChildren
|
||||
'/_app/settings': typeof AppSettingsRouteRouteWithChildren
|
||||
'/_app/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren
|
||||
'/auth/sign-in': typeof AuthSignInRoute
|
||||
'/auth/sign-up': typeof AuthSignUpRoute
|
||||
'/routeTree/gen': typeof RouteTreeGenRoute
|
||||
'/_app/_explore/explore': typeof AppExploreExploreRoute
|
||||
'/_app/_explore/feed': typeof AppExploreFeedRoute
|
||||
'/_app/bangumi/manage': typeof AppBangumiManageRoute
|
||||
'/_app/playground/graphql-api': typeof AppPlaygroundGraphqlApiRoute
|
||||
'/_app/settings/downloader': typeof AppSettingsDownloaderRoute
|
||||
'/_app/subscriptions/create': typeof AppSubscriptionsCreateRoute
|
||||
'/_app/subscriptions/manage': typeof AppSubscriptionsManageRoute
|
||||
'/auth/oidc/callback': typeof AuthOidcCallbackRoute
|
||||
'/_app/subscriptions/detail/$subscriptionId': typeof AppSubscriptionsDetailSubscriptionIdRoute
|
||||
'/_app/subscriptions/edit/$subscriptionId': typeof AppSubscriptionsEditSubscriptionIdRoute
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/'
|
||||
| ''
|
||||
| '/404'
|
||||
| '/about'
|
||||
| '/bangumi'
|
||||
| '/playground'
|
||||
| '/settings'
|
||||
| '/subscriptions'
|
||||
| '/auth/sign-in'
|
||||
| '/auth/sign-up'
|
||||
| '/routeTree/gen'
|
||||
| '/explore'
|
||||
| '/feed'
|
||||
| '/bangumi/manage'
|
||||
| '/playground/graphql-api'
|
||||
| '/settings/downloader'
|
||||
| '/subscriptions/create'
|
||||
| '/subscriptions/manage'
|
||||
| '/auth/oidc/callback'
|
||||
| '/subscriptions/detail/$subscriptionId'
|
||||
| '/subscriptions/edit/$subscriptionId'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| ''
|
||||
| '/404'
|
||||
| '/about'
|
||||
| '/bangumi'
|
||||
| '/playground'
|
||||
| '/settings'
|
||||
| '/subscriptions'
|
||||
| '/auth/sign-in'
|
||||
| '/auth/sign-up'
|
||||
| '/routeTree/gen'
|
||||
| '/explore'
|
||||
| '/feed'
|
||||
| '/bangumi/manage'
|
||||
| '/playground/graphql-api'
|
||||
| '/settings/downloader'
|
||||
| '/subscriptions/create'
|
||||
| '/subscriptions/manage'
|
||||
| '/auth/oidc/callback'
|
||||
| '/subscriptions/detail/$subscriptionId'
|
||||
| '/subscriptions/edit/$subscriptionId'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/_app'
|
||||
| '/404'
|
||||
| '/about'
|
||||
| '/_app/bangumi'
|
||||
| '/_app/playground'
|
||||
| '/_app/settings'
|
||||
| '/_app/subscriptions'
|
||||
| '/auth/sign-in'
|
||||
| '/auth/sign-up'
|
||||
| '/routeTree/gen'
|
||||
| '/_app/_explore/explore'
|
||||
| '/_app/_explore/feed'
|
||||
| '/_app/bangumi/manage'
|
||||
| '/_app/playground/graphql-api'
|
||||
| '/_app/settings/downloader'
|
||||
| '/_app/subscriptions/create'
|
||||
| '/_app/subscriptions/manage'
|
||||
| '/auth/oidc/callback'
|
||||
| '/_app/subscriptions/detail/$subscriptionId'
|
||||
| '/_app/subscriptions/edit/$subscriptionId'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
AppRouteRoute: typeof AppRouteRouteWithChildren
|
||||
R404Route: typeof R404Route
|
||||
AboutRoute: typeof AboutRoute
|
||||
AuthSignInRoute: typeof AuthSignInRoute
|
||||
AuthSignUpRoute: typeof AuthSignUpRoute
|
||||
RouteTreeGenRoute: typeof RouteTreeGenRoute
|
||||
AuthOidcCallbackRoute: typeof AuthOidcCallbackRoute
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AppRouteRoute: AppRouteRouteWithChildren,
|
||||
R404Route: R404Route,
|
||||
AboutRoute: AboutRoute,
|
||||
AuthSignInRoute: AuthSignInRoute,
|
||||
AuthSignUpRoute: AuthSignUpRoute,
|
||||
RouteTreeGenRoute: RouteTreeGenRoute,
|
||||
AuthOidcCallbackRoute: AuthOidcCallbackRoute,
|
||||
}
|
||||
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
|
||||
/* ROUTE_MANIFEST_START
|
||||
{
|
||||
"routes": {
|
||||
"__root__": {
|
||||
"filePath": "__root.tsx",
|
||||
"children": [
|
||||
"/",
|
||||
"/_app",
|
||||
"/404",
|
||||
"/about",
|
||||
"/auth/sign-in",
|
||||
"/auth/sign-up",
|
||||
"/routeTree/gen",
|
||||
"/auth/oidc/callback"
|
||||
]
|
||||
},
|
||||
"/": {
|
||||
"filePath": "index.tsx"
|
||||
},
|
||||
"/_app": {
|
||||
"filePath": "_app/route.tsx",
|
||||
"children": [
|
||||
"/_app/bangumi",
|
||||
"/_app/playground",
|
||||
"/_app/settings",
|
||||
"/_app/subscriptions",
|
||||
"/_app/_explore/explore",
|
||||
"/_app/_explore/feed"
|
||||
]
|
||||
},
|
||||
"/404": {
|
||||
"filePath": "404.tsx"
|
||||
},
|
||||
"/about": {
|
||||
"filePath": "about.tsx"
|
||||
},
|
||||
"/_app/bangumi": {
|
||||
"filePath": "_app/bangumi/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/bangumi/manage"
|
||||
]
|
||||
},
|
||||
"/_app/playground": {
|
||||
"filePath": "_app/playground/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/playground/graphql-api"
|
||||
]
|
||||
},
|
||||
"/_app/settings": {
|
||||
"filePath": "_app/settings/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/settings/downloader"
|
||||
]
|
||||
},
|
||||
"/_app/subscriptions": {
|
||||
"filePath": "_app/subscriptions/route.tsx",
|
||||
"parent": "/_app",
|
||||
"children": [
|
||||
"/_app/subscriptions/create",
|
||||
"/_app/subscriptions/manage",
|
||||
"/_app/subscriptions/detail/$subscriptionId",
|
||||
"/_app/subscriptions/edit/$subscriptionId"
|
||||
]
|
||||
},
|
||||
"/auth/sign-in": {
|
||||
"filePath": "auth/sign-in.tsx"
|
||||
},
|
||||
"/auth/sign-up": {
|
||||
"filePath": "auth/sign-up.tsx"
|
||||
},
|
||||
"/routeTree/gen": {
|
||||
"filePath": "routeTree.gen.ts"
|
||||
},
|
||||
"/_app/_explore/explore": {
|
||||
"filePath": "_app/_explore/explore.tsx",
|
||||
"parent": "/_app"
|
||||
},
|
||||
"/_app/_explore/feed": {
|
||||
"filePath": "_app/_explore/feed.tsx",
|
||||
"parent": "/_app"
|
||||
},
|
||||
"/_app/bangumi/manage": {
|
||||
"filePath": "_app/bangumi/manage.tsx",
|
||||
"parent": "/_app/bangumi"
|
||||
},
|
||||
"/_app/playground/graphql-api": {
|
||||
"filePath": "_app/playground/graphql-api.tsx",
|
||||
"parent": "/_app/playground"
|
||||
},
|
||||
"/_app/settings/downloader": {
|
||||
"filePath": "_app/settings/downloader.tsx",
|
||||
"parent": "/_app/settings"
|
||||
},
|
||||
"/_app/subscriptions/create": {
|
||||
"filePath": "_app/subscriptions/create.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
},
|
||||
"/_app/subscriptions/manage": {
|
||||
"filePath": "_app/subscriptions/manage.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
},
|
||||
"/auth/oidc/callback": {
|
||||
"filePath": "auth/oidc/callback.tsx"
|
||||
},
|
||||
"/_app/subscriptions/detail/$subscriptionId": {
|
||||
"filePath": "_app/subscriptions/detail.$subscriptionId.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
},
|
||||
"/_app/subscriptions/edit/$subscriptionId": {
|
||||
"filePath": "_app/subscriptions/edit.$subscriptionId.tsx",
|
||||
"parent": "/_app/subscriptions"
|
||||
}
|
||||
}
|
||||
}
|
||||
ROUTE_MANIFEST_END */
|
||||
7
apps/webui/src/presentation/utils/index.ts
Normal file
7
apps/webui/src/presentation/utils/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { ClassValue } from 'clsx';
|
||||
import { clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
Reference in New Issue
Block a user