initialize

This commit is contained in:
2026-03-26 03:49:05 +03:00
parent e9211a4aba
commit 5dc54ffd00
146 changed files with 12078 additions and 146 deletions
View File
+24
View File
@@ -0,0 +1,24 @@
"use client"
import { useEffect } from "react"
import { useAuthStore } from "@/shared/stores/auth-store"
/**
* Hydrates auth state from server cookies on mount and exposes auth helpers.
*/
export function useAuth() {
const store = useAuthStore()
useEffect(() => {
store.hydrate()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return {
token: store.token,
user: store.user,
isAuthenticated: store.isAuthenticated,
login: store.login,
logout: store.logout,
}
}
+22
View File
@@ -0,0 +1,22 @@
"use client"
import { useMutation, type UseMutationOptions } from "@tanstack/react-query"
import type { FieldValues, UseFormReturn } from "react-hook-form"
import { ApiError } from "@repo/api"
export function useFormMutation<TValues extends FieldValues, TResponse = unknown>(
form: UseFormReturn<TValues>,
options: UseMutationOptions<TResponse, Error, TValues>,
) {
return useMutation<TResponse, Error, TValues>({
...options,
onError: (err, vars,values, ctx) => {
if (err instanceof ApiError && err.validationErrors) {
Object.entries(err.validationErrors).forEach(([field, msgs]) => {
form.setError(field as any, { message: msgs[0] })
})
}
options.onError?.(err, vars, values, ctx,)
},
})
}
+19
View 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
}
+61
View File
@@ -0,0 +1,61 @@
"use client"
import { useEffect } from "react"
import { useForm, type DefaultValues, type FieldValues, type UseFormReturn } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { useQuery, type QueryKey } from "@tanstack/react-query"
import type { ZodType } from "zod"
type UseResourceFormOptions<TFormValues extends FieldValues, TApiData = unknown> = {
schema: ZodType<TFormValues, any, any>
defaultValues: DefaultValues<TFormValues>
resourceId?: string | null
initialize?: (id: string) => Promise<TApiData>
mapToFormValues: (data: TApiData) => TFormValues
initialData?: TApiData | null
queryKey?: QueryKey
}
type UseResourceFormReturn<TFormValues extends FieldValues> = {
form: UseFormReturn<TFormValues>
isEditing: boolean
isInitializing: boolean
}
export function useResourceForm<TFormValues extends FieldValues, TApiData = unknown>({
schema,
defaultValues,
resourceId,
initialize,
mapToFormValues,
initialData,
queryKey,
}: UseResourceFormOptions<TFormValues, TApiData>): UseResourceFormReturn<TFormValues> {
const isEditing = !!resourceId
const { data: queriedData, isLoading: isQueryLoading } = useQuery<TApiData>({
queryKey: queryKey ?? ["resource", resourceId],
queryFn: () => initialize!(resourceId!),
enabled: isEditing && !!initialize,
})
const resolvedData = queriedData ?? (isEditing ? initialData : undefined)
const form = useForm<TFormValues>({
resolver: zodResolver(schema) as any,
defaultValues,
})
useEffect(() => {
if (!isEditing) {
form.reset(defaultValues)
return
}
if (resolvedData) {
form.reset(mapToFormValues(resolvedData) as any)
}
}, [isEditing, resolvedData]) // eslint-disable-line react-hooks/exhaustive-deps
return { form, isEditing, isInitializing: isEditing && !!initialize && isQueryLoading }
}