Skip to content

Commit af73d30

Browse files
committed
first commit
0 parents  commit af73d30

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+13966
-0
lines changed

Diff for: .DS_Store

6 KB
Binary file not shown.

Diff for: frontend/.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
BASE_URL_BACKEND=YOUR_BACKEND_URL

Diff for: frontend/.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
3+
/.cache
4+
/build
5+
.env

Diff for: frontend/.prettierrc

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"prettier.prettierPath": "./node_modules/prettier",
3+
"editor.codeActionsOnSave": {
4+
"source.fixAll.eslint": "always"
5+
},
6+
"editor.formatOnSave": true,
7+
"eslint.validate": [
8+
"javascript",
9+
"javascriptreact",
10+
"typescript",
11+
"typescriptreact"
12+
],
13+
"editor.defaultFormatter": "esbenp.prettier-vscode"
14+
}

Diff for: frontend/.vscode/settings.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"prettier.prettierPath": "./node_modules/prettier",
3+
"editor.formatOnSave": true,
4+
"editor.defaultFormatter": "esbenp.prettier-vscode",
5+
"prettier.printWidth": 100
6+
}

Diff for: frontend/README.md

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Remix & HeroUI Template
2+
3+
This is a template for creating applications using Next.js 14 (app directory) and HeroUI (v2).
4+
5+
## Technologies Used
6+
7+
- [Remix 2](https://remix.run/docs/en/main/start/quickstart)
8+
- [HeroUI v2](https://heroui.com/)
9+
- [Tailwind CSS](https://tailwindcss.com/)
10+
- [Tailwind Variants](https://tailwind-variants.org)
11+
- [TypeScript](https://www.typescriptlang.org/)
12+
- [Framer Motion](https://www.framer.com/motion/)
13+
14+
## How to Use
15+
16+
### Use the template with create-remix
17+
18+
To create a new project based on this template using `create-remix`, run the following command:
19+
20+
```bash
21+
npx create-next-app -e https://github.com/frontio-ai/remix-template.git
22+
```
23+
24+
### Install dependencies
25+
26+
You can use one of them `npm`, `yarn`, `pnpm`, `bun`, Example using `npm`:
27+
28+
```bash
29+
npm install
30+
```
31+
32+
### Run the development server
33+
34+
```bash
35+
npm run dev
36+
```
37+
38+
### Setup pnpm (optional)
39+
40+
If you are using `pnpm`, you need to add the following code to your `.npmrc` file:
41+
42+
```bash
43+
public-hoist-pattern[]=*@heroui/*
44+
```
45+
46+
After modifying the `.npmrc` file, you need to run `pnpm install` again to ensure that the dependencies are installed correctly.

Diff for: frontend/app/contexts/AuthContext.tsx

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import React, { createContext, useContext, useEffect, useState } from "react";
2+
import { useNavigate, useLocation } from "@remix-run/react";
3+
import { AuthAPI } from "../../utils/services/api/AuthApi";
4+
import { useAuthStore } from "../stores/authStore";
5+
import AxiosCallApi from "~/utils/services/axios";
6+
import { toastMsg } from "~/utils/toasts";
7+
8+
// Define a simple context type; you can extend it if needed
9+
interface AuthContextProps {
10+
loading: boolean;
11+
}
12+
13+
const AuthContext = createContext<AuthContextProps | undefined>(undefined);
14+
15+
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
16+
children,
17+
}) => {
18+
// Use type annotation for useState<boolean>
19+
const [loading, setLoading] = useState<boolean>(true);
20+
const setAuthenticated = useAuthStore((state) => state.setAuthenticated);
21+
const setAccessToken = useAuthStore((state) => state.setAccessToken);
22+
const setUserData = useAuthStore((state) => state.setUserData);
23+
const userData = useAuthStore((state) => state.userData);
24+
const userId = useAuthStore((state) => state.userData?.id);
25+
const navigate = useNavigate();
26+
const location = useLocation();
27+
28+
useEffect(() => {
29+
// On page load, check for accessToken and a stored userId
30+
const token = localStorage.getItem("accessToken");
31+
32+
console.log("location", location);
33+
34+
console.log("useEffect token:", token?.length);
35+
36+
if (userId) {
37+
console.log("userId", userId);
38+
if (location.pathname === "/login") {
39+
navigate("/app");
40+
}
41+
return;
42+
}
43+
44+
if (token) {
45+
console.log("no userData found and token found");
46+
AuthAPI.signIn(token)
47+
.then((response) => {
48+
setAuthenticated(true);
49+
setAccessToken(response.accessToken);
50+
setLoading(false);
51+
AxiosCallApi.saveToken(response.accessToken);
52+
setUserData(response.userData);
53+
})
54+
.catch((error) => {
55+
console.error(error);
56+
toastMsg.error(
57+
"Erreur lors de la connexion, veuillez réessayer ou contacter l'assistance",
58+
);
59+
// If the signIn call fails, clear storage and redirect to /login.
60+
localStorage.removeItem("accessToken");
61+
localStorage.removeItem("userId");
62+
setAuthenticated(false);
63+
setLoading(false);
64+
if (location.pathname !== "/login") {
65+
navigate("/login");
66+
}
67+
});
68+
} else {
69+
console.log("no token");
70+
setLoading(false);
71+
// If no token exists and the user is not on the login page, redirect
72+
if (location.pathname !== "/login") {
73+
navigate("/login");
74+
}
75+
}
76+
}, [location.pathname, navigate, setAuthenticated, setAccessToken]);
77+
78+
return (
79+
<AuthContext.Provider value={{ loading }}>
80+
{loading ? <div>Loading...</div> : children}
81+
</AuthContext.Provider>
82+
);
83+
};
84+
85+
export function useAuth() {
86+
const context = useContext(AuthContext);
87+
if (!context) {
88+
throw new Error(
89+
"useAuth doit être utilisé à l’intérieur d’un AuthProvider",
90+
);
91+
}
92+
return context;
93+
}

Diff for: frontend/app/entry.client.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* By default, Remix will handle hydrating your app on the client for you.
3+
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4+
* For more information, see https://remix.run/file-conventions/entry.client
5+
*/
6+
7+
import { RemixBrowser } from "@remix-run/react";
8+
import { startTransition, StrictMode } from "react";
9+
import { hydrateRoot } from "react-dom/client";
10+
11+
import process from "process";
12+
window.process = process;
13+
14+
startTransition(() => {
15+
hydrateRoot(
16+
document,
17+
<StrictMode>
18+
<RemixBrowser />
19+
</StrictMode>
20+
);
21+
});

Diff for: frontend/app/entry.server.tsx

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/**
2+
* By default, Remix will handle generating the HTTP Response for you.
3+
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
4+
* For more information, see https://remix.run/file-conventions/entry.server
5+
*/
6+
7+
import { PassThrough } from "node:stream";
8+
9+
import type { AppLoadContext, EntryContext } from "@remix-run/node";
10+
import { createReadableStreamFromReadable } from "@remix-run/node";
11+
import { RemixServer } from "@remix-run/react";
12+
import { isbot } from "isbot";
13+
import { renderToPipeableStream } from "react-dom/server";
14+
15+
const ABORT_DELAY = 5_000;
16+
17+
export default function handleRequest(
18+
request: Request,
19+
responseStatusCode: number,
20+
responseHeaders: Headers,
21+
remixContext: EntryContext,
22+
// This is ignored so we can keep it in the template for visibility. Feel
23+
// free to delete this parameter in your app if you're not using it!
24+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
25+
loadContext: AppLoadContext
26+
) {
27+
return isbot(request.headers.get("user-agent") || "")
28+
? handleBotRequest(
29+
request,
30+
responseStatusCode,
31+
responseHeaders,
32+
remixContext
33+
)
34+
: handleBrowserRequest(
35+
request,
36+
responseStatusCode,
37+
responseHeaders,
38+
remixContext
39+
);
40+
}
41+
42+
function handleBotRequest(
43+
request: Request,
44+
responseStatusCode: number,
45+
responseHeaders: Headers,
46+
remixContext: EntryContext
47+
) {
48+
return new Promise((resolve, reject) => {
49+
let shellRendered = false;
50+
const { pipe, abort } = renderToPipeableStream(
51+
<RemixServer
52+
context={remixContext}
53+
url={request.url}
54+
abortDelay={ABORT_DELAY}
55+
/>,
56+
{
57+
onAllReady() {
58+
shellRendered = true;
59+
const body = new PassThrough();
60+
const stream = createReadableStreamFromReadable(body);
61+
62+
responseHeaders.set("Content-Type", "text/html");
63+
64+
resolve(
65+
new Response(stream, {
66+
headers: responseHeaders,
67+
status: responseStatusCode,
68+
})
69+
);
70+
71+
pipe(body);
72+
},
73+
onShellError(error: unknown) {
74+
reject(error);
75+
},
76+
onError(error: unknown) {
77+
responseStatusCode = 500;
78+
// Log streaming rendering errors from inside the shell. Don't log
79+
// errors encountered during initial shell rendering since they'll
80+
// reject and get logged in handleDocumentRequest.
81+
if (shellRendered) {
82+
console.error(error);
83+
}
84+
},
85+
}
86+
);
87+
88+
setTimeout(abort, ABORT_DELAY);
89+
});
90+
}
91+
92+
function handleBrowserRequest(
93+
request: Request,
94+
responseStatusCode: number,
95+
responseHeaders: Headers,
96+
remixContext: EntryContext
97+
) {
98+
return new Promise((resolve, reject) => {
99+
let shellRendered = false;
100+
const { pipe, abort } = renderToPipeableStream(
101+
<RemixServer
102+
context={remixContext}
103+
url={request.url}
104+
abortDelay={ABORT_DELAY}
105+
/>,
106+
{
107+
onShellReady() {
108+
shellRendered = true;
109+
const body = new PassThrough();
110+
const stream = createReadableStreamFromReadable(body);
111+
112+
responseHeaders.set("Content-Type", "text/html");
113+
114+
resolve(
115+
new Response(stream, {
116+
headers: responseHeaders,
117+
status: responseStatusCode,
118+
})
119+
);
120+
121+
pipe(body);
122+
},
123+
onShellError(error: unknown) {
124+
reject(error);
125+
},
126+
onError(error: unknown) {
127+
responseStatusCode = 500;
128+
// Log streaming rendering errors from inside the shell. Don't log
129+
// errors encountered during initial shell rendering since they'll
130+
// reject and get logged in handleDocumentRequest.
131+
if (shellRendered) {
132+
console.error(error);
133+
}
134+
},
135+
}
136+
);
137+
138+
setTimeout(abort, ABORT_DELAY);
139+
});
140+
}

Diff for: frontend/app/root.tsx

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { LinksFunction } from "@remix-run/node";
2+
import {
3+
Links,
4+
Meta,
5+
Outlet,
6+
Scripts,
7+
ScrollRestoration,
8+
} from "@remix-run/react";
9+
import { AuthProvider } from "./contexts/AuthContext";
10+
import { NuqsAdapter } from "nuqs/adapters/remix";
11+
12+
import "./tailwind.css";
13+
14+
export const links: LinksFunction = () => [
15+
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
16+
{
17+
rel: "preconnect",
18+
href: "https://fonts.gstatic.com",
19+
crossOrigin: "anonymous",
20+
},
21+
{
22+
rel: "stylesheet",
23+
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
24+
},
25+
];
26+
27+
export function Layout({ children }: { children: React.ReactNode }) {
28+
return (
29+
<html lang="en">
30+
<head>
31+
<meta charSet="utf-8" />
32+
<meta name="viewport" content="width=device-width, initial-scale=1" />
33+
<Meta />
34+
<Links />
35+
</head>
36+
<body>
37+
{children}
38+
<ScrollRestoration />
39+
<Scripts />
40+
</body>
41+
</html>
42+
);
43+
}
44+
45+
export default function App() {
46+
return (
47+
<AuthProvider>
48+
<NuqsAdapter>
49+
<Outlet />
50+
</NuqsAdapter>
51+
</AuthProvider>
52+
);
53+
}

0 commit comments

Comments
 (0)