Tantri POS
Guidelines
Standards for developing and maintaining the Tantri POS React Native app (RN 0.80, React 19). Covers folder structure, naming, data layer, navigation, styling, theming, and engineering practices.
Core Libraries (in use)
- axios: HTTP client (
src/lib/api/ApiClient.ts) - @tanstack/react-query v5: server-state, with persistence via AsyncStorage
- redux-toolkit + react-redux: client/UI state (
src/redux/*) - @react-navigation/native + native-stack + bottom-tabs + material-top-tabs
- @react-native-async-storage/async-storage: storage and query persistence
- moment (+ locale id): date/time formatting
- notifee + @react-native-firebase/messaging: notifications
- react-native-gesture-handler, screens, safe-area-context, reanimated
- react-native-fast-image: images
Naming
- Components: PascalCase (e.g.
StoreInformation.tsx) - Hooks, variables, functions, types, enums: camelCase (e.g.
useUserData,errorResponse,userTokenType) - Slices:
somethingSlice.tsand exported assomething: somethingReducerin store - Files colocated when used by only one feature; shared code in
src/liborsrc/types
Folder Structure
src/assets/images, fontssrc/components/shared UI and layout componentssrc/contexts/app-level contexts (auth, orientation, network, appState, customerDisplay)src/hooks/reusable hookssrc/lib/api/ApiClient.tsconstants/baseURL.tstheme/color, font size, font weightutils/error, permissions, listeners, token helpers
src/navigation/navigators and param typessrc/redux/store, typed hooks, slicessrc/screens/feature screens grouped by domainsrc/types/app-wide TS types and enums
appNavigation.tsx
navigation.type.ts
cartSlice.ts
paymentSlice.ts
settingSlice.ts
store.ts
hooks.ts
package.json
app.json
babel.config.js
// Providers wiring (Redux + React Query + Contexts)
<Provider store={store}>
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister: asyncStoragePersister }}
>
<AuthContext.Provider value={{ token, setToken, removeToken }}>
<OrientationContext.Provider value={{ isLandscape, screenOrientation }}>
<NetworkContext.Provider value={{ isConnected, isInternetReachable }}>
<AppStateContext.Provider value={{ appStateVisible }}>
<CustomerDisplayContext.Provider
value={{ showCustomerDisplay, setShowCustomerDisplay }}
>
<AppNavigation />
</CustomerDisplayContext.Provider>
</AppStateContext.Provider>
</NetworkContext.Provider>
</OrientationContext.Provider>
</AuthContext.Provider>
</PersistQueryClientProvider>
</Provider>Engineering Principles
- Start simple: minimal, readable components; split only when necessary
- One hook, one purpose: focused, reusable, typed
- Low coupling, high cohesion: colocate feature-only code under the feature; share via
src/lib/*when broadly used - Explicit data boundaries: React Query for server-state; Redux for UI/flow/cross-screen ephemeral state
- Typed APIs and params: type inputs/outputs for queries, mutations, and navigation params
API Layer
- Base URL from
src/lib/constants/baseURL.ts(staging/prod toggled in file) - Shared axios instance in
src/lib/api/ApiClient.ts - Use
errorResponse(error)to normalize thrown errors
const ApiClient = axios.create({ baseURL: apiUrl });
export default ApiClient;Example request with auth header and error handling:
const getDataStore = async ({
token,
idCafe,
}: {
token: string | null;
idCafe: string | null;
}) => {
try {
const res = await ApiClient.get(`/cafe/${idCafe}`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data.data;
} catch (error: any) {
errorResponse(error);
}
};React Query
- QueryClient created in
App.tsxwith AsyncStorage persister - Use v5 APIs (
useQuery,useMutation,useQueryClient) - Key rules: stable
queryKey, avoid mixing server-state into Redux
export const useGetDataStore = ({
token,
idCafe,
}: {
token: string | null;
idCafe: string | null;
}) =>
useQuery({
queryKey: ["getDataStore"],
queryFn: () => getDataStore({ token, idCafe }),
});Redux
- Store in
src/redux/store.ts, typed hooks insrc/redux/hooks.ts - Use for cart, payment, printer, shifting, setting, unread, splitBill, currency, etc.
export const store = configureStore({
reducer: {
buyer: buyerReducer,
cart: cartReducer,
payment: paymentReducer,
unread: unreadReducer,
printer: printerReducer,
viewMode: viewModeReducer,
shifting: shiftingSlice,
setting: settingSlice,
splitBill: splitBillSlice,
currency: currencySlice,
},
middleware: (g) => g({ serializableCheck: false }),
});Navigation
@react-navigation/native-stackfor stacks; bottom tabs + material top tabs- Types centralized in
src/navigation/navigation.type.ts; import types foruseNavigationandNativeStackScreenProps
<NavigationContainer>
<Stack.Navigator
initialRouteName={initialRoute}
screenOptions={{ headerShown: false }}
>
{token === null ? (
<>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="OTPVerification" component={OTPVerification} />
...
</>
) : (
<>
<Stack.Screen name="Dashboard" children={() => <DashboardTabs />} />
...
</>
)}
</Stack.Navigator>
</NavigationContainer>Styling & Theme
- Colors and typography in
src/lib/theme/index.ts(usecolor.*,fontSize,fontWeight) - Prefer inline styles or StyleSheet with theme tokens; nativewind present but not widely used here
Error Handling
- Use
errorResponseto throw normalizedError(message)
export const errorResponse = (error: any) => {
if (error?.response?.data?.message)
throw new Error(error.response.data.message);
if (error?.message) throw new Error(error.message);
if (error) throw new Error(error.toString());
throw new Error("Terjadi kesalahan");
};Auth Token
- Store token in
AuthContext; pass Bearer in per-request headers - Token refresh helper:
src/lib/utils/token/refreshToken.ts
Notifications & Realtime
- FCM + notifee for push; websocket listener component when authenticated
Do/Don't
- Do: colocate feature-only fetchers/hooks under the feature folder
- Do: keep server-state in React Query; UI/flow in Redux
- Do: type navigation params and API responses
- Don't: call APIs directly from components without a fetch helper and error handling
References
- React Navigation: https://reactnavigation.org/docs/getting-started
- TanStack Query v5: https://tanstack.com/query/latest
- Redux Toolkit: https://redux-toolkit.js.org/
- React Native Firebase Messaging: https://rnfirebase.io/messaging/usage
- Notifee: https://notifee.app/react-native/docs/overview

