diff --git a/clients/mobile/app/_layout.tsx b/clients/mobile/app/_layout.tsx index 44af1c301..cd2effd53 100644 --- a/clients/mobile/app/_layout.tsx +++ b/clients/mobile/app/_layout.tsx @@ -1,4 +1,4 @@ -import { Stack } from "expo-router"; +import { Stack, Redirect } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { SafeAreaProvider } from "react-native-safe-area-context"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; @@ -11,7 +11,12 @@ import { } from "@react-navigation/native"; import { tokenCache } from "@clerk/clerk-expo/token-cache"; -import { ClerkProvider, ClerkLoaded, useAuth } from "@clerk/clerk-expo"; +import { + ClerkProvider, + ClerkLoaded, + useAuth, + useOrganization, +} from "@clerk/clerk-expo"; import { useColorScheme } from "@/hooks/use-color-scheme"; import { setConfig } from "@shared"; @@ -33,9 +38,17 @@ export const unstable_settings = { // Component to configure auth provider and the api base url function AppConfigurator() { const { getToken } = useAuth(); + const { organization } = useOrganization(); + const hotelId = organization?.publicMetadata?.hotel_id; + + if (!hotelId) { + return ; + } + setConfig({ API_BASE_URL: process.env.EXPO_PUBLIC_API_BASE_URL ?? "", getToken, + hotelId: hotelId as string, }); return null; } diff --git a/clients/mobile/app/no-org.tsx b/clients/mobile/app/no-org.tsx new file mode 100644 index 000000000..137b7b7ac --- /dev/null +++ b/clients/mobile/app/no-org.tsx @@ -0,0 +1,11 @@ +import { View, Text } from "react-native"; + +export default function NoOrg() { + return ( + + + No Organization Found + + + ); +} diff --git a/clients/shared/src/api/client.ts b/clients/shared/src/api/client.ts index 648ae378f..4da8b4fbf 100644 --- a/clients/shared/src/api/client.ts +++ b/clients/shared/src/api/client.ts @@ -7,8 +7,8 @@ import { getConfig } from "./config"; export const createRequest = ( getToken: () => Promise, baseUrl: string, + hotelId: string, ) => { - const hardCodedHotelId = "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" return async (config: RequestConfig): Promise => { let fullUrl = `${baseUrl}${config.url}`; if (config.params && Object.keys(config.params).length > 0) { @@ -24,7 +24,7 @@ export const createRequest = ( headers: { "Content-Type": "application/json", ...(token && { Authorization: `Bearer ${token}` }), - "X-Hotel-ID": hardCodedHotelId, + "X-Hotel-ID": hotelId, ...config.headers, }, body: config.data ? JSON.stringify(config.data) : undefined, @@ -74,9 +74,9 @@ export const useAPIClient = (): HttpClient => { // can be called during app startup (e.g. in a useEffect) // before any API calls are executed. const request = async (config: RequestConfig): Promise => { - const { getToken } = getConfig(); + const { getToken, hotelId } = getConfig(); const baseUrl = getBaseUrl(); - const doRequest = createRequest(getToken, baseUrl); + const doRequest = createRequest(getToken, baseUrl, hotelId); return doRequest(config); }; diff --git a/clients/shared/src/api/config.ts b/clients/shared/src/api/config.ts index fee2aae85..12cb60820 100644 --- a/clients/shared/src/api/config.ts +++ b/clients/shared/src/api/config.ts @@ -8,6 +8,7 @@ export type Config = { API_BASE_URL: string getToken: () => Promise + hotelId: string } let config: Config | null = null diff --git a/clients/shared/src/api/orval-mutator.ts b/clients/shared/src/api/orval-mutator.ts index 927ca8e5e..ad0d90a55 100755 --- a/clients/shared/src/api/orval-mutator.ts +++ b/clients/shared/src/api/orval-mutator.ts @@ -10,8 +10,8 @@ import { RequestConfig } from "../types/api.types"; export const useCustomInstance = (): (( config: RequestConfig, ) => Promise) => { - const { getToken } = getConfig(); - const request = createRequest(getToken, getBaseUrl()); + const { getToken, hotelId } = getConfig(); + const request = createRequest(getToken, getBaseUrl(), hotelId); return async (config: RequestConfig): Promise => { const response = await request(config); diff --git a/clients/web/src/routeTree.gen.ts b/clients/web/src/routeTree.gen.ts index 385331118..d03f3cd37 100644 --- a/clients/web/src/routeTree.gen.ts +++ b/clients/web/src/routeTree.gen.ts @@ -11,6 +11,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as SignUpRouteImport } from './routes/sign-up' import { Route as SignInRouteImport } from './routes/sign-in' +import { Route as NoOrgRouteImport } from './routes/no-org' import { Route as ProtectedRouteImport } from './routes/_protected' import { Route as IndexRouteImport } from './routes/index' import { Route as ProtectedTestApiRouteImport } from './routes/_protected/test-api' @@ -32,6 +33,11 @@ const SignInRoute = SignInRouteImport.update({ path: '/sign-in', getParentRoute: () => rootRouteImport, } as any) +const NoOrgRoute = NoOrgRouteImport.update({ + id: '/no-org', + path: '/no-org', + getParentRoute: () => rootRouteImport, +} as any) const ProtectedRoute = ProtectedRouteImport.update({ id: '/_protected', getParentRoute: () => rootRouteImport, @@ -84,6 +90,7 @@ const ProtectedGuestsGuestIdRoute = ProtectedGuestsGuestIdRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/no-org': typeof NoOrgRoute '/sign-in': typeof SignInRoute '/sign-up': typeof SignUpRoute '/home': typeof ProtectedHomeRoute @@ -97,6 +104,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/': typeof IndexRoute + '/no-org': typeof NoOrgRoute '/sign-in': typeof SignInRoute '/sign-up': typeof SignUpRoute '/home': typeof ProtectedHomeRoute @@ -111,6 +119,7 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_protected': typeof ProtectedRouteWithChildren + '/no-org': typeof NoOrgRoute '/sign-in': typeof SignInRoute '/sign-up': typeof SignUpRoute '/_protected/home': typeof ProtectedHomeRoute @@ -126,6 +135,7 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' + | '/no-org' | '/sign-in' | '/sign-up' | '/home' @@ -139,6 +149,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' + | '/no-org' | '/sign-in' | '/sign-up' | '/home' @@ -152,6 +163,7 @@ export interface FileRouteTypes { | '__root__' | '/' | '/_protected' + | '/no-org' | '/sign-in' | '/sign-up' | '/_protected/home' @@ -167,6 +179,7 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute ProtectedRoute: typeof ProtectedRouteWithChildren + NoOrgRoute: typeof NoOrgRoute SignInRoute: typeof SignInRoute SignUpRoute: typeof SignUpRoute } @@ -187,6 +200,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof SignInRouteImport parentRoute: typeof rootRouteImport } + '/no-org': { + id: '/no-org' + path: '/no-org' + fullPath: '/no-org' + preLoaderRoute: typeof NoOrgRouteImport + parentRoute: typeof rootRouteImport + } '/_protected': { id: '/_protected' path: '' @@ -299,6 +319,7 @@ const ProtectedRouteWithChildren = ProtectedRoute._addFileChildren( const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, ProtectedRoute: ProtectedRouteWithChildren, + NoOrgRoute: NoOrgRoute, SignInRoute: SignInRoute, SignUpRoute: SignUpRoute, } diff --git a/clients/web/src/routes/__root.tsx b/clients/web/src/routes/__root.tsx index db64dc9a1..541323965 100644 --- a/clients/web/src/routes/__root.tsx +++ b/clients/web/src/routes/__root.tsx @@ -1,7 +1,12 @@ -import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router"; +import { + HeadContent, + Scripts, + createRootRoute, + useNavigate, +} from "@tanstack/react-router"; import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; import { TanStackDevtools } from "@tanstack/react-devtools"; -import { ClerkProvider, useAuth } from "@clerk/clerk-react"; +import { ClerkProvider, useAuth, useOrganization } from "@clerk/clerk-react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { setConfig } from "@shared"; @@ -60,8 +65,21 @@ export const Route = createRootRoute({ // Component to configure auth provider and the api base url function AppConfigurator() { const { getToken } = useAuth(); + const { organization } = useOrganization(); + const hotelId = organization?.publicMetadata.hotel_id; + + const navigate = useNavigate(); + + if (!hotelId) { + navigate({ to: "/no-org" }); + } + useEffect(() => { - setConfig({ API_BASE_URL: process.env.API_BASE_URL ?? "", getToken }); + setConfig({ + API_BASE_URL: process.env.API_BASE_URL ?? "", + getToken, + hotelId: hotelId as string, + }); }, [getToken]); return null; diff --git a/clients/web/src/routes/no-org.tsx b/clients/web/src/routes/no-org.tsx new file mode 100644 index 000000000..6b91bdc58 --- /dev/null +++ b/clients/web/src/routes/no-org.tsx @@ -0,0 +1,17 @@ +import { createFileRoute } from "@tanstack/react-router"; + +export const Route = createFileRoute("/no-org")({ + component: NoOrgPage, +}); + +function NoOrgPage() { + return ( +
+

No Organization Found

+

+ You're not part of a hotel organization yet. Please contact your manager + for an invitation. +

+
+ ); +}