* feat(schema): add auth, people, and financial tables Add users, organizations, teams, groups, and project members tables. Extend customers/vendors with netsuite fields. Add netsuite schema for invoices, bills, payments, and credit memos. Include all migrations, seeds, new UI primitives, and config updates. * feat(auth): add WorkOS authentication system Add login, signup, password reset, email verification, and invitation flows via WorkOS AuthKit. Includes auth middleware, permission helpers, dev mode fallbacks, and auth page components. * feat(people): add people management system Add user, team, group, and organization management with CRUD actions, dashboard pages, invite dialog, user drawer, and role-based filtering. Includes WorkOS invitation integration. * feat(netsuite): add NetSuite integration and financials Add bidirectional NetSuite REST API integration with OAuth 2.0, rate limiting, sync engine, and conflict resolution. Includes invoices, vendor bills, payments, credit memos CRUD, customer/vendor management pages, and financial dashboard with tabbed views. * feat(ui): add mobile support and dashboard improvements Add mobile bottom nav, FAB, filter bar, search, project switcher, pull-to-refresh, and schedule mobile view. Update sidebar with new nav items, settings modal with integrations tab, responsive dialogs, improved schedule and file components, PWA manifest, and service worker. * ci: retrigger build * ci: retrigger build --------- Co-authored-by: Nicholai <nicholaivogelfilms@gmail.com>
60 lines
1.4 KiB
TypeScript
Executable File
60 lines
1.4 KiB
TypeScript
Executable File
"use client"
|
|
|
|
import { useState, useRef, useCallback } from "react"
|
|
|
|
const TRIGGER_THRESHOLD = 80
|
|
const MAX_PULL = 120
|
|
|
|
interface PullToRefreshResult {
|
|
isRefreshing: boolean
|
|
pullDistance: number
|
|
handlers: {
|
|
onTouchStart: (e: React.TouchEvent) => void
|
|
onTouchMove: (e: React.TouchEvent) => void
|
|
onTouchEnd: () => void
|
|
}
|
|
}
|
|
|
|
export function usePullToRefresh(
|
|
onRefresh: () => Promise<void>
|
|
): PullToRefreshResult {
|
|
const [isRefreshing, setIsRefreshing] = useState(false)
|
|
const [pullDistance, setPullDistance] = useState(0)
|
|
const startY = useRef<number | null>(null)
|
|
|
|
const onTouchStart = useCallback((e: React.TouchEvent) => {
|
|
if (window.scrollY === 0) {
|
|
startY.current = e.touches[0].clientY
|
|
}
|
|
}, [])
|
|
|
|
const onTouchMove = useCallback((e: React.TouchEvent) => {
|
|
if (startY.current === null || window.scrollY > 0) return
|
|
const currentY = e.touches[0].clientY
|
|
const distance = Math.max(
|
|
0,
|
|
Math.min(currentY - startY.current, MAX_PULL)
|
|
)
|
|
setPullDistance(distance)
|
|
}, [])
|
|
|
|
const onTouchEnd = useCallback(async () => {
|
|
if (pullDistance > TRIGGER_THRESHOLD) {
|
|
setIsRefreshing(true)
|
|
try {
|
|
await onRefresh()
|
|
} finally {
|
|
setIsRefreshing(false)
|
|
}
|
|
}
|
|
setPullDistance(0)
|
|
startY.current = null
|
|
}, [pullDistance, onRefresh])
|
|
|
|
return {
|
|
isRefreshing,
|
|
pullDistance,
|
|
handlers: { onTouchStart, onTouchMove, onTouchEnd },
|
|
}
|
|
}
|