Skip to content

Commit

Permalink
[examples] [docs] Add Vite example with Firebase auth (#4500)
Browse files Browse the repository at this point in the history
Signed-off-by: Bharat Kashyap <[email protected]>
Co-authored-by: Pedro Ferreira <[email protected]>
  • Loading branch information
bharatkashyap and apedroferreira authored Dec 7, 2024
1 parent f3acd18 commit b9315b6
Show file tree
Hide file tree
Showing 21 changed files with 533 additions and 10 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 20 additions & 10 deletions docs/src/modules/components/Examples/core-examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ export default function examples() {
src: '/static/toolpad/docs/core/tutorial-1.png',
href: 'https://mui.com/toolpad/core/introduction/tutorial/',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/tutorial/',
codesandbox: 'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/tutorial',
stackblitz: 'https://stackblitz.com/github/mui/toolpad/tree/master/examples/core/tutorial',
codeSandbox: 'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/tutorial',
stackBlitz: 'https://stackblitz.com/github/mui/toolpad/tree/master/examples/core/tutorial',
},
{
title: 'Auth.js with Next.js App router',
Expand All @@ -17,7 +17,7 @@ export default function examples() {
src: '/static/toolpad/docs/core/auth-next.png',
srcDark: '/static/toolpad/docs/core/auth-next-dark.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs/',
codesandbox:
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-nextjs',
stackblitz: 'https://stackblitz.com/github/mui/toolpad/tree/master/examples/core/auth-nextjs',
},
Expand All @@ -28,7 +28,7 @@ export default function examples() {
src: '/static/toolpad/docs/core/auth-next.png',
srcDark: '/static/toolpad/docs/core/auth-next-dark.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-pages/',
codesandbox:
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-nextjs-pages',
stackblitz:
'https://stackblitz.com/github/mui/toolpad/tree/master/examples/core/auth-nextjs-pages',
Expand All @@ -39,7 +39,7 @@ export default function examples() {
'This app shows you to how to get started using Toolpad Core with Auth.js Magic Links and the Next.js App router',
src: '/static/toolpad/docs/core/auth-next.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-email',
codesandbox:
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-nextjs-email',
},
{
Expand All @@ -48,7 +48,7 @@ export default function examples() {
'This app shows you to how to get started using Toolpad Core with Vite and React Router',
src: '/static/toolpad/docs/core/vite-react-router.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/vite/',
codesandbox: 'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/vite',
codeSandbox: 'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/vite',
stackblitz: 'https://stackblitz.com/github/mui/toolpad/tree/master/examples/core/vite',
},
{
Expand All @@ -59,7 +59,7 @@ export default function examples() {
srcDark: '/static/toolpad/docs/core/auth-next-dark.png',
source:
'https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-pages-nextauth-4/',
codesandbox:
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-nextjs-pages-nextauth-4',
stackblitz:
'https://stackblitz.com/github/mui/toolpad/tree/master/examples/core/auth-nextjs-pages-nextauth-4',
Expand All @@ -71,18 +71,28 @@ export default function examples() {
src: '/static/toolpad/docs/core/auth-next-passkey.png',
srcDark: '/static/toolpad/docs/core/auth-next-passkey-dark.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/auth-nextjs-passkey/',
codesandbox:
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-nextjs-passkey',
},
{
title: 'Vite with React Router and authentication',
title: 'Vite with React Router and mock authentication',
description:
'This app shows you to how to get started using Toolpad Core with Vite, React Router and any external authentication provider',
src: '/static/toolpad/docs/core/vite-react-router.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/auth-vite',
codesandbox:
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/auth-vite',
},
{
title: 'Vite with React Router and Firebase Auth',
description:
'This app shows you to how to get started using Toolpad Core with Vite, React Router, and authentication using Firebase',
src: '/static/toolpad/docs/core/firebase-vite-light.png',
srcDark: '/static/toolpad/docs/core/firebase-vite-dark.png',
source: 'https://github.com/mui/toolpad/tree/master/examples/core/firebase-vite',
codeSandbox:
'https://codesandbox.io/s/github/mui/toolpad/tree/master/examples/core/firebase-vite',
},
{
title: 'Functional Dashboard',
description:
Expand Down
24 changes: 24 additions & 0 deletions examples/core/firebase-vite/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
8 changes: 8 additions & 0 deletions examples/core/firebase-vite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR, and authentication with Firebase.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
20 changes: 20 additions & 0 deletions examples/core/firebase-vite/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="initial-scale=1, width=device-width" />
<!-- Fonts to support Material Design -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
/>
<title>Toolpad Core Vite</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
35 changes: 35 additions & 0 deletions examples/core/firebase-vite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "auth-vite-themed",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite",
"preview": "vite preview"
},
"dependencies": {
"firebase": "^11",
"@emotion/react": "^11",
"@fontsource-variable/inter": "^5.1.0",
"@emotion/styled": "^11",
"@mui/icons-material": "^6",
"@mui/x-charts": "^7",
"@mui/x-tree-view": "^7",
"@mui/x-data-grid": "^7",
"@mui/x-date-pickers": "^7",
"dayjs": "^1",
"clsx": "^2",
"@react-spring/web": "^9",
"@mui/material": "^6",
"@toolpad/core": "latest",
"react": "^18",
"react-dom": "^18",
"react-router-dom": "^6"
},
"devDependencies": {
"@types/react": "^18",
"@types/react-dom": "^18",
"@vitejs/plugin-react": "^4.3.2",
"typescript": "^5",
"vite": "^5.4.8"
}
}
1 change: 1 addition & 0 deletions examples/core/firebase-vite/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions examples/core/firebase-vite/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as React from 'react';
import DashboardIcon from '@mui/icons-material/Dashboard';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
import { Outlet } from 'react-router-dom';
import type { User } from 'firebase/auth';
import { AppProvider } from '@toolpad/core/react-router-dom';
import type { Navigation, Authentication } from '@toolpad/core/AppProvider';
import { firebaseSignOut, signInWithGoogle, onAuthStateChanged } from './firebase/auth';
import SessionContext, { type Session } from './SessionContext';

const NAVIGATION: Navigation = [
{
kind: 'header',
title: 'Main items',
},
{
title: 'Dashboard',
icon: <DashboardIcon />,
},
{
segment: 'orders',
title: 'Orders',
icon: <ShoppingCartIcon />,
},
];

const BRANDING = {
title: 'My Toolpad Core App',
};

const AUTHENTICATION: Authentication = {
signIn: signInWithGoogle,
signOut: firebaseSignOut,
};

export default function App() {
const [session, setSession] = React.useState<Session | null>(null);
const [loading, setLoading] = React.useState(true);

const sessionContextValue = React.useMemo(
() => ({
session,
setSession,
loading,
}),
[session, loading],
);

React.useEffect(() => {
// Returns an `unsubscribe` function to be called during teardown
const unsubscribe = onAuthStateChanged((user: User | null) => {
if (user) {
setSession({
user: {
name: user.displayName || '',
email: user.email || '',
image: user.photoURL || '',
},
});
} else {
setSession(null);
}
setLoading(false);
});

return () => unsubscribe();
}, []);

return (
<AppProvider
navigation={NAVIGATION}
branding={BRANDING}
session={session}
authentication={AUTHENTICATION}
>
<SessionContext.Provider value={sessionContextValue}>
<Outlet />
</SessionContext.Provider>
</AppProvider>
);
}
25 changes: 25 additions & 0 deletions examples/core/firebase-vite/src/SessionContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';

export interface Session {
user: {
name?: string;
email?: string;
image?: string;
};
}

interface SessionContextType {
session: Session | null;
setSession: (session: Session) => void;
loading: boolean;
}

const SessionContext = React.createContext<SessionContextType>({
session: null,
setSession: () => {},
loading: true,
});

export default SessionContext;

export const useSession = () => React.useContext(SessionContext);
Empty file.
92 changes: 92 additions & 0 deletions examples/core/firebase-vite/src/firebase/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
GoogleAuthProvider,
GithubAuthProvider,
signInWithPopup,
setPersistence,
browserSessionPersistence,
signInWithEmailAndPassword,
signOut,
} from 'firebase/auth';
import { firebaseAuth } from './firebaseConfig';

const googleProvider = new GoogleAuthProvider();
const githubProvider = new GithubAuthProvider();

// Sign in with Google functionality
export const signInWithGoogle = async () => {
try {
return setPersistence(firebaseAuth, browserSessionPersistence).then(async () => {
const result = await signInWithPopup(firebaseAuth, googleProvider);
return {
success: true,
user: result.user,
error: null,
};
});
} catch (error: any) {
return {
success: false,
user: null,
error: error.message,
};
}
};

// Sign in with GitHub functionality
export const signInWithGithub = async () => {
try {
return setPersistence(firebaseAuth, browserSessionPersistence).then(async () => {
const result = await signInWithPopup(firebaseAuth, githubProvider);
return {
success: true,
user: result.user,
error: null,
};
});
} catch (error: any) {
return {
success: false,
user: null,
error: error.message,
};
}
};

// Sign in with email and password

export async function signInWithCredentials(email: string, password: string) {
try {
return setPersistence(firebaseAuth, browserSessionPersistence).then(async () => {
const userCredential = await signInWithEmailAndPassword(firebaseAuth, email, password);
return {
success: true,
user: userCredential.user,
error: null,
};
});
} catch (error: any) {
return {
success: false,
user: null,
error: error.message || 'Failed to sign in with email/password',
};
}
}

// Sign out functionality
export const firebaseSignOut = async () => {
try {
await signOut(firebaseAuth);
return { success: true };
} catch (error: any) {
return {
success: false,
error: error.message,
};
}
};

// Auth state observer
export const onAuthStateChanged = (callback: (user: any) => void) => {
return firebaseAuth.onAuthStateChanged(callback);
};
14 changes: 14 additions & 0 deletions examples/core/firebase-vite/src/firebase/firebaseConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const app = initializeApp({
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGE_SENDER_ID,
appId: import.meta.env.VITE_FIREBASE_APP_ID,
});

export const firebaseAuth = getAuth(app);
export default app;
Loading

0 comments on commit b9315b6

Please sign in to comment.