omnibook-types
v1.0.6
Published
Shared TypeScript types, Tanstack Query hooks, and Zustand stores for React Native and Next.js projects
Downloads
29
Readme
Omnibook Types
A shared TypeScript library providing types, hooks, functions, and Zustand stores for both React Native and Next.js projects.
Features
- 🔒 Authentication System - Complete auth flow with platform-specific login
- 🌐 Cross-Platform - Works identically in React Native and Next.js
- 🚀 TanStack Query Integration - Optimized data fetching
- 🎨 Zustand Stores - Shared state management
- 📱 Platform Detection - Smart platform-specific behavior
- 🔄 Auto Token Refresh - Seamless authentication
- 💾 Persistent Storage - Works with localStorage and AsyncStorage
Installation
npm install omnibook-types @tanstack/react-query zustandPlatform-Specific Dependencies
React Native:
npm install @react-native-async-storage/async-storageNext.js: No additional dependencies needed.
Setup
1. Configure TanStack Query
Next.js (app/layout.tsx):
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthProvider } from 'omnibook-types';
const queryClient = new QueryClient();
export default function RootLayout({ children }) {
return (
<html>
<body>
<QueryClientProvider client={queryClient}>
<AuthProvider>
{children}
</AuthProvider>
</QueryClientProvider>
</body>
</html>
);
}React Native (App.tsx):
import React from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthProvider } from 'omnibook-types';
import AsyncStorage from '@react-native-async-storage/async-storage';
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider storage={AsyncStorage}>
{/* Your app components */}
</AuthProvider>
</QueryClientProvider>
);
}2. Environment Configuration
Create your API base URL configuration:
// config/api.ts
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.omnibook.online';Authentication Usage
Login/Register
import { useAuth } from 'omnibook-types';
function LoginScreen() {
const { login, register, isLoading, error } = useAuth();
const handleLogin = async () => {
try {
await login({
email: '[email protected]',
password: 'password123'
});
// User is now authenticated
} catch (error) {
console.error('Login failed:', error);
}
};
const handleRegister = async () => {
try {
await register({
email: '[email protected]',
password: 'password123',
firstName: 'John',
lastName: 'Doe'
});
// User is registered and automatically logged in
} catch (error) {
console.error('Registration failed:', error);
}
};
return (
<div>
{error && <p>Error: {error}</p>}
<button onClick={handleLogin} disabled={isLoading}>
Login
</button>
<button onClick={handleRegister} disabled={isLoading}>
Register
</button>
</div>
);
}Protected Routes/Components
import { useAuth } from 'omnibook-types';
function ProtectedComponent() {
const { user, isAuthenticated, isLoading } = useAuth();
if (isLoading) return <div>Loading...</div>;
if (!isAuthenticated) return <div>Please login</div>;
return (
<div>
<h1>Welcome, {user?.firstName}!</h1>
<p>Email: {user?.email}</p>
</div>
);
}Using Auth Store Directly
import { useAuthStore } from 'omnibook-types';
function UserProfile() {
const { user, updateUser, logout } = useAuthStore();
const handleLogout = async () => {
await logout();
};
return (
<div>
<h1>{user?.firstName} {user?.lastName}</h1>
<p>Role: {user?.role}</p>
<button onClick={handleLogout}>Logout</button>
</div>
);
}Fetching Current User Data
import { useCurrentUser, getCurrentUser, userApi } from 'omnibook-types';
// Using TanStack Query hook (recommended)
function UserProfile() {
const { data: user, isLoading, error, refetch } = useCurrentUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{user?.firstName} {user?.lastName}</h1>
<p>Email: {user?.email}</p>
<p>Role: {user?.role}</p>
<button onClick={() => refetch()}>Refresh</button>
</div>
);
}
// Using direct API calls
async function loadUser() {
try {
const user = await getCurrentUser(); // Direct function
// or
const user2 = await userApi.getCurrentUser(); // Via userApi
console.log('Current user:', user);
} catch (error) {
console.error('Failed to fetch user:', error);
}
}API Usage
With Authentication
import { authApi, useApiQuery, useApiMutation } from 'omnibook-types';
function UserDashboard() {
// Authenticated GET request using TanStack Query
const { data: salons, isLoading } = useApiQuery(
['salons'],
{ url: '/api/Salon', method: 'GET' }
);
// Authenticated mutation
const createSalon = useApiMutation({
onSuccess: () => {
// Invalidate and refetch salons
}
});
// Direct API calls with automatic auth headers
const handleCreateSalon = async () => {
try {
const newSalon = await authApi.post('/api/Salon', {
name: 'My Salon',
email: '[email protected]',
// ... other salon data
});
console.log('Created salon:', newSalon);
} catch (error) {
console.error('Failed to create salon:', error);
}
};
return (
<div>
{isLoading ? (
<div>Loading salons...</div>
) : (
salons?.map(salon => (
<div key={salon.id}>{salon.name}</div>
))
)}
<button onClick={handleCreateSalon}>Create Salon</button>
</div>
);
}Without Authentication
import { useApiQuery } from 'omnibook-types';
function PublicComponent() {
const { data, isLoading, error } = useApiQuery(
['public-data'],
{ url: '/api/public-endpoint', method: 'GET' }
);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{JSON.stringify(data)}</div>;
}Platform Detection
import { usePlatform, useIsWeb, useIsMobile } from 'omnibook-types';
function PlatformAwareComponent() {
const platform = usePlatform(); // 'web' | 'ios' | 'android'
const isWeb = useIsWeb();
const isMobile = useIsMobile();
return (
<div>
<p>Platform: {platform}</p>
{isWeb && <p>Web-specific content</p>}
{isMobile && <p>Mobile-specific content</p>}
</div>
);
}Theme Management
import { useThemeStore } from 'omnibook-types';
function ThemeToggle() {
const { currentTheme, theme, setTheme, toggleTheme } = useThemeStore();
return (
<div style={{
backgroundColor: theme.colors.background,
color: theme.colors.text
}}>
<p>Current theme: {currentTheme}</p>
<button onClick={toggleTheme}>Toggle Theme</button>
<button onClick={() => setTheme('light')}>Light</button>
<button onClick={() => setTheme('dark')}>Dark</button>
<button onClick={() => setTheme('system')}>System</button>
</div>
);
}Local Storage
import { useLocalStorage } from 'omnibook-types';
function SettingsComponent() {
const [settings, setSettings] = useLocalStorage('user-settings', {
notifications: true,
theme: 'auto'
});
const updateSetting = (key: string, value: any) => {
setSettings(prev => ({ ...prev, [key]: value }));
};
return (
<div>
<label>
<input
type="checkbox"
checked={settings.notifications}
onChange={(e) => updateSetting('notifications', e.target.checked)}
/>
Enable notifications
</label>
</div>
);
}Validation
import {
validateField,
validateObject,
ValidationRules
} from 'omnibook-types';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const emailValidation = validateField(email, [
ValidationRules.required(),
ValidationRules.email()
]);
const formValidation = validateObject(
{ email, password },
{
email: [ValidationRules.required(), ValidationRules.email()],
password: [ValidationRules.required(), ValidationRules.minLength(6)]
}
);
return (
<form>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className={!emailValidation.isValid ? 'error' : ''}
/>
{emailValidation.errors.map(error => (
<span key={error} className="error">{error}</span>
))}
<button disabled={!formValidation.isValid}>
Login
</button>
</form>
);
}Type Definitions
All types from the OpenAPI schema are included:
import type {
// Auth types
LoginRequest,
RegisterRequest,
UserResponse,
CurrentUserResponse,
AuthUser,
// API types
ApiResponse,
ApiError,
// Common types
Theme,
Platform
} from 'omnibook-types';Development
# Install dependencies
npm install
# Build the library
npm run build
# Development mode (watch)
npm run dev
# Type checking
npm run type-check
# Linting
npm run lintContributing
- Follow the existing code style
- Add proper TypeScript types
- Test on both React Native and Next.js
- Update documentation for new features
License
MIT
