Logo
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.ts and exported as something: somethingReducer in store
  • Files colocated when used by only one feature; shared code in src/lib or src/types

Folder Structure

  • src/assets/ images, fonts
  • src/components/ shared UI and layout components
  • src/contexts/ app-level contexts (auth, orientation, network, appState, customerDisplay)
  • src/hooks/ reusable hooks
  • src/lib/
    • api/ApiClient.ts
    • constants/baseURL.ts
    • theme/ color, font size, font weight
    • utils/ error, permissions, listeners, token helpers
  • src/navigation/ navigators and param types
  • src/redux/ store, typed hooks, slices
  • src/screens/ feature screens grouped by domain
  • src/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

  1. Start simple: minimal, readable components; split only when necessary
  2. One hook, one purpose: focused, reusable, typed
  3. Low coupling, high cohesion: colocate feature-only code under the feature; share via src/lib/* when broadly used
  4. Explicit data boundaries: React Query for server-state; Redux for UI/flow/cross-screen ephemeral state
  5. 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.tsx with 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 in src/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 }),
});
  • @react-navigation/native-stack for stacks; bottom tabs + material top tabs
  • Types centralized in src/navigation/navigation.type.ts; import types for useNavigation and NativeStackScreenProps
<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 (use color.*, fontSize, fontWeight)
  • Prefer inline styles or StyleSheet with theme tokens; nativewind present but not widely used here

Error Handling

  • Use errorResponse to throw normalized Error(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