feat(webhook): Phase 4 — admin-ui /webhooks page
Routes: - /webhooks (layout) → /webhooks/index (list) + /webhooks/$id (detail) - nav link добавлен в __root.tsx между Outbox и Language switch List page (webhooks.index.tsx): - Table: name | URL | scope filter (colored dots) | dictionaries | status - ScopeChips reuse SCOPE_DOT из lib/scope-style.ts (consistency с dictionaries scope coloring) - Create dialog: name, URL, scope filter (MultiSelect), dictionary & event type (CSV inputs), description. Client-side validation (URL scheme, name required) дублирует server-side @Pattern. - Secret reveal modal после create: warning + plaintext + copy button. Single-time view — после закрытия secret не получить, только rotate. Detail page (webhooks.$id.tsx): - Subscription metadata grid (URL, status, filters, masked secret, createdBy + timestamp) - Action buttons: rotate-secret + delete (с confirmation) - Delivery history table: outboxEventId | status badge (delivered/ retrying/pending/dlq) | attempts | last attempt | HTTP code | error - Pagination 50/page - Status badges: delivered=success, retrying=warning, pending=neutral, dlq=error API client (queries + mutations): - 4 queries: subscriptions list, subscription by id, deliveries page, DLQ page (last unused в UI пока, но готов для admin DLQ tab) - 4 mutations: create, update, delete, rotateSecret. Все invalidate правильные query keys. i18n: 50 ключей × 2 локали (ru-RU + en-US) под webhooks.*
This commit is contained in:
@@ -9,13 +9,21 @@
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as WebhooksRouteImport } from './routes/webhooks'
|
||||
import { Route as OutboxRouteImport } from './routes/outbox'
|
||||
import { Route as DictionariesRouteImport } from './routes/dictionaries'
|
||||
import { Route as AuditRouteImport } from './routes/audit'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
import { Route as WebhooksIndexRouteImport } from './routes/webhooks.index'
|
||||
import { Route as DictionariesIndexRouteImport } from './routes/dictionaries.index'
|
||||
import { Route as WebhooksIdRouteImport } from './routes/webhooks.$id'
|
||||
import { Route as DictionariesNameRouteImport } from './routes/dictionaries.$name'
|
||||
|
||||
const WebhooksRoute = WebhooksRouteImport.update({
|
||||
id: '/webhooks',
|
||||
path: '/webhooks',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const OutboxRoute = OutboxRouteImport.update({
|
||||
id: '/outbox',
|
||||
path: '/outbox',
|
||||
@@ -36,11 +44,21 @@ const IndexRoute = IndexRouteImport.update({
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
const WebhooksIndexRoute = WebhooksIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => WebhooksRoute,
|
||||
} as any)
|
||||
const DictionariesIndexRoute = DictionariesIndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => DictionariesRoute,
|
||||
} as any)
|
||||
const WebhooksIdRoute = WebhooksIdRouteImport.update({
|
||||
id: '/$id',
|
||||
path: '/$id',
|
||||
getParentRoute: () => WebhooksRoute,
|
||||
} as any)
|
||||
const DictionariesNameRoute = DictionariesNameRouteImport.update({
|
||||
id: '/$name',
|
||||
path: '/$name',
|
||||
@@ -52,15 +70,20 @@ export interface FileRoutesByFullPath {
|
||||
'/audit': typeof AuditRoute
|
||||
'/dictionaries': typeof DictionariesRouteWithChildren
|
||||
'/outbox': typeof OutboxRoute
|
||||
'/webhooks': typeof WebhooksRouteWithChildren
|
||||
'/dictionaries/$name': typeof DictionariesNameRoute
|
||||
'/webhooks/$id': typeof WebhooksIdRoute
|
||||
'/dictionaries/': typeof DictionariesIndexRoute
|
||||
'/webhooks/': typeof WebhooksIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/audit': typeof AuditRoute
|
||||
'/outbox': typeof OutboxRoute
|
||||
'/dictionaries/$name': typeof DictionariesNameRoute
|
||||
'/webhooks/$id': typeof WebhooksIdRoute
|
||||
'/dictionaries': typeof DictionariesIndexRoute
|
||||
'/webhooks': typeof WebhooksIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
@@ -68,8 +91,11 @@ export interface FileRoutesById {
|
||||
'/audit': typeof AuditRoute
|
||||
'/dictionaries': typeof DictionariesRouteWithChildren
|
||||
'/outbox': typeof OutboxRoute
|
||||
'/webhooks': typeof WebhooksRouteWithChildren
|
||||
'/dictionaries/$name': typeof DictionariesNameRoute
|
||||
'/webhooks/$id': typeof WebhooksIdRoute
|
||||
'/dictionaries/': typeof DictionariesIndexRoute
|
||||
'/webhooks/': typeof WebhooksIndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
@@ -78,18 +104,31 @@ export interface FileRouteTypes {
|
||||
| '/audit'
|
||||
| '/dictionaries'
|
||||
| '/outbox'
|
||||
| '/webhooks'
|
||||
| '/dictionaries/$name'
|
||||
| '/webhooks/$id'
|
||||
| '/dictionaries/'
|
||||
| '/webhooks/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/' | '/audit' | '/outbox' | '/dictionaries/$name' | '/dictionaries'
|
||||
to:
|
||||
| '/'
|
||||
| '/audit'
|
||||
| '/outbox'
|
||||
| '/dictionaries/$name'
|
||||
| '/webhooks/$id'
|
||||
| '/dictionaries'
|
||||
| '/webhooks'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/audit'
|
||||
| '/dictionaries'
|
||||
| '/outbox'
|
||||
| '/webhooks'
|
||||
| '/dictionaries/$name'
|
||||
| '/webhooks/$id'
|
||||
| '/dictionaries/'
|
||||
| '/webhooks/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
@@ -97,10 +136,18 @@ export interface RootRouteChildren {
|
||||
AuditRoute: typeof AuditRoute
|
||||
DictionariesRoute: typeof DictionariesRouteWithChildren
|
||||
OutboxRoute: typeof OutboxRoute
|
||||
WebhooksRoute: typeof WebhooksRouteWithChildren
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/webhooks': {
|
||||
id: '/webhooks'
|
||||
path: '/webhooks'
|
||||
fullPath: '/webhooks'
|
||||
preLoaderRoute: typeof WebhooksRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/outbox': {
|
||||
id: '/outbox'
|
||||
path: '/outbox'
|
||||
@@ -129,6 +176,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
'/webhooks/': {
|
||||
id: '/webhooks/'
|
||||
path: '/'
|
||||
fullPath: '/webhooks/'
|
||||
preLoaderRoute: typeof WebhooksIndexRouteImport
|
||||
parentRoute: typeof WebhooksRoute
|
||||
}
|
||||
'/dictionaries/': {
|
||||
id: '/dictionaries/'
|
||||
path: '/'
|
||||
@@ -136,6 +190,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof DictionariesIndexRouteImport
|
||||
parentRoute: typeof DictionariesRoute
|
||||
}
|
||||
'/webhooks/$id': {
|
||||
id: '/webhooks/$id'
|
||||
path: '/$id'
|
||||
fullPath: '/webhooks/$id'
|
||||
preLoaderRoute: typeof WebhooksIdRouteImport
|
||||
parentRoute: typeof WebhooksRoute
|
||||
}
|
||||
'/dictionaries/$name': {
|
||||
id: '/dictionaries/$name'
|
||||
path: '/$name'
|
||||
@@ -160,11 +221,26 @@ const DictionariesRouteWithChildren = DictionariesRoute._addFileChildren(
|
||||
DictionariesRouteChildren,
|
||||
)
|
||||
|
||||
interface WebhooksRouteChildren {
|
||||
WebhooksIdRoute: typeof WebhooksIdRoute
|
||||
WebhooksIndexRoute: typeof WebhooksIndexRoute
|
||||
}
|
||||
|
||||
const WebhooksRouteChildren: WebhooksRouteChildren = {
|
||||
WebhooksIdRoute: WebhooksIdRoute,
|
||||
WebhooksIndexRoute: WebhooksIndexRoute,
|
||||
}
|
||||
|
||||
const WebhooksRouteWithChildren = WebhooksRoute._addFileChildren(
|
||||
WebhooksRouteChildren,
|
||||
)
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
AuditRoute: AuditRoute,
|
||||
DictionariesRoute: DictionariesRouteWithChildren,
|
||||
OutboxRoute: OutboxRoute,
|
||||
WebhooksRoute: WebhooksRouteWithChildren,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
|
||||
Reference in New Issue
Block a user