fix: fix issues

This commit is contained in:
master 2025-07-02 01:33:32 +08:00
parent 81bf27ed28
commit 94919878ea
25 changed files with 391 additions and 245 deletions

14
Cargo.lock generated
View File

@ -1260,9 +1260,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.40" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -1270,9 +1270,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.40" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -1282,9 +1282,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.40" version = "4.5.41"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@ -7682,7 +7682,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]] [[package]]
name = "seaography" name = "seaography"
version = "1.1.4" version = "1.1.4"
source = "git+https://github.com/dumtruck/seaography.git?rev=9f7fc7c#9f7fc7cf05234abe35fd9144c895321dd2b5db62" source = "git+https://github.com/dumtruck/seaography.git?rev=292cdd2#292cdd248217fdcf81c41aa97fe1c047c9b5f4de"
dependencies = [ dependencies = [
"async-graphql", "async-graphql",
"fnv", "fnv",

View File

@ -31,22 +31,22 @@ reqwest = { version = "0.12.20", features = [
"macos-system-configuration", "macos-system-configuration",
"cookies", "cookies",
] } ] }
moka = "0.12" moka = "0.12.10"
futures = "0.3" futures = "0.3.31"
quirks_path = "0.1" quirks_path = "0.1.1"
snafu = { version = "0.8", features = ["futures"] } snafu = { version = "0.8.0", features = ["futures"] }
testcontainers = { version = "0.24" } testcontainers = { version = "0.24.0" }
testcontainers-modules = { version = "0.12.1" } testcontainers-modules = { version = "0.12.1" }
testcontainers-ext = { version = "0.1.0", features = ["tracing"] } testcontainers-ext = { version = "0.1.0", features = ["tracing"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
tokio = { version = "1.45.1", features = [ tokio = { version = "1.46", features = [
"macros", "macros",
"fs", "fs",
"rt-multi-thread", "rt-multi-thread",
"signal", "signal",
] } ] }
serde_json = "1" serde_json = "1.0.140"
async-trait = "0.1" async-trait = "0.1.88"
tracing = "0.1" tracing = "0.1"
url = "2.5.2" url = "2.5.2"
anyhow = "1" anyhow = "1"
@ -77,7 +77,7 @@ http = "1.2.0"
async-stream = "0.3.6" async-stream = "0.3.6"
serde_variant = "0.1.3" serde_variant = "0.1.3"
tracing-appender = "0.2.3" tracing-appender = "0.2.3"
clap = "4.5.40" clap = "4.5.41"
ipnetwork = "0.21.1" ipnetwork = "0.21.1"
typed-builder = "0.21.0" typed-builder = "0.21.0"
nanoid = "0.4.0" nanoid = "0.4.0"
@ -85,4 +85,4 @@ webp = "0.3.0"
[patch.crates-io] [patch.crates-io]
seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "9f7fc7c" } seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "292cdd2" }

View File

@ -126,7 +126,6 @@ seaography = { version = "1.1", features = [
"with-postgres-array", "with-postgres-array",
"with-json-as-scalar", "with-json-as-scalar",
"with-custom-as-json", "with-custom-as-json",
"with-chrono-datetime-utc-as-timestamp",
] } ] }
tower = { version = "0.5.2", features = ["util"] } tower = { version = "0.5.2", features = ["util"] }
tower-http = { version = "0.6", features = [ tower-http = { version = "0.6", features = [

View File

@ -1,4 +1,3 @@
import type { NavMainGroup } from '@/infra/routes/nav';
import { import {
BookOpen, BookOpen,
Folders, Folders,
@ -9,6 +8,7 @@ import {
Telescope, Telescope,
Tv, Tv,
} from 'lucide-react'; } from 'lucide-react';
import type { NavMainGroup } from '@/infra/routes/nav';
export const AppNavMainData: NavMainGroup[] = [ export const AppNavMainData: NavMainGroup[] = [
{ {
@ -49,13 +49,13 @@ export const AppNavMainData: NavMainGroup[] = [
{ {
title: 'Manage', title: 'Manage',
link: { link: {
to: '/bangumi/recorder', to: '/bangumi',
}, },
}, },
{ {
title: 'Feed', title: 'Feed',
link: { link: {
to: '/bangumi/feed', to: '/bangumi',
}, },
}, },
], ],

View File

@ -1,7 +1,7 @@
'use client'; 'use client';
import { useMatches } from '@tanstack/react-router';
import { ChevronRight } from 'lucide-react'; import { ChevronRight } from 'lucide-react';
import { import {
Collapsible, Collapsible,
CollapsibleContent, CollapsibleContent,
@ -27,13 +27,8 @@ import {
useSidebar, useSidebar,
} from '@/components/ui/sidebar'; } from '@/components/ui/sidebar';
import type { NavMainGroup, NavMainItem } from '@/infra/routes/nav'; import type { NavMainGroup, NavMainItem } from '@/infra/routes/nav';
import { useMatches } from '@tanstack/react-router';
export function NavMain({ export function NavMain({ groups }: { groups: NavMainGroup[] }) {
groups,
}: {
groups: NavMainGroup[];
}) {
const matches = useMatches(); const matches = useMatches();
const { state } = useSidebar(); const { state } = useSidebar();

View File

@ -1,4 +1,4 @@
import { type VariantProps, cva } from "class-variance-authority"; import { cva, type VariantProps } from "class-variance-authority";
import * as React from "react"; import * as React from "react";
import { cn } from "@/presentation/utils"; import { cn } from "@/presentation/utils";

View File

@ -1,20 +1,32 @@
import { type LinkComponent, createLink } from "@tanstack/react-router"; import { createLink, type LinkComponentProps } from "@tanstack/react-router";
import type { AnchorHTMLAttributes, ComponentProps } from "react"; import type { AnchorHTMLAttributes } from "react";
export interface BasicLinkProps export interface BasicLinkProps
extends AnchorHTMLAttributes<HTMLAnchorElement> {} extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
href: string;
to?: undefined;
}
const BasicLinkComponent = (props: ComponentProps<"a">) => { const BasicLinkComponent = (props: BasicLinkProps) => {
return <a {...props} />; return <a {...props} />;
}; };
const CreatedLinkComponent = createLink(BasicLinkComponent); const CreatedLinkComponent = createLink(BasicLinkComponent);
export const ProLink: LinkComponent<typeof BasicLinkComponent> = (props) => { export const ProLink = (
props: LinkComponentProps<typeof BasicLinkComponent> | BasicLinkProps
) => {
if (props.href) { if (props.href) {
return <BasicLinkComponent {...(props as any)} />; return <BasicLinkComponent {...(props as any)} />;
} }
return <CreatedLinkComponent preload={"intent"} {...props} />; return (
<CreatedLinkComponent
preload={"intent"}
{...(props as LinkComponentProps<typeof BasicLinkComponent>)}
/>
);
}; };
export type ProLinkProps = ComponentProps<typeof ProLink>; export type ProLinkProps =
| LinkComponentProps<typeof BasicLinkComponent>
| BasicLinkProps;

View File

@ -1,6 +1,6 @@
import { AlertCircle } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { AlertCircle } from "lucide-react";
export interface QueryErrorViewProps { export interface QueryErrorViewProps {
title?: string; title?: string;

View File

@ -83,10 +83,8 @@ export const DELETE_SUBSCRIPTIONS = gql`
`; `;
export const GET_SUBSCRIPTION_DETAIL = gql` export const GET_SUBSCRIPTION_DETAIL = gql`
query GetSubscriptionDetail ($id: Int!) { query GetSubscriptionDetail ($filter: SubscriptionsFilterInput!) {
subscriptions(filter: { id: { subscriptions(filter: $filter) {
eq: $id
} }) {
nodes { nodes {
id id
subscriberId subscriberId
@ -106,7 +104,15 @@ query GetSubscriptionDetail ($id: Int!) {
feedSource feedSource
} }
} }
subscriberTask { subscriberTask(pagination: {
page: {
page: 0,
limit: 3,
}
},
orderBy: {
runAt: DESC,
}) {
nodes { nodes {
id id
taskType taskType
@ -117,7 +123,15 @@ query GetSubscriptionDetail ($id: Int!) {
id id
username username
} }
cron { cron (pagination: {
page: {
page: 0,
limit: 3,
}
},
orderBy: {
createdAt: DESC,
}) {
nodes { nodes {
id id
cronExpr cronExpr

View File

@ -30,7 +30,7 @@ type Documents = {
"\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": typeof types.InsertSubscriptionDocument, "\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": typeof types.InsertSubscriptionDocument,
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument, "\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument,
"\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": typeof types.DeleteSubscriptionsDocument, "\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": typeof types.DeleteSubscriptionsDocument,
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument, "\nquery GetSubscriptionDetail ($filter: SubscriptionsFilterInput!) {\n subscriptions(filter: $filter) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask(pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n runAt: DESC,\n }) {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron (pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n createdAt: DESC,\n }) {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument,
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetTasksDocument, "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetTasksDocument,
"\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": typeof types.InsertSubscriberTaskDocument, "\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": typeof types.InsertSubscriberTaskDocument,
"\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": typeof types.DeleteTasksDocument, "\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": typeof types.DeleteTasksDocument,
@ -53,7 +53,7 @@ const documents: Documents = {
"\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": types.InsertSubscriptionDocument, "\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": types.InsertSubscriptionDocument,
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument, "\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument,
"\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": types.DeleteSubscriptionsDocument, "\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": types.DeleteSubscriptionsDocument,
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument, "\nquery GetSubscriptionDetail ($filter: SubscriptionsFilterInput!) {\n subscriptions(filter: $filter) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask(pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n runAt: DESC,\n }) {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron (pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n createdAt: DESC,\n }) {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument,
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetTasksDocument, "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n cron {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetTasksDocument,
"\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": types.InsertSubscriberTaskDocument, "\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": types.InsertSubscriberTaskDocument,
"\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": types.DeleteTasksDocument, "\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": types.DeleteTasksDocument,
@ -141,7 +141,7 @@ export function gql(source: "\n mutation DeleteSubscriptions($filter: Subscri
/** /**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"]; export function gql(source: "\nquery GetSubscriptionDetail ($filter: SubscriptionsFilterInput!) {\n subscriptions(filter: $filter) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask(pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n runAt: DESC,\n }) {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron (pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n createdAt: DESC,\n }) {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($filter: SubscriptionsFilterInput!) {\n subscriptions(filter: $filter) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask(pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n runAt: DESC,\n }) {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n cron (pagination: {\n page: {\n page: 0,\n limit: 3,\n }\n },\n orderBy: {\n createdAt: DESC,\n }) {\n nodes {\n id\n cronExpr\n nextRun\n lastRun\n lastError\n enabled\n status\n lockedAt\n lockedBy\n createdAt\n updatedAt\n timeoutMs\n maxAttempts\n priority\n attempts\n subscriberTaskCron\n }\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"];
/** /**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,10 @@ export class IntlService {
return this.Intl.DateTimeFormat().resolvedOptions().timeZone; return this.Intl.DateTimeFormat().resolvedOptions().timeZone;
} }
formatTimestamp(timestamp: number, options?: Intl.DateTimeFormatOptions) { formatDatetimeWithTz(
timestamp: number | string | Date,
options?: Intl.DateTimeFormatOptions
) {
const defaultOptions: Intl.DateTimeFormatOptions = { const defaultOptions: Intl.DateTimeFormatOptions = {
year: 'numeric', year: 'numeric',
month: '2-digit', month: '2-digit',

View File

@ -1,6 +1,6 @@
import type { ProLinkProps } from '@/components/ui/pro-link';
import { type } from 'arktype'; import { type } from 'arktype';
import type { LucideIcon } from 'lucide-react'; import type { LucideIcon } from 'lucide-react';
import type { ProLinkProps } from '@/components/ui/pro-link';
export interface NavMainItem { export interface NavMainItem {
link?: ProLinkProps; link?: ProLinkProps;

View File

@ -1,3 +1,7 @@
import { useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { CheckIcon, Edit, Eye, EyeOff } from 'lucide-react';
import { useState } from 'react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
@ -15,13 +19,10 @@ import { Label } from '@/components/ui/label';
import { QueryErrorView } from '@/components/ui/query-error-view'; import { QueryErrorView } from '@/components/ui/query-error-view';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { GET_CREDENTIAL_3RD_DETAIL } from '@/domains/recorder/schema/credential3rd'; import { GET_CREDENTIAL_3RD_DETAIL } from '@/domains/recorder/schema/credential3rd';
import { useInject } from '@/infra/di/inject';
import type { GetCredential3rdDetailQuery } from '@/infra/graphql/gql/graphql'; import type { GetCredential3rdDetailQuery } from '@/infra/graphql/gql/graphql';
import { IntlService } from '@/infra/intl/intl.service';
import type { RouteStateDataOption } from '@/infra/routes/traits'; import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { format } from 'date-fns/format';
import { CheckIcon, Edit, Eye, EyeOff } from 'lucide-react';
import { useState } from 'react';
import { Credential3rdCheckAvailableViewDialogContent } from './-check-available'; import { Credential3rdCheckAvailableViewDialogContent } from './-check-available';
export const Route = createFileRoute('/_app/credential3rd/detail/$id')({ export const Route = createFileRoute('/_app/credential3rd/detail/$id')({
@ -34,6 +35,7 @@ export const Route = createFileRoute('/_app/credential3rd/detail/$id')({
function Credential3rdDetailRouteComponent() { function Credential3rdDetailRouteComponent() {
const { id } = Route.useParams(); const { id } = Route.useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const intlService = useInject(IntlService);
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
@ -41,7 +43,7 @@ function Credential3rdDetailRouteComponent() {
GET_CREDENTIAL_3RD_DETAIL, GET_CREDENTIAL_3RD_DETAIL,
{ {
variables: { variables: {
id: Number.parseInt(id), id: Number.parseInt(id, 10),
}, },
} }
); );
@ -177,10 +179,7 @@ function Credential3rdDetailRouteComponent() {
<Label className="font-medium text-sm">Created at</Label> <Label className="font-medium text-sm">Created at</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{format( {intlService.formatDatetimeWithTz(credential.createdAt)}
new Date(credential.createdAt),
'yyyy-MM-dd HH:mm:ss'
)}
</span> </span>
</div> </div>
</div> </div>
@ -189,10 +188,7 @@ function Credential3rdDetailRouteComponent() {
<Label className="font-medium text-sm">Updated at</Label> <Label className="font-medium text-sm">Updated at</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{format( {intlService.formatDatetimeWithTz(credential.updatedAt)}
new Date(credential.updatedAt),
'yyyy-MM-dd HH:mm:ss'
)}
</span> </span>
</div> </div>
</div> </div>

View File

@ -1,3 +1,20 @@
import { useMutation, useQuery } from '@apollo/client';
import { Dialog } from '@radix-ui/react-dialog';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import {
type ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
type PaginationState,
type Row,
type SortingState,
useReactTable,
type VisibilityState,
} from '@tanstack/react-table';
import { Eye, EyeOff, Plus } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ContainerHeader } from '@/components/ui/container-header'; import { ContainerHeader } from '@/components/ui/container-header';
@ -21,33 +38,17 @@ import {
DELETE_CREDENTIAL_3RD, DELETE_CREDENTIAL_3RD,
GET_CREDENTIAL_3RD, GET_CREDENTIAL_3RD,
} from '@/domains/recorder/schema/credential3rd'; } from '@/domains/recorder/schema/credential3rd';
import { useInject } from '@/infra/di/inject';
import { import {
apolloErrorToMessage, apolloErrorToMessage,
getApolloQueryError, getApolloQueryError,
} from '@/infra/errors/apollo'; } from '@/infra/errors/apollo';
import type { GetCredential3rdQuery } from '@/infra/graphql/gql/graphql'; import type { GetCredential3rdQuery } from '@/infra/graphql/gql/graphql';
import { IntlService } from '@/infra/intl/intl.service';
import type { RouteStateDataOption } from '@/infra/routes/traits'; import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton'; import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton';
import { useEvent } from '@/presentation/hooks/use-event'; import { useEvent } from '@/presentation/hooks/use-event';
import { cn } from '@/presentation/utils'; import { cn } from '@/presentation/utils';
import { useMutation, useQuery } from '@apollo/client';
import { Dialog } from '@radix-ui/react-dialog';
import { createFileRoute, 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 { format } from 'date-fns';
import { Eye, EyeOff, Plus } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Credential3rdCheckAvailableViewDialogContent } from './-check-available'; import { Credential3rdCheckAvailableViewDialogContent } from './-check-available';
export const Route = createFileRoute('/_app/credential3rd/manage')({ export const Route = createFileRoute('/_app/credential3rd/manage')({
@ -59,6 +60,7 @@ export const Route = createFileRoute('/_app/credential3rd/manage')({
function CredentialManageRouteComponent() { function CredentialManageRouteComponent() {
const navigate = useNavigate(); const navigate = useNavigate();
const intlService = useInject(IntlService);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({ const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
createdAt: false, createdAt: false,
@ -94,18 +96,18 @@ function CredentialManageRouteComponent() {
const [deleteCredential] = useMutation(DELETE_CREDENTIAL_3RD, { const [deleteCredential] = useMutation(DELETE_CREDENTIAL_3RD, {
onCompleted: async () => { onCompleted: async () => {
const refetchResult = await refetch(); const refetchResult = await refetch();
const error = getApolloQueryError(refetchResult); const e = getApolloQueryError(refetchResult);
if (error) { if (e) {
toast.error('Failed to delete credential', { toast.error('Failed to delete credential', {
description: apolloErrorToMessage(error), description: apolloErrorToMessage(e),
}); });
return; return;
} }
toast.success('Credential deleted'); toast.success('Credential deleted');
}, },
onError: (error) => { onError: (e) => {
toast.error('Failed to delete credential', { toast.error('Failed to delete credential', {
description: error.message, description: e.message,
}); });
}, },
}); });
@ -212,7 +214,7 @@ function CredentialManageRouteComponent() {
const createdAt = row.original.createdAt; const createdAt = row.original.createdAt;
return ( return (
<div className="text-sm"> <div className="text-sm">
{format(new Date(createdAt), 'yyyy-MM-dd HH:mm:ss')} {intlService.formatDatetimeWithTz(createdAt)}
</div> </div>
); );
}, },
@ -224,7 +226,7 @@ function CredentialManageRouteComponent() {
const updatedAt = row.original.updatedAt; const updatedAt = row.original.updatedAt;
return ( return (
<div className="text-sm"> <div className="text-sm">
{format(new Date(updatedAt), 'yyyy-MM-dd HH:mm:ss')} {intlService.formatDatetimeWithTz(updatedAt)}
</div> </div>
); );
}, },
@ -266,7 +268,13 @@ function CredentialManageRouteComponent() {
}, },
]; ];
return cs; return cs;
}, [handleDeleteRecord, navigate, showPasswords, togglePasswordVisibility]); }, [
handleDeleteRecord,
navigate,
showPasswords,
togglePasswordVisibility,
intlService.formatDatetimeWithTz,
]);
const table = useReactTable({ const table = useReactTable({
data: useMemo(() => credentials?.nodes ?? [], [credentials]), data: useMemo(() => credentials?.nodes ?? [], [credentials]),

View File

@ -1,3 +1,17 @@
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import {
Edit,
ExternalLink,
ListIcon,
Pause,
Play,
PlusIcon,
RefreshCcwIcon,
Trash2,
} from 'lucide-react';
import { useMemo } from 'react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
@ -13,6 +27,7 @@ import { DetailEmptyView } from '@/components/ui/detail-empty-view';
import { Dialog, DialogTrigger } from '@/components/ui/dialog'; import { Dialog, DialogTrigger } from '@/components/ui/dialog';
import { Img } from '@/components/ui/img'; import { Img } from '@/components/ui/img';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { ProLink } from '@/components/ui/pro-link';
import { QueryErrorView } from '@/components/ui/query-error-view'; import { QueryErrorView } from '@/components/ui/query-error-view';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { UPDATE_CRONS } from '@/domains/recorder/schema/cron'; import { UPDATE_CRONS } from '@/domains/recorder/schema/cron';
@ -33,27 +48,14 @@ import {
FeedSourceEnum, FeedSourceEnum,
FeedTypeEnum, FeedTypeEnum,
type GetSubscriptionDetailQuery, type GetSubscriptionDetailQuery,
type GetSubscriptionDetailQueryVariables,
type InsertFeedMutation, type InsertFeedMutation,
type InsertFeedMutationVariables, type InsertFeedMutationVariables,
SubscriptionCategoryEnum, SubscriptionCategoryEnum,
type UpdateCronsMutation, type UpdateCronsMutation,
type UpdateCronsMutationVariables, type UpdateCronsMutationVariables,
} from '@/infra/graphql/gql/graphql'; } from '@/infra/graphql/gql/graphql';
import { useMutation, useQuery } from '@apollo/client'; import { IntlService } from '@/infra/intl/intl.service';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { format } from 'date-fns';
import {
Edit,
ExternalLink,
ListIcon,
Pause,
Play,
PlusIcon,
RefreshCcwIcon,
Trash2,
} from 'lucide-react';
import { useMemo } from 'react';
import { toast } from 'sonner';
import { prettyTaskType } from '../tasks/-pretty-task-type'; import { prettyTaskType } from '../tasks/-pretty-task-type';
import { SubscriptionCronCreationDialogContent } from './-cron-creation'; import { SubscriptionCronCreationDialogContent } from './-cron-creation';
import { SubscriptionTaskCreationDialogContent } from './-task-creation'; import { SubscriptionTaskCreationDialogContent } from './-task-creation';
@ -66,6 +68,7 @@ function SubscriptionDetailRouteComponent() {
const { id } = Route.useParams(); const { id } = Route.useParams();
const navigate = useNavigate(); const navigate = useNavigate();
const subscriptionService = useInject(SubscriptionService); const subscriptionService = useInject(SubscriptionService);
const intlService = useInject(IntlService);
const handleReload = async () => { const handleReload = async () => {
const result = await refetch(); const result = await refetch();
@ -77,12 +80,23 @@ function SubscriptionDetailRouteComponent() {
} }
}; };
const { data, loading, error, refetch } = const {
useQuery<GetSubscriptionDetailQuery>(GET_SUBSCRIPTION_DETAIL, { data,
loading,
error: subscriptionError,
refetch,
} = useQuery<GetSubscriptionDetailQuery, GetSubscriptionDetailQueryVariables>(
GET_SUBSCRIPTION_DETAIL,
{
variables: { variables: {
id: Number.parseInt(id), filter: {
id: {
eq: Number.parseInt(id, 10),
},
},
}, },
}); }
);
const handleEnterEditMode = () => { const handleEnterEditMode = () => {
navigate({ navigate({
@ -203,8 +217,8 @@ function SubscriptionDetailRouteComponent() {
return <DetailCardSkeleton />; return <DetailCardSkeleton />;
} }
if (error) { if (subscriptionError) {
return <QueryErrorView message={error.message} />; return <QueryErrorView message={subscriptionError.message} />;
} }
if (!subscription) { if (!subscription) {
@ -342,10 +356,7 @@ function SubscriptionDetailRouteComponent() {
<Label className="font-medium text-sm">Created at</Label> <Label className="font-medium text-sm">Created at</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{format( {intlService.formatDatetimeWithTz(subscription.createdAt)}
new Date(subscription.createdAt),
'yyyy-MM-dd HH:mm:ss'
)}
</span> </span>
</div> </div>
</div> </div>
@ -354,10 +365,7 @@ function SubscriptionDetailRouteComponent() {
<Label className="font-medium text-sm">Updated at</Label> <Label className="font-medium text-sm">Updated at</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{format( {intlService.formatDatetimeWithTz(subscription.updatedAt)}
new Date(subscription.updatedAt),
'yyyy-MM-dd HH:mm:ss'
)}
</span> </span>
</div> </div>
</div> </div>
@ -374,7 +382,7 @@ function SubscriptionDetailRouteComponent() {
insertFeed({ insertFeed({
variables: { variables: {
data: { data: {
subscriptionId: Number.parseInt(id), subscriptionId: Number.parseInt(id, 10),
feedType: FeedTypeEnum.Rss, feedType: FeedTypeEnum.Rss,
feedSource: FeedSourceEnum.SubscriptionEpisode, feedSource: FeedSourceEnum.SubscriptionEpisode,
}, },
@ -429,7 +437,7 @@ function SubscriptionDetailRouteComponent() {
</code> </code>
<div className="text-muted-foreground text-xs"> <div className="text-muted-foreground text-xs">
{format(new Date(feed.createdAt), 'MM-dd HH:mm')} {intlService.formatDatetimeWithTz(feed.createdAt)}
</div> </div>
</div> </div>
</Card> </Card>
@ -684,24 +692,22 @@ function SubscriptionDetailRouteComponent() {
Updated At Updated At
</Label> </Label>
<div className="font-mono text-sm"> <div className="font-mono text-sm">
{format( {intlService.formatDatetimeWithTz(
new Date(bangumi.updatedAt), bangumi.updatedAt
'yyyy-MM-dd'
)} )}
</div> </div>
</div> </div>
</div> </div>
{bangumi.homepage && ( {bangumi.homepage && (
<div className="mt-3 border-t pt-3"> <div className="mt-3 border-t pt-3">
<Button <Button variant="outline" size="sm" asChild>
variant="outline" <ProLink
size="sm" href={bangumi.homepage}
onClick={() => target="_blank"
window.open(bangumi.homepage!, '_blank') >
} <ExternalLink className="mr-2 h-3 w-3" />
> Homepage
<ExternalLink className="mr-2 h-3 w-3" /> </ProLink>
Homepage
</Button> </Button>
</div> </div>
)} )}

View File

@ -1,3 +1,18 @@
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import {
type ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
type PaginationState,
type SortingState,
useReactTable,
type VisibilityState,
} from '@tanstack/react-table';
import { Plus } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ContainerHeader } from '@/components/ui/container-header'; import { ContainerHeader } from '@/components/ui/container-header';
import { DataTablePagination } from '@/components/ui/data-table-pagination'; import { DataTablePagination } from '@/components/ui/data-table-pagination';
@ -22,31 +37,19 @@ import {
type SubscriptionDto, type SubscriptionDto,
UPDATE_SUBSCRIPTIONS, UPDATE_SUBSCRIPTIONS,
} from '@/domains/recorder/schema/subscriptions'; } from '@/domains/recorder/schema/subscriptions';
import { useInject } from '@/infra/di/inject';
import { import {
apolloErrorToMessage, apolloErrorToMessage,
getApolloQueryError, getApolloQueryError,
} from '@/infra/errors/apollo'; } from '@/infra/errors/apollo';
import type { GetSubscriptionsQuery } from '@/infra/graphql/gql/graphql'; import type {
GetSubscriptionsQuery,
GetSubscriptionsQueryVariables,
} from '@/infra/graphql/gql/graphql';
import { IntlService } from '@/infra/intl/intl.service';
import type { RouteStateDataOption } from '@/infra/routes/traits'; import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton'; import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton';
import { cn } from '@/presentation/utils'; import { cn } from '@/presentation/utils';
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute } from '@tanstack/react-router';
import { useNavigate } from '@tanstack/react-router';
import {
type ColumnDef,
type PaginationState,
type SortingState,
type VisibilityState,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from '@tanstack/react-table';
import { format } from 'date-fns';
import { Plus } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { SubscriptionTaskCreationDialogContent } from './-task-creation'; import { SubscriptionTaskCreationDialogContent } from './-task-creation';
export const Route = createFileRoute('/_app/subscriptions/manage')({ export const Route = createFileRoute('/_app/subscriptions/manage')({
@ -58,6 +61,7 @@ export const Route = createFileRoute('/_app/subscriptions/manage')({
function SubscriptionManageRouteComponent() { function SubscriptionManageRouteComponent() {
const navigate = useNavigate(); const navigate = useNavigate();
const intlService = useInject(IntlService);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({ const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({
createdAt: false, createdAt: false,
@ -69,7 +73,12 @@ function SubscriptionManageRouteComponent() {
pageSize: 10, pageSize: 10,
}); });
const { loading, error, data, refetch } = useQuery<GetSubscriptionsQuery>( const {
loading,
error: subscriptionsError,
data,
refetch,
} = useQuery<GetSubscriptionsQuery, GetSubscriptionsQueryVariables>(
GET_SUBSCRIPTIONS, GET_SUBSCRIPTIONS,
{ {
variables: { variables: {
@ -138,11 +147,11 @@ function SubscriptionManageRouteComponent() {
<div className="px-1"> <div className="px-1">
<Switch <Switch
checked={enabled} checked={enabled}
onCheckedChange={(enabled) => onCheckedChange={(checked) =>
updateSubscription({ updateSubscription({
variables: { variables: {
data: { data: {
enabled, enabled: checked,
}, },
filter: { filter: {
id: { id: {
@ -189,7 +198,7 @@ function SubscriptionManageRouteComponent() {
const createdAt = row.original.createdAt; const createdAt = row.original.createdAt;
return ( return (
<div className="text-sm"> <div className="text-sm">
{format(new Date(createdAt), 'yyyy-MM-dd HH:mm:ss')} {intlService.formatDatetimeWithTz(createdAt)}
</div> </div>
); );
}, },
@ -201,7 +210,7 @@ function SubscriptionManageRouteComponent() {
const updatedAt = row.original.updatedAt; const updatedAt = row.original.updatedAt;
return ( return (
<div className="text-sm"> <div className="text-sm">
{format(new Date(updatedAt), 'yyyy-MM-dd HH:mm:ss')} {intlService.formatDatetimeWithTz(updatedAt)}
</div> </div>
); );
}, },
@ -247,7 +256,12 @@ function SubscriptionManageRouteComponent() {
}, },
]; ];
return cs; return cs;
}, [updateSubscription, deleteSubscription, navigate]); }, [
updateSubscription,
deleteSubscription,
navigate,
intlService.formatDatetimeWithTz,
]);
const table = useReactTable({ const table = useReactTable({
data: useMemo(() => subscriptions?.nodes ?? [], [subscriptions]), data: useMemo(() => subscriptions?.nodes ?? [], [subscriptions]),
@ -274,8 +288,10 @@ function SubscriptionManageRouteComponent() {
}, },
}); });
if (error) { if (subscriptionsError) {
return <QueryErrorView message={error.message} onRetry={refetch} />; return (
<QueryErrorView message={subscriptionsError.message} onRetry={refetch} />
);
} }
return ( return (

View File

@ -1,6 +1,5 @@
import { useQuery } from '@apollo/client'; import { useQuery } from '@apollo/client';
import { createFileRoute } from '@tanstack/react-router'; import { createFileRoute } from '@tanstack/react-router';
import { format } from 'date-fns';
import { RefreshCw } from 'lucide-react'; import { RefreshCw } from 'lucide-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
@ -153,7 +152,7 @@ function CronDetailRouteComponent() {
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{cron.nextRun {cron.nextRun
? format(new Date(cron.nextRun), 'yyyy-MM-dd HH:mm:ss') ? intlService.formatDatetimeWithTz(cron.nextRun)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -164,7 +163,7 @@ function CronDetailRouteComponent() {
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{cron.lastRun {cron.lastRun
? format(new Date(cron.lastRun), 'yyyy-MM-dd HH:mm:ss') ? intlService.formatDatetimeWithTz(cron.lastRun)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -175,7 +174,7 @@ function CronDetailRouteComponent() {
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{cron.lockedAt {cron.lockedAt
? format(new Date(cron.lockedAt), 'yyyy-MM-dd HH:mm:ss') ? intlService.formatDatetimeWithTz(cron.lockedAt)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -201,10 +200,7 @@ function CronDetailRouteComponent() {
<Label className="font-medium text-sm">Created at</Label> <Label className="font-medium text-sm">Created at</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{intlService.formatTimestamp( {intlService.formatDatetimeWithTz(cron.createdAt)}
cron.createdAt,
'yyyy-MM-dd HH:mm:ss'
)}
</span> </span>
</div> </div>
</div> </div>
@ -213,31 +209,12 @@ function CronDetailRouteComponent() {
<Label className="font-medium text-sm">Updated at</Label> <Label className="font-medium text-sm">Updated at</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{format(new Date(cron.updatedAt), 'yyyy-MM-dd HH:mm:ss')} {intlService.formatDatetimeWithTz(cron.updatedAt)}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
{/* Cron Expression Display */}
{cron.cronExpr && (
<>
<Separator />
<div className="space-y-2">
<Label className="font-medium text-sm">
Cron expression
</Label>
<CronDisplay
expression={cron.cronExpr}
timezone="UTC"
showDescription={true}
showNextRuns={true}
withCard={false}
/>
</div>
</>
)}
{/* Subscriber Task Details */} {/* Subscriber Task Details */}
{subscriberTaskCron && ( {subscriberTaskCron && (
<> <>
@ -257,6 +234,25 @@ function CronDetailRouteComponent() {
</> </>
)} )}
{/* Cron Expression Display */}
{cron.cronExpr && (
<>
<Separator />
<div className="space-y-2">
<Label className="font-medium text-sm">
Cron expression
</Label>
<CronDisplay
expression={cron.cronExpr}
timezone="UTC"
showDescription={true}
showNextRuns={true}
withCard={false}
/>
</div>
</>
)}
{/* Related Subscriber Tasks */} {/* Related Subscriber Tasks */}
{cron.subscriberTask?.nodes && {cron.subscriberTask?.nodes &&
cron.subscriberTask.nodes.length > 0 && ( cron.subscriberTask.nodes.length > 0 && (
@ -277,11 +273,11 @@ function CronDetailRouteComponent() {
<Badge variant="outline">{task.status}</Badge> <Badge variant="outline">{task.status}</Badge>
</div> </div>
<div className="mt-2 text-muted-foreground text-sm"> <div className="mt-2 text-muted-foreground text-sm">
Priority: {task.priority} | Retry: {task.attempts} Priority: {task.priority} | Attempts:{' '}
/{task.maxAttempts} {task.attempts}/{task.maxAttempts}
</div> </div>
{task.subscription && ( {task.subscription && (
<div className="mt-1 text-sm"> <div className="mt-2 text-sm">
<span className="font-medium"> <span className="font-medium">
Subscription: Subscription:
</span>{' '} </span>{' '}

View File

@ -9,7 +9,6 @@ import {
useReactTable, useReactTable,
type VisibilityState, type VisibilityState,
} from '@tanstack/react-table'; } from '@tanstack/react-table';
import { format } from 'date-fns';
import { RefreshCw } from 'lucide-react'; import { RefreshCw } from 'lucide-react';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
@ -26,6 +25,7 @@ import {
DELETE_CRONS, DELETE_CRONS,
GET_CRONS, GET_CRONS,
} from '@/domains/recorder/schema/cron'; } from '@/domains/recorder/schema/cron';
import { useInject } from '@/infra/di/inject';
import { import {
apolloErrorToMessage, apolloErrorToMessage,
getApolloQueryError, getApolloQueryError,
@ -37,6 +37,7 @@ import {
type GetCronsQuery, type GetCronsQuery,
type GetCronsQueryVariables, type GetCronsQueryVariables,
} from '@/infra/graphql/gql/graphql'; } from '@/infra/graphql/gql/graphql';
import { IntlService } from '@/infra/intl/intl.service';
import type { RouteStateDataOption } from '@/infra/routes/traits'; import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton'; import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton';
import { getStatusBadge } from './-status-badge'; import { getStatusBadge } from './-status-badge';
@ -50,6 +51,7 @@ export const Route = createFileRoute('/_app/tasks/cron/manage')({
function TaskCronManageRouteComponent() { function TaskCronManageRouteComponent() {
const navigate = useNavigate(); const navigate = useNavigate();
const intlService = useInject(IntlService);
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({}); const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
const [sorting, setSorting] = useState<SortingState>([]); const [sorting, setSorting] = useState<SortingState>([]);
@ -224,7 +226,7 @@ function TaskCronManageRouteComponent() {
<span className="text-muted-foreground">Next run: </span> <span className="text-muted-foreground">Next run: </span>
<span> <span>
{cron.nextRun {cron.nextRun
? format(new Date(cron.nextRun), 'MM/dd HH:mm') ? intlService.formatDatetimeWithTz(cron.nextRun)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -233,7 +235,7 @@ function TaskCronManageRouteComponent() {
<span className="text-muted-foreground">Last run: </span> <span className="text-muted-foreground">Last run: </span>
<span> <span>
{cron.lastRun {cron.lastRun
? format(new Date(cron.lastRun), 'MM/dd HH:mm') ? intlService.formatDatetimeWithTz(cron.lastRun)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -251,7 +253,7 @@ function TaskCronManageRouteComponent() {
<span className="text-muted-foreground">Lock at: </span> <span className="text-muted-foreground">Lock at: </span>
<span> <span>
{cron.lockedAt {cron.lockedAt
? format(new Date(cron.lockedAt), 'MM/dd HH:mm') ? intlService.formatDatetimeWithTz(cron.lockedAt)
: '-'} : '-'}
</span> </span>
</div> </div>

View File

@ -1,3 +1,8 @@
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute } from '@tanstack/react-router';
import { RefreshCw } from 'lucide-react';
import { useMemo } from 'react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
@ -14,8 +19,11 @@ import { Label } from '@/components/ui/label';
import { QueryErrorView } from '@/components/ui/query-error-view'; import { QueryErrorView } from '@/components/ui/query-error-view';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import { GET_TASKS, RETRY_TASKS } from '@/domains/recorder/schema/tasks'; import { GET_TASKS, RETRY_TASKS } from '@/domains/recorder/schema/tasks';
import { getApolloQueryError } from '@/infra/errors/apollo'; import { useInject } from '@/infra/di/inject';
import { apolloErrorToMessage } from '@/infra/errors/apollo'; import {
apolloErrorToMessage,
getApolloQueryError,
} from '@/infra/errors/apollo';
import { import {
type GetTasksQuery, type GetTasksQuery,
type GetTasksQueryVariables, type GetTasksQueryVariables,
@ -23,13 +31,8 @@ import {
type RetryTasksMutationVariables, type RetryTasksMutationVariables,
SubscriberTaskStatusEnum, SubscriberTaskStatusEnum,
} from '@/infra/graphql/gql/graphql'; } from '@/infra/graphql/gql/graphql';
import { IntlService } from '@/infra/intl/intl.service';
import type { RouteStateDataOption } from '@/infra/routes/traits'; import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute } from '@tanstack/react-router';
import { format } from 'date-fns';
import { RefreshCw } from 'lucide-react';
import { useMemo } from 'react';
import { toast } from 'sonner';
import { prettyTaskType } from './-pretty-task-type'; import { prettyTaskType } from './-pretty-task-type';
import { getStatusBadge } from './-status-badge'; import { getStatusBadge } from './-status-badge';
@ -43,10 +46,14 @@ export const Route = createFileRoute('/_app/tasks/detail/$id')({
function TaskDetailRouteComponent() { function TaskDetailRouteComponent() {
const { id } = Route.useParams(); const { id } = Route.useParams();
const { data, loading, error, refetch } = useQuery< const intlService = useInject(IntlService);
GetTasksQuery,
GetTasksQueryVariables const {
>(GET_TASKS, { data,
loading,
error: taskError,
refetch,
} = useQuery<GetTasksQuery, GetTasksQueryVariables>(GET_TASKS, {
variables: { variables: {
filter: { filter: {
id: { id: {
@ -102,8 +109,8 @@ function TaskDetailRouteComponent() {
return <DetailCardSkeleton />; return <DetailCardSkeleton />;
} }
if (error) { if (taskError) {
return <QueryErrorView message={error.message} onRetry={refetch} />; return <QueryErrorView message={taskError.message} onRetry={refetch} />;
} }
if (!task) { if (!task) {
@ -195,7 +202,7 @@ function TaskDetailRouteComponent() {
</Label> </Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{format(new Date(task.runAt), 'yyyy-MM-dd HH:mm:ss')} {intlService.formatDatetimeWithTz(task.runAt)}
</span> </span>
</div> </div>
</div> </div>
@ -205,7 +212,7 @@ function TaskDetailRouteComponent() {
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{task.doneAt {task.doneAt
? format(new Date(task.doneAt), 'yyyy-MM-dd HH:mm:ss') ? intlService.formatDatetimeWithTz(task.doneAt)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -216,7 +223,7 @@ function TaskDetailRouteComponent() {
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<span className="text-sm"> <span className="text-sm">
{task.lockAt {task.lockAt
? format(new Date(task.lockAt), 'yyyy-MM-dd HH:mm:ss') ? intlService.formatDatetimeWithTz(task.lockAt)
: '-'} : '-'}
</span> </span>
</div> </div>

View File

@ -1,7 +1,23 @@
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import {
type ColumnDef,
getCoreRowModel,
getPaginationRowModel,
type PaginationState,
type SortingState,
useReactTable,
type VisibilityState,
} from '@tanstack/react-table';
import { RefreshCw } from 'lucide-react';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { ContainerHeader } from '@/components/ui/container-header';
import { DataTablePagination } from '@/components/ui/data-table-pagination'; import { DataTablePagination } from '@/components/ui/data-table-pagination';
import { DetailEmptyView } from '@/components/ui/detail-empty-view'; import { DetailEmptyView } from '@/components/ui/detail-empty-view';
import { DropdownMenuItem } from '@/components/ui/dropdown-menu';
import { DropdownMenuActions } from '@/components/ui/dropdown-menu-actions'; import { DropdownMenuActions } from '@/components/ui/dropdown-menu-actions';
import { QueryErrorView } from '@/components/ui/query-error-view'; import { QueryErrorView } from '@/components/ui/query-error-view';
import { Skeleton } from '@/components/ui/skeleton'; import { Skeleton } from '@/components/ui/skeleton';
@ -11,6 +27,11 @@ import {
RETRY_TASKS, RETRY_TASKS,
type TaskDto, type TaskDto,
} from '@/domains/recorder/schema/tasks'; } from '@/domains/recorder/schema/tasks';
import { useInject } from '@/infra/di/inject';
import {
apolloErrorToMessage,
getApolloQueryError,
} from '@/infra/errors/apollo';
import { import {
type DeleteTasksMutation, type DeleteTasksMutation,
type DeleteTasksMutationVariables, type DeleteTasksMutationVariables,
@ -20,30 +41,9 @@ import {
type RetryTasksMutationVariables, type RetryTasksMutationVariables,
SubscriberTaskStatusEnum, SubscriberTaskStatusEnum,
} from '@/infra/graphql/gql/graphql'; } from '@/infra/graphql/gql/graphql';
import { IntlService } from '@/infra/intl/intl.service';
import type { RouteStateDataOption } from '@/infra/routes/traits'; import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton'; import { useDebouncedSkeleton } from '@/presentation/hooks/use-debounded-skeleton';
import { useMutation, useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import {
type ColumnDef,
type PaginationState,
type SortingState,
type VisibilityState,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from '@tanstack/react-table';
import { format } from 'date-fns';
import { RefreshCw } from 'lucide-react';
import { ContainerHeader } from '@/components/ui/container-header';
import { DropdownMenuItem } from '@/components/ui/dropdown-menu';
import {
apolloErrorToMessage,
getApolloQueryError,
} from '@/infra/errors/apollo';
import { useMemo, useState } from 'react';
import { toast } from 'sonner';
import { prettyTaskType } from './-pretty-task-type'; import { prettyTaskType } from './-pretty-task-type';
import { getStatusBadge } from './-status-badge'; import { getStatusBadge } from './-status-badge';
@ -64,10 +64,14 @@ function TaskManageRouteComponent() {
pageSize: 10, pageSize: 10,
}); });
const { loading, error, data, refetch } = useQuery< const intlService = useInject(IntlService);
GetTasksQuery,
GetTasksQueryVariables const {
>(GET_TASKS, { loading,
error: tasksError,
data,
refetch,
} = useQuery<GetTasksQuery, GetTasksQueryVariables>(GET_TASKS, {
variables: { variables: {
pagination: { pagination: {
page: { page: {
@ -168,8 +172,8 @@ function TaskManageRouteComponent() {
}, },
}); });
if (error) { if (tasksError) {
return <QueryErrorView message={error.message} onRetry={refetch} />; return <QueryErrorView message={tasksError.message} onRetry={refetch} />;
} }
return ( return (
@ -262,14 +266,14 @@ function TaskManageRouteComponent() {
<div className="grid grid-cols-2 gap-2 text-sm"> <div className="grid grid-cols-2 gap-2 text-sm">
<div> <div>
<span className="text-muted-foreground">Run at: </span> <span className="text-muted-foreground">Run at: </span>
<span>{format(new Date(task.runAt), 'MM/dd HH:mm')}</span> <span>{intlService.formatDatetimeWithTz(task.runAt)}</span>
</div> </div>
<div> <div>
<span className="text-muted-foreground">Done: </span> <span className="text-muted-foreground">Done: </span>
<span> <span>
{task.doneAt {task.doneAt
? format(new Date(task.doneAt), 'MM/dd HH:mm') ? intlService.formatDatetimeWithTz(task.doneAt)
: '-'} : '-'}
</span> </span>
</div> </div>
@ -287,7 +291,7 @@ function TaskManageRouteComponent() {
<span className="text-muted-foreground">Lock at: </span> <span className="text-muted-foreground">Lock at: </span>
<span> <span>
{task.lockAt {task.lockAt
? format(new Date(task.lockAt), 'MM/dd HH:mm') ? intlService.formatDatetimeWithTz(task.lockAt)
: '-'} : '-'}
</span> </span>
</div> </div>

View File

@ -31,6 +31,9 @@
"a11y": { "a11y": {
"noSvgWithoutTitle": "off" "noSvgWithoutTitle": "off"
}, },
"performance": {
"noNamespaceImport": "off"
},
"complexity": { "complexity": {
"noExcessiveCognitiveComplexity": { "noExcessiveCognitiveComplexity": {
"level": "warn", "level": "warn",

View File

@ -3,7 +3,10 @@
"version": "0.0.0", "version": "0.0.0",
"description": "Kono bangumi?", "description": "Kono bangumi?",
"license": "MIT", "license": "MIT",
"workspaces": ["packages/*", "apps/*"], "workspaces": [
"packages/*",
"apps/*"
],
"type": "module", "type": "module",
"repository": { "repository": {
"type": "git", "type": "git",
@ -20,6 +23,7 @@
"node": ">=24" "node": ">=24"
}, },
"dependencies": { "dependencies": {
"@typescript/native-preview": "7.0.0-dev.20250712.1",
"es-toolkit": "^1.39.6" "es-toolkit": "^1.39.6"
}, },
"devDependencies": { "devDependencies": {

81
pnpm-lock.yaml generated
View File

@ -11,6 +11,9 @@ importers:
.: .:
dependencies: dependencies:
'@typescript/native-preview':
specifier: 7.0.0-dev.20250712.1
version: 7.0.0-dev.20250712.1
es-toolkit: es-toolkit:
specifier: ^1.39.6 specifier: ^1.39.6
version: 1.39.6 version: 1.39.6
@ -3208,6 +3211,53 @@ packages:
'@types/ws@8.18.1': '@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@typescript/native-preview-darwin-arm64@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-h5MlpLXr6I6zrKZKZOjyKdUhzUuO2+kKLEYmrR0HWS/U6dlcwvNuS2wUo1lRNgUTCblHJuOKmyWx3Sz+JG8Oxw==}
engines: {node: '>=20.6.0'}
cpu: [arm64]
os: [darwin]
'@typescript/native-preview-darwin-x64@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-72SPfl0/U2ra4KlkzLWgA86zLKUIimBPYE1NqZpJHLMoXkC5XtS9aXT8p6ivxkK+p1VV3fIWKM8BbxOUY3af0A==}
engines: {node: '>=20.6.0'}
cpu: [x64]
os: [darwin]
'@typescript/native-preview-linux-arm64@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-LqIRXAn1xC5amD+ypd7xVTyMhgbbhb9XLzLM32Gr8ogJUCvcLBbd8KCHsKnHSR8nc+3b9FKnYdK7YL6a2IqavA==}
engines: {node: '>=20.6.0'}
cpu: [arm64]
os: [linux]
'@typescript/native-preview-linux-arm@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-XedHV/oRfLKrfU7XE5/Fz8Lf+eKCaQyNINCikQQlAuQWgT4pf8gW9FPkZYmeNvl7y+++43Wr2YJklmwXMrIDiA==}
engines: {node: '>=20.6.0'}
cpu: [arm]
os: [linux]
'@typescript/native-preview-linux-x64@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-dCIb4GTXvetTpeVJRsKf0lNnq1udfjkhDmyUX15yWV41mPg+PJUiLeZty2GOwFovSfEUSK+pQSP2iaA6ITbLtw==}
engines: {node: '>=20.6.0'}
cpu: [x64]
os: [linux]
'@typescript/native-preview-win32-arm64@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-+Wze9OFlre7YxOh/2LePfh6TmdwMJkSyiFD6XRtmm1hkwoB8jk5h1Q5aC5P4a3LTYx6jie6eYSVSYUXtaWZqMw==}
engines: {node: '>=20.6.0'}
cpu: [arm64]
os: [win32]
'@typescript/native-preview-win32-x64@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-PwLTlosngLgI4O41qjIFFanl5Q+G8bzUIvFdT0yk2vPAPnPyT0N2gLmGbAJouM3nSe7e+id2+iqdY+QCPvh0uA==}
engines: {node: '>=20.6.0'}
cpu: [x64]
os: [win32]
'@typescript/native-preview@7.0.0-dev.20250712.1':
resolution: {integrity: sha512-A8/aOsMpG6H8IcSIKYJSuHzbNkVr8dJOxbb4LMrSfOZ/JWayHQ4O5UJ9mSaKtyPwR6fInE5B8yMt7BYQOz77kA==}
engines: {node: '>=20.6.0'}
hasBin: true
'@vitejs/plugin-react@4.5.2': '@vitejs/plugin-react@4.5.2':
resolution: {integrity: sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==} resolution: {integrity: sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
@ -9970,6 +10020,37 @@ snapshots:
dependencies: dependencies:
'@types/node': 24.0.10 '@types/node': 24.0.10
'@typescript/native-preview-darwin-arm64@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview-darwin-x64@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview-linux-arm64@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview-linux-arm@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview-linux-x64@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview-win32-arm64@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview-win32-x64@7.0.0-dev.20250712.1':
optional: true
'@typescript/native-preview@7.0.0-dev.20250712.1':
optionalDependencies:
'@typescript/native-preview-darwin-arm64': 7.0.0-dev.20250712.1
'@typescript/native-preview-darwin-x64': 7.0.0-dev.20250712.1
'@typescript/native-preview-linux-arm': 7.0.0-dev.20250712.1
'@typescript/native-preview-linux-arm64': 7.0.0-dev.20250712.1
'@typescript/native-preview-linux-x64': 7.0.0-dev.20250712.1
'@typescript/native-preview-win32-arm64': 7.0.0-dev.20250712.1
'@typescript/native-preview-win32-x64': 7.0.0-dev.20250712.1
'@vitejs/plugin-react@4.5.2(vite@5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1))': '@vitejs/plugin-react@4.5.2(vite@5.4.11(@types/node@24.0.10)(lightningcss@1.30.1)(sass@1.77.4)(terser@5.43.1))':
dependencies: dependencies:
'@babel/core': 7.27.4 '@babel/core': 7.27.4