feat: UI–BLL Integration#50
Conversation
…, user invite schema
… and mutation hooks
…s-nav widget, rename invites to invitations
| export function useTeamHotkeys(teams: TUser.UserTeamResponse[], onSelect: (slug: string) => void) { | ||
| useEffect(() => { | ||
| const handler = (e: KeyboardEvent) => { | ||
| if (!(e.metaKey || e.ctrlKey)) return; |
There was a problem hiding this comment.
А там не через бинд E20/E32 кнопки и тп?
Я чего-то видать упустил =D
|
|
||
| const debouncedCheckSlug = useMemo(() => debounce((fn: typeof refetch) => fn(), 400), []); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
Хук опасный по депенденси
Внешние ссылки, старое замыкание и новые баги, что либо в бесконечный цикл уйдет, либо ошибку на клиента отдаст голую
Если у тебя на форме стоит условие criteriaMode: onBlur - ок, такого, вроде не должно быть
Вариант 1
const id = useId();
const { field, fieldState, formState } = useController(props);
const slug = (field.value as string) ?? '';
const defaultSlug = (formState.defaultValues?.[props.name] as string | undefined) ?? '';
const isDirty = fieldState.isDirty;
// React Query должен реагировать на изменение slug автоматически, но с условием enabled
const { data, isFetching } = useQuery({
...TeamQueries.checkSlug(slug),
// Запрос идет только если поле изменилось, оно не пустое и длиннее 1 символа
enabled: isDirty && slug.trim().length > 1 && slug !== defaultSlug,
});
// Синхронизация ошибок API с состоянием формы
useEffect(() => {
if (!isDirty || !slug) {
clearErrors(props.name);
return;
}
if (data?.available === false) {
setError(props.name, {
type: 'validate',
message: data.message ?? 'Этот адрес уже занят'
});
} else if (data?.available === true) {
clearErrors(props.name);
}
}, [data, isDirty, slug, props.name, setError, clearErrors]);
const showStatus = isDirty && !!slug && slug !== defaultSlug;Вариант 2
const id = useId();
const { field, fieldState } = useController({
...props,
rules: {
validate: () => {
if (fieldState.isDirty && data?.available === false) {
return data.message ?? 'Этот адрес уже занят';
}
return true;
}
}
});
const slug = (field.value as string) ?? '';
const { data, isFetching, refetch } = useQuery({
...TeamQueries.checkSlug(slug),
enabled: fieldState.isDirty && slug.length > 1,
});
useEffect(() => {
if (data) {
field.onBlur(); // Заставляет пересчитать rules.validate
}
}, [data]);| import { useCreateTeam, type UseCreateTeamOptions } from './useCreateTeam'; | ||
|
|
||
| export function useCreateTeamForm(mutateOptions: UseCreateTeamOptions = {}) { | ||
| const form = useForm<CreateTeamFormValues>({ |
There was a problem hiding this comment.
https://react-hook-form.com/docs/useform
mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit' - почитай, как и кто что делает
https://react-hook-form.com/docs/useform#reValidateMode
reValidateMode: onChange | onBlur | onSubmit = 'onChange' - почитай, как и кто что делает
https://react-hook-form.com/docs/useform#criteriaMode
criteriaMode: firstError | all - лучше до первой ошибки
There was a problem hiding this comment.
Это база, что улучшить ux, в плане ошибок, валидации и ре-рендеров
feat: UI–BLL Integration
Ветка интегрирует фронтенд с реальным API и завершает построение ключевых фич продукта.
Что сделано
Teams — полный CRUD
createStorefactory + team slug store)Team Settings — реальное API
react-hook-form+ TanStack Query (вместо моков)Profile — вкладка Teams
Layout & Sidebar
SidebarLayout, модуль сайдбара переработан с плоской структуройTeamsDropdownTabsNavEntities & Shared
teamиuser, добавленUserAvatar,SlugFieldcreateStorefactory с автогенерацией селекторовinvite → invitationпо всей кодовой базеfile → assetTheme
Infra
infra/dev/.env.exampleПроверка