Initial commit: FunZone accounting frontend with Sepidar-inspired RTL modules

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Shayan Azadi
2026-07-01 00:29:25 +03:30
commit ce6c110e06
52 changed files with 8257 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
import { useCallback, useEffect, useState } from 'react'
interface ApiResourceState<T> {
data: T | null
loading: boolean
error: string | null
reload: () => void
}
/**
* Fetches a remote resource and tracks loading/error state.
* Re-runs whenever `deps` change and only when `enabled` is true.
*/
export function useApiResource<T>(
fetcher: () => Promise<T>,
deps: ReadonlyArray<unknown>,
enabled = true,
): ApiResourceState<T> {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const run = useCallback(async () => {
if (!enabled) return
setLoading(true)
setError(null)
try {
setData(await fetcher())
} catch (err) {
setError(err instanceof Error ? err.message : 'خطای ناشناخته رخ داد.')
} finally {
setLoading(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [enabled, ...deps])
useEffect(() => {
run()
}, [run])
return { data, loading, error, reload: run }
}