Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 🎸 Custom UI Boiler Plate Code #121

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1126a89
feat: 🎸 Prompt Choices for UI Build Type
ceroy-ak Oct 7, 2024
ba57497
docs: ✏️ Added docs
ceroy-ak Oct 7, 2024
27c494e
fix: 🐛 argument name and invalid argument bug fix
ceroy-ak Oct 7, 2024
3452efe
fix: 🐛 removed unnecessary promises and added initialanswer
ceroy-ak Oct 7, 2024
a3aefdc
feat: 🎸 React Custom UI
ceroy-ak Oct 8, 2024
7b960b2
fix: 🐛 removed forked github repo link
ceroy-ak Oct 8, 2024
96cd85e
introduction of script for additional runtime changes when needed for…
ceroy-ak Oct 8, 2024
8abf037
changes
ceroy-ak Oct 9, 2024
5e61425
feat: 🎸 Custom React UI Components
ceroy-ak Oct 9, 2024
267a54d
feat: 🎸 Custom UI design for EmailPassword and PasswordLess
ceroy-ak Oct 10, 2024
222891d
feat: 🎸 Custom UI for Thirdparty
ceroy-ak Oct 10, 2024
473df43
fixes
ceroy-ak Oct 10, 2024
26da43d
thirdpartypasswordless UI
ceroy-ak Oct 10, 2024
4810897
Thirdparty Passwordless defunct code removed
ceroy-ak Oct 11, 2024
cc31372
thirdpartypasswordless UI
ceroy-ak Oct 11, 2024
19c7fd6
thirdpartyemailpassword UI
ceroy-ak Oct 11, 2024
e008b0e
fix: 🐛 Moved Scripts to scriptUtils
ceroy-ak Oct 11, 2024
16599c0
thirdpartyemailpassword config
ceroy-ak Oct 11, 2024
7c269e9
thirdpartyemailpassword UI
ceroy-ak Oct 11, 2024
a8a0315
fixes
ceroy-ak Oct 11, 2024
911f2fa
reset package.json file
ceroy-ak Oct 11, 2024
4368abc
removed the setup-scripts folder from js build folder
ceroy-ak Oct 11, 2024
b0c1e97
added ui flag check for frontend prompt when condition
ceroy-ak Oct 11, 2024
36dc67a
fixes
ceroy-ak Oct 11, 2024
16b865b
removed import from build type and session initial state is undefined
ceroy-ak Oct 11, 2024
125aafa
moved the business logic from hooks to utils file
ceroy-ak Oct 11, 2024
40d03a4
moved all utils to components itself to reduce end user cognition
ceroy-ak Oct 12, 2024
d7728da
added loading state for thirdparty
ceroy-ak Oct 12, 2024
6a0f191
fixes
ceroy-ak Oct 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
Copy link
Contributor

@rishabhpoddar rishabhpoddar Oct 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of it looks good. But i would need the following changes before merging:

  • Better styling
  • Removing of "react-custom" id. We should keep frontend as react ID and use the ui type flag in our logic.
  • Thorough testing cause we have changed a lot of the core CLI logic

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?
50 changes: 50 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

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

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ["./tsconfig.node.json", "./tsconfig.app.json"],
tsconfigRootDir: import.meta.dirname,
},
},
});
```

- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:

```js
// eslint.config.js
import react from "eslint-plugin-react";

export default tseslint.config({
// Set the react version
settings: { react: { version: "18.3" } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs["jsx-runtime"].rules,
},
});
```
25 changes: 25 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
},
}
);
13 changes: 13 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Supertokens Demo App | React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
38 changes: 38 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"start": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-loader-spinner": "^6.1.6",
"react-router-dom": "^6.26.2",
"react-toastify": "^10.0.5",
"supertokens-web-js": "^0.13.1"
},
"devDependencies": {
"@eslint/js": "^9.11.1",
"@types/node": "^22.7.5",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.2",
"autoprefixer": "^10.4.20",
"eslint": "^9.11.1",
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
"eslint-plugin-react-refresh": "^0.4.12",
"globals": "^15.9.0",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.13",
"typescript": "^5.5.3",
"typescript-eslint": "^8.7.0",
"vite": "^5.4.8"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Binary file not shown.
Binary file not shown.
33 changes: 33 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import SuperTokens from "supertokens-web-js";
import { superTokensConfig } from "./config";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import HomePage from "@/pages/Home";
import AuthRoutes from "@/pages/Auth";
import DashboardPage from "@/pages/Dashboard";
import Protected from "@/auth/Protected";

SuperTokens.init(superTokensConfig);

const router = createBrowserRouter([
{
path: "/",
element: <HomePage />,
},
{
path: "/dashboard",
element: <Protected />,
children: [
{
index: true,
element: <DashboardPage />,
},
],
},
...AuthRoutes,
]);

function App() {
return <RouterProvider router={router} />;
}

export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import Session from "supertokens-web-js/recipe/session";

export default function Protected() {
const [isSessionStatusLoading, setIsSessionStatusLoading] = useState(true);
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
async function checkSession() {
setIsSessionStatusLoading(true);
try {
const isSessionValid = await Session.doesSessionExist();
if (!isSessionValid) {
navigate(`/authenticate?redirectTo=${location.pathname}`);
}
} catch (error) {
console.error(error);
} finally {
setIsSessionStatusLoading(false);
}
}
checkSession();
}, []);

if (isSessionStatusLoading) {
return <div>Loading...</div>;
}
return (
<div>
<Outlet />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface FooterProps {
title?: string;
}

export default function Footer({ title }: FooterProps) {
return <div>{title && <p className="text-white text-pretty text-sm my-5">{title}</p>}</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
interface HeaderProps {
title?: string;
}
export default function Header({ title }: HeaderProps) {
return (
<div className="flex flex-col items-center justify-center p-5 gap-5">
<img src="/logo.webp" alt="Supertokens Logo" className="w-40" />
{title && <h1 className="text-primary text-3xl">{title}</h1>}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React, { forwardRef } from "react";

type InputProps = React.InputHTMLAttributes<HTMLInputElement>;

const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
return <input ref={ref} {...props} className="min-w-[300px] min-h-12 rounded-md p-3" />;
});

export default Input;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ColorRing } from "react-loader-spinner";

export default function Spinner() {
return (
<div className="flex items-center justify-center h-[100dvh] w-full">
<ColorRing
visible={true}
height="80"
width="80"
ariaLabel="color-ring-loading"
wrapperStyle={{}}
wrapperClass="color-ring-wrapper"
colors={["#e15b64", "#f47e60", "#f8b26a", "#abbd81", "#849b87"]}
/>
</div>
);
}
25 changes: 25 additions & 0 deletions boilerplate/frontend/supertokens-react-custom/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import EmailPassword from "supertokens-web-js/recipe/emailpassword";
import Session from "supertokens-web-js/recipe/session";
import Passwordless from "supertokens-web-js/recipe/passwordless";
import ThirdParty from "supertokens-web-js/recipe/thirdparty";

export function getApiDomain() {
const apiPort = import.meta.env.VITE_APP_API_PORT || 3001;
const apiUrl = import.meta.env.VITE_APP_API_URL || `http://localhost:${apiPort}`;
return apiUrl;
}

export function getWebsiteDomain() {
const websitePort = import.meta.env.VITE_APP_WEBSITE_PORT || 5173;
const websiteUrl = import.meta.env.VITE_APP_WEBSITE_URL || `http://localhost:${websitePort}`;
return websiteUrl;
}

export const superTokensConfig = {
appInfo: {
apiDomain: "http://localhost:3001",
apiBasePath: "/auth",
appName: "Custom UI Demo",
},
recipeList: [Session.init(), EmailPassword.init(), Passwordless.init(), ThirdParty.init()],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import EmailPassword from "supertokens-web-js/recipe/emailpassword";
import Session from "supertokens-web-js/recipe/session";

export function getApiDomain() {
const apiPort = import.meta.env.VITE_APP_API_PORT || 3001;
const apiUrl = import.meta.env.VITE_APP_API_URL || `http://localhost:${apiPort}`;
return apiUrl;
}

export function getWebsiteDomain() {
const websitePort = import.meta.env.VITE_APP_WEBSITE_PORT || 3000;
const websiteUrl = import.meta.env.VITE_APP_WEBSITE_URL || `http://localhost:${websitePort}`;
return websiteUrl;
}

export const superTokensConfig = {
appInfo: {
apiBasePath: "/auth",
appName: "SuperTokens Demo App",
apiDomain: getApiDomain(),
},
recipeList: [Session.init(), EmailPassword.init()],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Session from "supertokens-web-js/recipe/session";
import Passwordless from "supertokens-web-js/recipe/passwordless";

export function getApiDomain() {
const apiPort = import.meta.env.VITE_APP_API_PORT || 3001;
const apiUrl = import.meta.env.VITE_APP_API_URL || `http://localhost:${apiPort}`;
return apiUrl;
}

export function getWebsiteDomain() {
const websitePort = import.meta.env.VITE_APP_WEBSITE_PORT || 3000;
const websiteUrl = import.meta.env.VITE_APP_WEBSITE_URL || `http://localhost:${websitePort}`;
return websiteUrl;
}

export const superTokensConfig = {
appInfo: {
apiBasePath: "/auth",
appName: "SuperTokens Demo App",
apiDomain: getApiDomain(),
},
recipeList: [Session.init(), Passwordless.init()],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Session from "supertokens-web-js/recipe/session";
import ThirdParty from "supertokens-web-js/recipe/thirdparty";

export function getApiDomain() {
const apiPort = import.meta.env.VITE_APP_API_PORT || 3001;
const apiUrl = import.meta.env.VITE_APP_API_URL || `http://localhost:${apiPort}`;
return apiUrl;
}

export function getWebsiteDomain() {
const websitePort = import.meta.env.VITE_APP_WEBSITE_PORT || 3000;
const websiteUrl = import.meta.env.VITE_APP_WEBSITE_URL || `http://localhost:${websitePort}`;
return websiteUrl;
}

export const superTokensConfig = {
appInfo: {
apiBasePath: "/auth",
appName: "SuperTokens Demo App",
apiDomain: getApiDomain(),
},
recipeList: [Session.init(), ThirdParty.init()],
};
Loading