Add Prettier to the frontend and gate formatting in CI
Форматтера у фронтенда не было: стиль держался вручную и успел разъехаться в 50 файлах. Ставим Prettier с настройками под уже сложившийся стиль (без точек с запятой, одинарные кавычки, ширина 100 — подобрана замером: при 100 расходится меньше файлов, чем при 96 или 110) и прогоняем его по коду. `src/routeTree.gen.ts` исключён — его переписывает плагин роутера. Чтобы форматирование больше не расходилось незаметно, добавлены проверки в CI: `csharpier check` для бэкенда (его отсутствие и позволило накопиться 79 неотформатированным файлам) и `prettier --check` для фронтенда. Версии форматтеров прибиты точно, без кареток: минорка меняет вывод и красит CI на файлах, которых никто не трогал. `.editorconfig` задаёт редакторам те же отступы и LF ещё до форматтера; значения совпадают с настройками csharpier и Prettier намеренно — оба его читают. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0442056367
commit
0606ea3e6e
@@ -28,7 +28,10 @@ export async function refreshAccessToken(): Promise<boolean> {
|
||||
if (!refreshInFlight) {
|
||||
refreshInFlight = (async () => {
|
||||
try {
|
||||
const response = await fetch('/api/auth/refresh', { method: 'POST', credentials: 'include' })
|
||||
const response = await fetch('/api/auth/refresh', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
})
|
||||
if (!response.ok) return false
|
||||
const data = (await response.json()) as { accessToken?: unknown }
|
||||
if (typeof data?.accessToken !== 'string') return false
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useState } from 'react'
|
||||
|
||||
export type KeyedRow<T> = { key: string; value: T }
|
||||
|
||||
const toRows = <T,>(values: readonly T[]): KeyedRow<T>[] =>
|
||||
const toRows = <T>(values: readonly T[]): KeyedRow<T>[] =>
|
||||
values.map((value) => ({ key: crypto.randomUUID(), value }))
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,7 @@ import { toast } from '@/shared/ui/toast-store'
|
||||
export function useApiError() {
|
||||
const { t } = useTranslation()
|
||||
return useCallback(
|
||||
(error: unknown) =>
|
||||
toast.error(error instanceof HttpError ? error.detail : t('common.error')),
|
||||
(error: unknown) => toast.error(error instanceof HttpError ? error.detail : t('common.error')),
|
||||
[t],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { useCallback, useMemo, useState, type ReactNode } from 'react'
|
||||
import {
|
||||
ToastContext,
|
||||
registerToastPush,
|
||||
type ToastItem,
|
||||
type ToastVariant,
|
||||
} from './toast-store'
|
||||
import { ToastContext, registerToastPush, type ToastItem, type ToastVariant } from './toast-store'
|
||||
|
||||
let nextId = 1
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@ type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> &
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : 'button'
|
||||
return <Comp className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props} />
|
||||
return (
|
||||
<Comp className={cn(buttonVariants({ variant, size }), className)} ref={ref} {...props} />
|
||||
)
|
||||
},
|
||||
)
|
||||
Button.displayName = 'Button'
|
||||
|
||||
@@ -1,33 +1,46 @@
|
||||
import { type HTMLAttributes, forwardRef } from 'react'
|
||||
import { cn } from '@/shared/lib/cn'
|
||||
|
||||
export const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('crt-panel rounded-md', className)} {...props} />
|
||||
))
|
||||
export const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('crt-panel rounded-md', className)} {...props} />
|
||||
),
|
||||
)
|
||||
Card.displayName = 'Card'
|
||||
|
||||
export const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('flex flex-col gap-1.5 p-6', className)} {...props} />
|
||||
))
|
||||
export const CardHeader = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('flex flex-col gap-1.5 p-6', className)} {...props} />
|
||||
),
|
||||
)
|
||||
CardHeader.displayName = 'CardHeader'
|
||||
|
||||
export const CardTitle = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLHeadingElement>>(
|
||||
// children разворачиваем явно: заголовок без видимого содержимого — это дыра для скринридера,
|
||||
// и статический анализ такое ловит только тогда, когда содержимое видно в разметке.
|
||||
({ className, children, ...props }, ref) => (
|
||||
<h3 ref={ref} className={cn('crt-glow text-xl font-semibold tracking-tight', className)} {...props}>
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn('crt-glow text-xl font-semibold tracking-tight', className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
)
|
||||
CardTitle.displayName = 'CardTitle'
|
||||
|
||||
export const CardDescription = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />,
|
||||
)
|
||||
export const CardDescription = forwardRef<
|
||||
HTMLParagraphElement,
|
||||
HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
|
||||
))
|
||||
CardDescription.displayName = 'CardDescription'
|
||||
|
||||
export const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
))
|
||||
export const CardContent = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
),
|
||||
)
|
||||
CardContent.displayName = 'CardContent'
|
||||
|
||||
@@ -50,7 +50,11 @@ export const DialogTitle = forwardRef<
|
||||
ElementRef<typeof DialogPrimitive.Title>,
|
||||
ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title ref={ref} className={cn('crt-glow text-lg font-semibold', className)} {...props} />
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn('crt-glow text-lg font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
||||
|
||||
@@ -58,7 +62,11 @@ export const DialogDescription = forwardRef<
|
||||
ElementRef<typeof DialogPrimitive.Description>,
|
||||
ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||
|
||||
|
||||
@@ -23,10 +23,7 @@ export function SortHeader({
|
||||
return (
|
||||
// aria-sort — атрибут заголовка столбца, а не кнопки внутри него: у роли button его нет,
|
||||
// и скринридер там его просто не прочтёт.
|
||||
<th
|
||||
className={cn('px-4 py-2 font-medium', className)}
|
||||
aria-sort={active ? direction : 'none'}
|
||||
>
|
||||
<th className={cn('px-4 py-2 font-medium', className)} aria-sort={active ? direction : 'none'}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onToggle(sortKey)}
|
||||
|
||||
@@ -10,7 +10,13 @@ export function Toaster() {
|
||||
return (
|
||||
<div className="pointer-events-none fixed bottom-4 right-4 z-[10000] flex flex-col gap-2">
|
||||
{toasts.map((t) => (
|
||||
<ToastItem key={t.id} id={t.id} message={t.message} variant={t.variant} onDismiss={dismiss} />
|
||||
<ToastItem
|
||||
key={t.id}
|
||||
id={t.id}
|
||||
message={t.message}
|
||||
variant={t.variant}
|
||||
onDismiss={dismiss}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user