Skip to content

Commit 31241ee

Browse files
committed
Refactor web app structure by updating Dockerfile for improved build process, adding server-side rendering support, and enhancing README with deployment instructions. Replace unused routes and components, and introduce new welcome page with logos. Update package dependencies and configurations for better organization.
1 parent bc2131a commit 31241ee

30 files changed

+502
-942
lines changed

apps/web/.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.react-router
2+
build
3+
node_modules
4+
README.md

apps/web/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
/node_modules/
3+
*.tsbuildinfo
34

45
# React Router
56
/.react-router/

apps/web/Dockerfile

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,22 @@
1-
FROM node:18-alpine AS base
2-
3-
RUN npm install -g pnpm
1+
FROM node:20-alpine AS development-dependencies-env
2+
COPY . /app
43
WORKDIR /app
4+
RUN npm ci
55

6-
# Copy workspace configuration
7-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
8-
9-
# Copy all package.json files for proper dependency resolution
10-
COPY apps/cms/package.json ./apps/cms/
11-
COPY apps/web/package.json ./apps/web/
12-
COPY packages/ui/package.json ./packages/ui/
13-
14-
# Install all dependencies
15-
RUN pnpm install --frozen-lockfile
16-
17-
# Copy source code
18-
COPY apps/web ./apps/web
19-
COPY packages/ui ./packages/ui
20-
21-
# Build UI package first
22-
RUN pnpm --filter @acme/ui build
23-
24-
# Build web app
25-
RUN pnpm --filter web build
26-
27-
# Production stage
28-
FROM node:18-alpine AS production
29-
30-
RUN npm install -g pnpm
6+
FROM node:20-alpine AS production-dependencies-env
7+
COPY ./package.json package-lock.json /app/
318
WORKDIR /app
9+
RUN npm ci --omit=dev
3210

33-
# Copy workspace configuration
34-
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
35-
36-
# Copy all package.json files
37-
COPY apps/cms/package.json ./apps/cms/
38-
COPY apps/web/package.json ./apps/web/
39-
COPY packages/ui/package.json ./packages/ui/
40-
41-
# Install production dependencies only
42-
RUN pnpm install --frozen-lockfile --prod
43-
44-
# Copy built application from base stage
45-
COPY --from=base /app/apps/web/build ./apps/web/build
46-
COPY --from=base /app/apps/web/package.json ./apps/web/
11+
FROM node:20-alpine AS build-env
12+
COPY . /app/
13+
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
14+
WORKDIR /app
15+
RUN npm run build
4716

48-
EXPOSE 3000
49-
ENV NODE_ENV=production
50-
ENV PORT=3000
51-
CMD ["pnpm", "start", "--filter=web"]
17+
FROM node:20-alpine
18+
COPY ./package.json package-lock.json server.js /app/
19+
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
20+
COPY --from=build-env /app/build /app/build
21+
WORKDIR /app
22+
CMD ["npm", "run", "start"]

apps/web/README.md

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# Welcome to React Router!
22

3-
A template for experimenting with React Router v7.
3+
A modern, production-ready template for building full-stack React applications using React Router.
44

5-
> ![NOTE]
6-
> This template should not be used for production apps and is intended more for experimentation and demo applications. Please see the [default](https://github.com/remix-run/react-router-templates/tree/main/default) template for a more full-featured template.
5+
## Features
6+
7+
- 🚀 Server-side rendering
8+
- ⚡️ Hot Module Replacement (HMR)
9+
- 📦 Asset bundling and optimization
10+
- 🔄 Data loading and mutations
11+
- 🔒 TypeScript by default
12+
- 🎉 TailwindCSS for styling
13+
- 📖 [React Router docs](https://reactrouter.com/)
714

815
## Getting Started
916

@@ -12,15 +19,55 @@ A template for experimenting with React Router v7.
1219
Install the dependencies:
1320

1421
```bash
15-
pnpm install
22+
npm install
1623
```
1724

1825
### Development
1926

2027
Start the development server with HMR:
2128

2229
```bash
23-
pnpm run dev
30+
npm run dev
2431
```
2532

2633
Your application will be available at `http://localhost:3000`.
34+
35+
## Building for Production
36+
37+
Create a production build:
38+
39+
```bash
40+
npm run build
41+
```
42+
43+
## Deployment
44+
45+
### Docker Deployment
46+
47+
To build and run using Docker:
48+
49+
```bash
50+
docker build -t my-app .
51+
52+
# Run the container
53+
docker run -p 3000:3000 my-app
54+
```
55+
56+
### DIY Deployment
57+
58+
If you're familiar with deploying Node applications, the built-in app server is production-ready.
59+
60+
Make sure to deploy the output of `npm run build`
61+
62+
```
63+
├── package.json
64+
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb)
65+
├── server.js
66+
├── build/
67+
│ ├── client/ # Static assets
68+
│ └── server/ # Server-side code
69+
```
70+
71+
## Styling
72+
73+
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.

apps/web/app/app.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@import "tailwindcss";
2+
3+
@theme {
4+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
5+
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
6+
}
7+
8+
html,
9+
body {
10+
@apply bg-white dark:bg-gray-950;
11+
12+
@media (prefers-color-scheme: dark) {
13+
color-scheme: dark;
14+
}
15+
}

apps/web/app/lib/payloadClient.ts

Lines changed: 0 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -49,65 +49,6 @@ export interface Post {
4949
updatedAt: string;
5050
}
5151

52-
export interface Book {
53-
id: string;
54-
title: string;
55-
slug: string;
56-
description?: any;
57-
coverImage?: {
58-
id: string;
59-
url: string;
60-
alt?: string;
61-
};
62-
author: string;
63-
isbn?: string;
64-
publishedDate?: string;
65-
pages?: number;
66-
status: "draft" | "published";
67-
chapters?: Array<{
68-
id: string;
69-
title: string;
70-
slug: string;
71-
chapterNumber: number;
72-
}>;
73-
seo?: {
74-
title?: string;
75-
description?: string;
76-
keywords?: string;
77-
image?: {
78-
id: string;
79-
url: string;
80-
};
81-
};
82-
createdAt: string;
83-
updatedAt: string;
84-
}
85-
86-
export interface Chapter {
87-
id: string;
88-
title: string;
89-
slug: string;
90-
content: any;
91-
book: {
92-
id: string;
93-
title: string;
94-
slug: string;
95-
};
96-
chapterNumber: number;
97-
status: "draft" | "published";
98-
seo?: {
99-
title?: string;
100-
description?: string;
101-
keywords?: string;
102-
image?: {
103-
id: string;
104-
url: string;
105-
};
106-
};
107-
createdAt: string;
108-
updatedAt: string;
109-
}
110-
11152
export interface Tag {
11253
id: string;
11354
name: string;
@@ -201,58 +142,6 @@ class PayloadClient {
201142
return response.docs[0];
202143
}
203144

204-
// Books
205-
async getBooks(options?: {
206-
limit?: number;
207-
page?: number;
208-
where?: any;
209-
sort?: string;
210-
draft?: boolean;
211-
}): Promise<PayloadResponse<Book>> {
212-
const params = new URLSearchParams();
213-
214-
if (options?.limit) params.set("limit", options.limit.toString());
215-
if (options?.page) params.set("page", options.page.toString());
216-
if (options?.sort) params.set("sort", options.sort);
217-
if (options?.draft) params.set("draft", "true");
218-
if (options?.where) params.set("where", JSON.stringify(options.where));
219-
220-
return this.fetch<PayloadResponse<Book>>(`/books?${params.toString()}`);
221-
}
222-
223-
async getBook(slug: string, draft = false): Promise<Book> {
224-
const params = new URLSearchParams();
225-
if (draft) params.set("draft", "true");
226-
227-
const response = await this.fetch<PayloadResponse<Book>>(
228-
`/books?where[slug][equals]=${slug}&${params.toString()}`
229-
);
230-
if (response.docs.length === 0) {
231-
throw new Error(`Book with slug "${slug}" not found`);
232-
}
233-
return response.docs[0];
234-
}
235-
236-
// Chapters
237-
async getChapter(
238-
bookSlug: string,
239-
chapterSlug: string,
240-
draft = false
241-
): Promise<Chapter> {
242-
const params = new URLSearchParams();
243-
if (draft) params.set("draft", "true");
244-
245-
const response = await this.fetch<PayloadResponse<Chapter>>(
246-
`/chapters?where[and][0][book.slug][equals]=${bookSlug}&where[and][1][slug][equals]=${chapterSlug}&${params.toString()}`
247-
);
248-
if (response.docs.length === 0) {
249-
throw new Error(
250-
`Chapter "${chapterSlug}" in book "${bookSlug}" not found`
251-
);
252-
}
253-
return response.docs[0];
254-
}
255-
256145
// Tags
257146
async getTags(): Promise<PayloadResponse<Tag>> {
258147
return this.fetch<PayloadResponse<Tag>>("/tags");

apps/web/app/lib/seo.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Book, Chapter, Post, SiteSettings } from "./payloadClient";
1+
import type { Post, SiteSettings } from "./payloadClient";
22

33
export interface SEOData {
44
title: string;
@@ -10,9 +10,9 @@ export interface SEOData {
1010
}
1111

1212
export function generateSEO(
13-
data: Post | Book | Chapter | SiteSettings,
13+
data: Post | SiteSettings,
1414
siteSettings: SiteSettings,
15-
type: "post" | "book" | "chapter" | "home" = "home"
15+
type: "post" | "home" = "home"
1616
): SEOData {
1717
const baseTitle = siteSettings.title;
1818
const baseDescription = siteSettings.description;
@@ -28,20 +28,6 @@ export function generateSEO(
2828
description = post.seo?.description || post.excerpt || baseDescription;
2929
keywords = post.seo?.keywords;
3030
image = post.seo?.image?.url || post.featuredImage?.url;
31-
} else if (type === "book" && "title" in data) {
32-
const book = data as Book;
33-
title = book.seo?.title || `${book.title} | ${baseTitle}`;
34-
description = book.seo?.description || baseDescription;
35-
keywords = book.seo?.keywords;
36-
image = book.seo?.image?.url || book.coverImage?.url;
37-
} else if (type === "chapter" && "title" in data) {
38-
const chapter = data as Chapter;
39-
title =
40-
chapter.seo?.title ||
41-
`${chapter.title} | ${chapter.book.title} | ${baseTitle}`;
42-
description = chapter.seo?.description || baseDescription;
43-
keywords = chapter.seo?.keywords;
44-
image = chapter.seo?.image?.url;
4531
}
4632

4733
return {

apps/web/app/root.css

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)