diff --git a/.gitignore b/.gitignore index 7a45fce..882155b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* -node_modules +*node_modules dist-ssr *.local diff --git a/api/src/database.types.ts b/api/src/database.types.ts index cbb3ea7..51e1eed 100644 --- a/api/src/database.types.ts +++ b/api/src/database.types.ts @@ -7,6 +7,11 @@ export type Json = | Json[] export type Database = { + // Allows to automatically instanciate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "12.2.3 (519615d)" + } public: { Tables: { devis: { @@ -367,21 +372,25 @@ export type Database = { } } -type DefaultSchema = Database[Extract] +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] export type Tables< DefaultSchemaTableNameOrOptions extends | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & - Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) : never = never, -> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } - ? (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & - Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { Row: infer R } ? R @@ -399,14 +408,16 @@ export type Tables< export type TablesInsert< DefaultSchemaTableNameOrOptions extends | keyof DefaultSchema["Tables"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, -> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } - ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { Insert: infer I } ? I @@ -422,14 +433,16 @@ export type TablesInsert< export type TablesUpdate< DefaultSchemaTableNameOrOptions extends | keyof DefaultSchema["Tables"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, -> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } - ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { Update: infer U } ? U @@ -445,14 +458,16 @@ export type TablesUpdate< export type Enums< DefaultSchemaEnumNameOrOptions extends | keyof DefaultSchema["Enums"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, EnumName extends DefaultSchemaEnumNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] : never = never, -> = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database } - ? Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] : never @@ -460,14 +475,16 @@ export type Enums< export type CompositeTypes< PublicCompositeTypeNameOrOptions extends | keyof DefaultSchema["CompositeTypes"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] : never = never, -> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database } - ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] : never diff --git a/justfile b/justfile index 4bf2b03..c2f1ad1 100644 --- a/justfile +++ b/justfile @@ -20,4 +20,13 @@ dev: just _api-dev & (just _frontend-dev) update-types: - npx supabase gen types typescript --project-id "mhcafqvzbrrwvahpvvzd" --schema public > ui/src/types/database.types.ts && cp ui/src/types/database.types.ts api/src/database.types.ts + npx supabase gen types typescript --project-id "mhcafqvzbrrwvahpvvzd" --schema public > ui/src/types/database.types.ts && cp ui/src/types/database.types.ts api/src/database.types.ts && cp ui/src/types/database.types.ts xtablo-expo/lib/database.types.ts + +expo-install-all: + cd xtablo-expo && npx expo install -- --legacy-peer-deps + +expo-install +package: + cd xtablo-expo && npx expo install {{package}} -- --legacy-peer-deps + +expo-start *args: + cd xtablo-expo && npx expo start {{args}} diff --git a/ui/package.json b/ui/package.json index 1e033d3..9a568b7 100644 --- a/ui/package.json +++ b/ui/package.json @@ -11,7 +11,7 @@ "build:staging": "tsc -b && vite build --mode staging", "build:prod": "tsc -b && vite build --mode production", "deploy:staging": "pnpm run build:staging && wrangler deploy", - "deploy:prod": "pnpm run build:prod && wrangler deploy", + "deploy:prod": "pnpm run build:prod && wrangler deploy", "cf-typegen": "wrangler types", "test": "vitest run --mode dev", "test:watch": "vitest watch", @@ -61,6 +61,8 @@ "wrangler": "^4.24.3" }, "dependencies": { + "@datadog/browser-rum": "^6.13.0", + "@datadog/browser-rum-react": "^6.13.0", "@react-stately/calendar": "^3.7.1", "@supabase/supabase-js": "^2.49.3", "@tailwindcss/vite": "^4.0.14", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 3613eac..5ff21f4 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@datadog/browser-rum': + specifier: ^6.13.0 + version: 6.13.0 + '@datadog/browser-rum-react': + specifier: ^6.13.0 + version: 6.13.0(@datadog/browser-rum@6.13.0)(react-router-dom@7.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) '@react-stately/calendar': specifier: ^3.7.1 version: 3.7.1(react@19.0.0) @@ -430,6 +436,37 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@datadog/browser-core@6.13.0': + resolution: {integrity: sha512-wWhQ22F8pDjh7kGtvBetJhX5luyKsIVzJQTisjM/6p+A2nchDuoOiZ0GdjajRA5LlPa9pj/PlOoe/g+B9AMELg==} + + '@datadog/browser-rum-core@6.13.0': + resolution: {integrity: sha512-+gp9MEDc24xB5dwvVYsOPoRueujTOy0GNW2Q3XNyWMstzuSQIwXaIuDIRVhCoi8+i4UioUCd7ZO30yEB01Ajwg==} + + '@datadog/browser-rum-react@6.13.0': + resolution: {integrity: sha512-r53c4soTqiAVK96raOQVaci163lhX6/DVXXGZteHW36q+OMTbIIEaz/jyNX+4JrXORFNrz8b1dWELt49CkDX5w==} + peerDependencies: + '@datadog/browser-rum': '*' + '@datadog/browser-rum-slim': '*' + react: 18 || 19 + react-router-dom: 6 || 7 + peerDependenciesMeta: + '@datadog/browser-rum': + optional: true + '@datadog/browser-rum-slim': + optional: true + react: + optional: true + react-router-dom: + optional: true + + '@datadog/browser-rum@6.13.0': + resolution: {integrity: sha512-VIVpoD+A2WLIskB0PXzO0Gh3bZs7MkOrzhnDpamWMrcbGH/UaX6BUmTlorqtT374Jrre6frzN2IEKGQs7kVamQ==} + peerDependencies: + '@datadog/browser-logs': 6.13.0 + peerDependenciesMeta: + '@datadog/browser-logs': + optional: true + '@emnapi/runtime@1.4.4': resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==} @@ -5528,6 +5565,26 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@datadog/browser-core@6.13.0': {} + + '@datadog/browser-rum-core@6.13.0': + dependencies: + '@datadog/browser-core': 6.13.0 + + '@datadog/browser-rum-react@6.13.0(@datadog/browser-rum@6.13.0)(react-router-dom@7.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)': + dependencies: + '@datadog/browser-core': 6.13.0 + '@datadog/browser-rum-core': 6.13.0 + optionalDependencies: + '@datadog/browser-rum': 6.13.0 + react: 19.0.0 + react-router-dom: 7.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + + '@datadog/browser-rum@6.13.0': + dependencies: + '@datadog/browser-core': 6.13.0 + '@datadog/browser-rum-core': 6.13.0 + '@emnapi/runtime@1.4.4': dependencies: tslib: 2.8.1 diff --git a/ui/src/App.tsx b/ui/src/App.tsx index a0275ed..62de770 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,116 +1,49 @@ -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import { LoginPage } from "./pages/login"; -import { SignUpPage } from "./pages/signup"; -import { ThemeProvider } from "./contexts/ThemeContext"; +import { BrowserRouter as Router, useRoutes } from "react-router-dom"; +import { ThemeProvider } from "@ui/contexts/ThemeContext"; import { twMerge } from "tailwind-merge"; -import { ResetPasswordPage } from "./pages/reset-password"; -import { LandingPage } from "./pages/landing"; -import { PublicRoute } from "./components/PublicRoute"; -import { TabloPage } from "./pages/tablo"; -import { SessionProvider } from "./contexts/SessionContext"; -import { OAuthSigninPage } from "./pages/oauth-signin"; -import { NotFoundPage } from "./pages/NotFoundPage"; -import { Layout } from "./components/Layout"; -import { DevisPage } from "./pages/devis"; -import { FacturesPage } from "./pages/factures"; -import { PlanningPage } from "./pages/planning"; -import { ChantiersPage } from "./pages/chantiers"; -import { ChatPage } from "./pages/chat"; -import { FeedbackPage } from "./pages/feedback"; -import { SupportPage } from "./pages/support"; +import { SessionProvider } from "@ui/contexts/SessionContext"; import { AllCommunityModule, ModuleRegistry } from "ag-grid-community"; -import ChatProvider from "./providers/ChatProvider"; -import { UserStoreProvider } from "./providers/UserStoreProvider"; -import { ProtectedRoute } from "./components/ProtectedRoute"; -import { JoinPage } from "@ui/pages/join"; -import { CreateEventModal } from "./components/CreateEventModal"; -import { isProd } from "./utils/helpers"; +import { UserStoreProvider } from "@ui/providers/UserStoreProvider"; +import { isProd } from "@ui/utils/helpers"; +import { DatadogRumProvider } from "@ui/providers/DatadogRumProvider"; +import { routes } from "@ui/lib/routes"; // Register all Community features ModuleRegistry.registerModules([AllCommunityModule]); +const AppRoutes = () => { + const element = useRoutes(routes); + return element; +}; + export const App = () => { return ( -
- - }> - }> - } /> - } /> - } /> - } /> - }> - - - } /> - - }> - - - - } /> - - {(client) => } - - } - > - - - - } /> - } /> - - - - + +
+ + -
+ .animate-slide { + animation: slide 24s linear infinite; + } + `} + +
+
diff --git a/ui/src/lib/routes.tsx b/ui/src/lib/routes.tsx new file mode 100644 index 0000000..36a05f1 --- /dev/null +++ b/ui/src/lib/routes.tsx @@ -0,0 +1,139 @@ +import { RouteObject } from "react-router-dom"; +import { ProtectedRoute } from "@ui/components/ProtectedRoute"; +import { Layout } from "@ui/components/Layout"; +import { TabloPage } from "@ui/pages/tablo"; +import { DevisPage } from "@ui/pages/devis"; +import { FacturesPage } from "@ui/pages/factures"; +import { PlanningPage } from "@ui/pages/planning"; +import { NotFoundPage } from "@ui/pages/NotFoundPage"; +import { JoinPage } from "@ui/pages/join"; +import { OAuthSigninPage } from "@ui/pages/oauth-signin"; +import { LandingPage } from "@ui/pages/landing"; +import { LoginPage } from "@ui/pages/login"; +import { SignUpPage } from "@ui/pages/signup"; +import { ResetPasswordPage } from "@ui/pages/reset-password"; +import { PublicRoute } from "@ui/components/PublicRoute"; +import ChatProvider from "@ui/providers/ChatProvider"; +import { CreateEventModal } from "@ui/components/CreateEventModal"; +import { ChantiersPage } from "@ui/pages/chantiers"; +import { ChatPage } from "@ui/pages/chat"; +import { FeedbackPage } from "@ui/pages/feedback"; +import { SupportPage } from "@ui/pages/support"; + +export const routes: RouteObject[] = [ + // Protected routes + { + path: "/", + element: , + children: [ + { + path: "", + element: , + children: [ + { + index: true, + element: , + }, + { + path: "tablo", + element: , + }, + { + path: "devis", + element: , + }, + { + path: "factures", + element: , + }, + { + path: "planning", + element: , + children: [ + { index: true }, + { path: ":tablo_id" }, + { path: "create", element: }, + ], + }, + { + path: "kanban", + element: , + children: [{ index: true }, { path: ":tablo_id" }], + }, + { + path: "chantiers", + element: , + }, + { + path: "chat", + element: ( + + + + ), + children: [{ index: true }, { path: ":channelId" }], + }, + { + path: "feedback", + element: , + }, + { + path: "support", + element: , + }, + ], + }, + ], + }, + // Protected routes with redirect to current page + { + path: "/join/:tablo_name", + element: , + children: [ + { + path: "", + element: , + children: [ + { + index: true, + element: , + }, + ], + }, + ], + }, + // OAuth signin route + { + path: "/login-with-oauth", + element: , + }, + // Landing page + { + path: "/landing", + element: , + }, + // Public routes (authentication pages) + { + path: "/", + element: , + children: [ + { + path: "login", + element: , + }, + { + path: "signup", + element: , + }, + { + path: "reset-password", + element: , + }, + ], + }, + // Catch-all route for 404 + { + path: "*", + element: , + }, +]; diff --git a/ui/src/lib/rum.ts b/ui/src/lib/rum.ts new file mode 100644 index 0000000..34e568c --- /dev/null +++ b/ui/src/lib/rum.ts @@ -0,0 +1,21 @@ +import { datadogRum } from "@datadog/browser-rum"; +import { reactPlugin } from "@datadog/browser-rum-react"; + +export const initRum = () => { + datadogRum.init({ + applicationId: "8e268e1a-1be0-44c6-b12a-978530d497c7", + clientToken: "pub1761af09ab04e215cc90d34da6ac576b", + site: "datadoghq.com", + service: "xtablo-ui", + env: import.meta.env.MODE, + + // Specify a version number to identify the deployed version of your application in Datadog + // version: '1.0.0', + sessionSampleRate: 100, + sessionReplaySampleRate: 80, + defaultPrivacyLevel: "mask-user-input", + plugins: [reactPlugin({ router: true })], + trackViewsManually: true, + startSessionReplayRecordingManually: false, + }); +}; diff --git a/ui/src/pages/chat.tsx b/ui/src/pages/chat.tsx index 30a81c6..d9d7d26 100644 --- a/ui/src/pages/chat.tsx +++ b/ui/src/pages/chat.tsx @@ -8,20 +8,18 @@ import { } from "stream-chat-react"; import { useUser } from "@ui/providers/UserStoreProvider"; import { ChannelPreview } from "@ui/components/ChannelPreview"; -import { StreamChat } from "stream-chat"; import { useChannelFromUrl } from "@ui/hooks/channel"; import { useTablosList } from "@ui/hooks/tablos"; import { CustomChannelHeader } from "@ui/components/CustomChannelHeader"; import { useEffect, useState } from "react"; -export function ChatPage({ client }: { client: StreamChat }) { +export function ChatPage() { const user = useUser(); const filters = { members: { $in: [user.id] }, type: "messaging" }; + const { client, channel, setActiveChannel } = useChatContext(); const { channel: channelFromUrl, isChannelInUrl } = useChannelFromUrl(client); - const { channel, setActiveChannel } = useChatContext(); - const { data: tablos } = useTablosList(); const [isChannelListExpanded, setIsChannelListExpanded] = useState(false); diff --git a/ui/src/pages/login.tsx b/ui/src/pages/login.tsx index 1f2ccac..261cad9 100644 --- a/ui/src/pages/login.tsx +++ b/ui/src/pages/login.tsx @@ -39,6 +39,28 @@ export function LoginPage() { )} onClick={(e) => e.stopPropagation()} > +
+ + + + + Retour à l'accueil + +
+

Se connecter

diff --git a/ui/src/pages/signup.tsx b/ui/src/pages/signup.tsx index 8010887..296c8ca 100644 --- a/ui/src/pages/signup.tsx +++ b/ui/src/pages/signup.tsx @@ -86,6 +86,28 @@ export function SignUpPage() { )} onClick={(e) => e.stopPropagation()} > +
+ + + + + Retour à l'accueil + +
+

Créer un compte

diff --git a/ui/src/providers/ChatProvider.tsx b/ui/src/providers/ChatProvider.tsx index 86edb02..c9ed980 100644 --- a/ui/src/providers/ChatProvider.tsx +++ b/ui/src/providers/ChatProvider.tsx @@ -1,12 +1,11 @@ import { Chat, useCreateChatClient } from "stream-chat-react"; import { useUser } from "./UserStoreProvider"; import { LoadingSpinner } from "@ui/components/LoadingSpinner"; -import { StreamChat } from "stream-chat"; export default function ChatProvider({ children, }: { - children: (client: StreamChat) => React.ReactNode; + children: React.ReactNode; }) { const apiKey = import.meta.env.VITE_STREAM_CHAT_API_KEY as string; const user = useUser(); @@ -42,7 +41,7 @@ export default function ChatProvider({ return ( - {children(client)} + {children} ); } diff --git a/ui/src/providers/DatadogRumProvider.tsx b/ui/src/providers/DatadogRumProvider.tsx new file mode 100644 index 0000000..1780a80 --- /dev/null +++ b/ui/src/providers/DatadogRumProvider.tsx @@ -0,0 +1,47 @@ +import { datadogRum } from "@datadog/browser-rum"; +import { routes } from "@ui/lib/routes"; +import { initRum } from "@ui/lib/rum"; +import { useEffect } from "react"; +import { matchRoutes, RouteMatch, useLocation } from "react-router-dom"; + +function computeViewName(routeMatches: RouteMatch[]) { + let viewName = ""; + for (let index = 0; index < routeMatches.length; index++) { + const routeMatch = routeMatches[index]; + const path = routeMatch.route.path; + // Skip pathless routes + if (!path) { + continue; + } + + if (path.startsWith("/")) { + // Handle absolute child route paths + viewName = path; + } else { + // Handle route paths ending with "/" + viewName += viewName.endsWith("/") ? path : `/${path}`; + } + } + + return viewName || "/"; +} + +export const DatadogRumProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const location = useLocation(); + useEffect(() => { + initRum(); + }, []); + + useEffect(() => { + const routeMatches = matchRoutes(routes, location.pathname); + const viewName = routeMatches && computeViewName(routeMatches); + if (viewName) { + datadogRum.startView({ name: viewName }); + } + }, [location.pathname]); + return <>{children}; +}; diff --git a/ui/src/types/database.types.ts b/ui/src/types/database.types.ts index cbb3ea7..51e1eed 100644 --- a/ui/src/types/database.types.ts +++ b/ui/src/types/database.types.ts @@ -7,6 +7,11 @@ export type Json = | Json[] export type Database = { + // Allows to automatically instanciate createClient with right options + // instead of createClient(URL, KEY) + __InternalSupabase: { + PostgrestVersion: "12.2.3 (519615d)" + } public: { Tables: { devis: { @@ -367,21 +372,25 @@ export type Database = { } } -type DefaultSchema = Database[Extract] +type DatabaseWithoutInternals = Omit + +type DefaultSchema = DatabaseWithoutInternals[Extract] export type Tables< DefaultSchemaTableNameOrOptions extends | keyof (DefaultSchema["Tables"] & DefaultSchema["Views"]) - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & - Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) + ? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"]) : never = never, -> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } - ? (Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & - Database[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] & + DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends { Row: infer R } ? R @@ -399,14 +408,16 @@ export type Tables< export type TablesInsert< DefaultSchemaTableNameOrOptions extends | keyof DefaultSchema["Tables"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, -> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } - ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { Insert: infer I } ? I @@ -422,14 +433,16 @@ export type TablesInsert< export type TablesUpdate< DefaultSchemaTableNameOrOptions extends | keyof DefaultSchema["Tables"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, TableName extends DefaultSchemaTableNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] + ? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] : never = never, -> = DefaultSchemaTableNameOrOptions extends { schema: keyof Database } - ? Database[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { +> = DefaultSchemaTableNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends { Update: infer U } ? U @@ -445,14 +458,16 @@ export type TablesUpdate< export type Enums< DefaultSchemaEnumNameOrOptions extends | keyof DefaultSchema["Enums"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, EnumName extends DefaultSchemaEnumNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] + ? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"] : never = never, -> = DefaultSchemaEnumNameOrOptions extends { schema: keyof Database } - ? Database[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] +> = DefaultSchemaEnumNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName] : DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"] ? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions] : never @@ -460,14 +475,16 @@ export type Enums< export type CompositeTypes< PublicCompositeTypeNameOrOptions extends | keyof DefaultSchema["CompositeTypes"] - | { schema: keyof Database }, + | { schema: keyof DatabaseWithoutInternals }, CompositeTypeName extends PublicCompositeTypeNameOrOptions extends { - schema: keyof Database + schema: keyof DatabaseWithoutInternals } - ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] + ? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"] : never = never, -> = PublicCompositeTypeNameOrOptions extends { schema: keyof Database } - ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] +> = PublicCompositeTypeNameOrOptions extends { + schema: keyof DatabaseWithoutInternals +} + ? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName] : PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"] ? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions] : never diff --git a/xtablo-expo/.env b/xtablo-expo/.env index d9a93de..e0f39da 100644 --- a/xtablo-expo/.env +++ b/xtablo-expo/.env @@ -1,3 +1,6 @@ EXPO_PUBLIC_STREAM_CHAT_API_KEY="t5vvvddteapa" + EXPO_PUBLIC_SUPABASE_URL=https://mhcafqvzbrrwvahpvvzd.supabase.co -EXPO_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1oY2FmcXZ6YnJyd3ZhaHB2dnpkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDEyNDEzMjEsImV4cCI6MjA1NjgxNzMyMX0.Otxn5BWCPD2ABlMM59hCgeur9Tf_Q7PndAbTkqXDPtM \ No newline at end of file +EXPO_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1oY2FmcXZ6YnJyd3ZhaHB2dnpkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDEyNDEzMjEsImV4cCI6MjA1NjgxNzMyMX0.Otxn5BWCPD2ABlMM59hCgeur9Tf_Q7PndAbTkqXDPtM + +EXPO_PUBLIC_API_URL=http://192.168.1.110:8080 \ No newline at end of file diff --git a/xtablo-expo/.gitignore b/xtablo-expo/.gitignore index c9d575d..7c55d10 100644 --- a/xtablo-expo/.gitignore +++ b/xtablo-expo/.gitignore @@ -36,3 +36,5 @@ yarn-error.* *.tsbuildinfo app-example + +ios \ No newline at end of file diff --git a/xtablo-expo/app.json b/xtablo-expo/app.json index 695e9d6..daca2a0 100644 --- a/xtablo-expo/app.json +++ b/xtablo-expo/app.json @@ -1,21 +1,26 @@ { "expo": { - "name": "xtablo-expo", - "slug": "xtablo-expo", + "name": "xtablo", + "slug": "xtablo", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/images/icon.png", - "scheme": "myapp", + "scheme": "com.xtablo.app", "userInterfaceStyle": "automatic", "newArchEnabled": true, "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "com.xtablo.app", + "infoPlist": { + "ITSAppUsesNonExemptEncryption": false + } }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/images/adaptive-icon.png", "backgroundColor": "#ffffff" - } + }, + "package": "com.xtablo.app" }, "web": { "bundler": "metro", @@ -32,10 +37,17 @@ "resizeMode": "contain", "backgroundColor": "#ffffff" } - ] + ], + "expo-secure-store" ], "experiments": { "typedRoutes": true + }, + "extra": { + "router": {}, + "eas": { + "projectId": "a3acc825-7c18-4cd4-83c7-60836639decc" + } } } } diff --git a/xtablo-expo/app/(auth)/_layout.tsx b/xtablo-expo/app/(auth)/_layout.tsx index bc4f4e7..586987b 100644 --- a/xtablo-expo/app/(auth)/_layout.tsx +++ b/xtablo-expo/app/(auth)/_layout.tsx @@ -1,26 +1,25 @@ -import { Redirect, Slot, Stack } from "expo-router"; +import { Redirect, Slot } from "expo-router"; import { useAuth } from "@/stores/auth"; -import { ActivityIndicator, AppState } from "react-native"; -import { supabase } from "@/lib/supabase"; - -// Tells Supabase Auth to continuously refresh the session automatically if -// the app is in the foreground. When this is added, you will continue to receive -// `onAuthStateChange` events with the `TOKEN_REFRESHED` or `SIGNED_OUT` event -// if the user's session is terminated. This should only be registered once. -AppState.addEventListener("change", (state) => { - if (state === "active") { - supabase.auth.startAutoRefresh(); - } else { - supabase.auth.stopAutoRefresh(); - } -}); +import { ActivityIndicator } from "react-native"; +import { useGetUser } from "@/hooks/user"; +import { useEffect } from "react"; +import { useQueryClient } from "@tanstack/react-query"; export default function AuthLayout() { - const { session, loading } = useAuth(); - if (loading) { + const { loading, initialize } = useAuth(); + const queryClient = useQueryClient(); + const { user, isLoading: isUserLoading } = useGetUser(); + + const isLoading = loading || isUserLoading; + + useEffect(() => { + initialize(queryClient); + }, []); + + if (isLoading) { return ; } - if (session) { + if (user) { return ; } return ; diff --git a/xtablo-expo/app/(auth)/login.tsx b/xtablo-expo/app/(auth)/login.tsx index e1b42ec..776b506 100644 --- a/xtablo-expo/app/(auth)/login.tsx +++ b/xtablo-expo/app/(auth)/login.tsx @@ -1,8 +1,11 @@ import React, { useState } from "react"; -import { StyleSheet, View, Text } from "react-native"; +import { StyleSheet, View, Text, Image } from "react-native"; import { Button, Input } from "@rn-vui/themed"; import { useAuth } from "@/stores/auth"; import { Link } from "expo-router"; +import { Mail, Lock } from "lucide-react-native"; +import { GoogleLoginButton } from "@/components/GoogleLoginButton"; +import { AppleLoginButton } from "@/components/AppleLoginButton"; export default function Auth() { const [email, setEmail] = useState(""); @@ -10,45 +13,58 @@ export default function Auth() { const login = useAuth((state) => state.login); const authLoading = useAuth((state) => state.loading); + const performOAuth = useAuth((state) => state.performOAuth); return ( - Welcome Back! - Sign in to your account - + + Connexion XTablo + Connectez-vous à votre compte + } onChangeText={(text) => setEmail(text)} value={email} - placeholder="email@address.com" + placeholder="jean@dupont.com" autoCapitalize={"none"} /> } onChangeText={(text) => setPassword(text)} value={password} secureTextEntry={true} - placeholder="Password" + placeholder="Mot de passe" autoCapitalize={"none"} />