Skip to content

Commit 02edeb6

Browse files
authored
Merge pull request #420 from openscript-ch/204-configurable-theme
feat: make theme color configurable
2 parents dff0f77 + 220905b commit 02edeb6

33 files changed

+430
-362
lines changed

.changeset/new-berries-cry.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@quassel/frontend": minor
3+
"@quassel/ui": minor
4+
---
5+
6+
Make frontend themeable

.devcontainer/devcontainer.json

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
]
1717
}
1818
},
19+
"forwardPorts": [
20+
3000,
21+
3001,
22+
3002,
23+
3003
24+
],
1925
"features": {
2026
// "ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
2127
}

.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ web_modules/
140140
.yarn-integrity
141141

142142
# dotenv environment variable files
143-
.env
144143
.env.development.local
145144
.env.test.local
146145
.env.production.local

apps/backend/README.md

-85
Original file line numberDiff line numberDiff line change
@@ -1,85 +0,0 @@
1-
<p align="center">
2-
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
3-
</p>
4-
5-
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
6-
[circleci-url]: https://circleci.com/gh/nestjs/nest
7-
8-
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
9-
<p align="center">
10-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
11-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
12-
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
13-
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
14-
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
15-
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
16-
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
17-
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
18-
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
19-
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
20-
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
21-
</p>
22-
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
23-
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
24-
25-
## Description
26-
27-
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
28-
29-
## Project setup
30-
31-
```bash
32-
$ pnpm install
33-
```
34-
35-
## Compile and run the project
36-
37-
```bash
38-
# development
39-
$ pnpm run start
40-
41-
# watch mode
42-
$ pnpm run start:dev
43-
44-
# production mode
45-
$ pnpm run start:prod
46-
```
47-
48-
## Run tests
49-
50-
```bash
51-
# unit tests
52-
$ pnpm run test
53-
54-
# e2e tests
55-
$ pnpm run test:e2e
56-
57-
# test coverage
58-
$ pnpm run test:cov
59-
```
60-
61-
## Resources
62-
63-
Check out a few resources that may come in handy when working with NestJS:
64-
65-
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
66-
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
67-
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
68-
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
69-
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
70-
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
71-
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
72-
73-
## Support
74-
75-
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
76-
77-
## Stay in touch
78-
79-
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
80-
- Website - [https://nestjs.com](https://nestjs.com/)
81-
- Twitter - [@nestframework](https://twitter.com/nestframework)
82-
83-
## License
84-
85-
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).

apps/frontend/.docker/entrypoint.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ set -xe
33

44
: "${API_URL?Define the api endpoint}"
55

6-
sed -i "s|//REPLACE_WITH_API_URL|$API_URL|g" /usr/share/nginx/html/index.html
6+
envsubst < /usr/share/nginx/html/index.template.html > /usr/share/nginx/html/index.html
7+
8+
if [ -d /usr/share/nginx/html/icons ]; then
9+
cp /usr/share/nginx/html/icons/* /usr/share/nginx/html
10+
fi
711

812
exec "$@"

apps/frontend/.env

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
VITE_API_URL=http://localhost:3000
2+
VITE_THEME_COLOR=quassel
3+
VITE_TITLE=Quassel
4+
VITE_LOGOS='[{"path": "/logos/uzh-logo.svg", "alt": "University of Zurich Logo", "url": "https://uzh.ch"}, {"path": "/logos/weltentdecker-logo.png", "alt": "Kleine Weltentdecker Logo", "url": "https://www.psychologie.uzh.ch/de/bereiche/dev/devpsy/Weltentdecker.html"}]'

apps/frontend/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
FROM docker.io/nginx:stable-alpine
22

33
COPY dist/ /usr/share/nginx/html/
4+
RUN cp /usr/share/nginx/html/index.html /usr/share/nginx/html/index.template.html
45
COPY ./.docker/nginx.conf /etc/nginx/conf.d/default.conf
56
COPY ./.docker/entrypoint.sh /
67

apps/frontend/README.md

-50
Original file line numberDiff line numberDiff line change
@@ -1,50 +0,0 @@
1-
# React + TypeScript + Vite
2-
3-
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4-
5-
Currently, two official plugins are available:
6-
7-
- [@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
8-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9-
10-
## Expanding the ESLint configuration
11-
12-
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
13-
14-
- Configure the top-level `parserOptions` property like this:
15-
16-
```js
17-
export default tseslint.config({
18-
languageOptions: {
19-
// other options...
20-
parserOptions: {
21-
project: ['./tsconfig.node.json', './tsconfig.app.json'],
22-
tsconfigRootDir: import.meta.dirname,
23-
},
24-
},
25-
})
26-
```
27-
28-
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
29-
- Optionally add `...tseslint.configs.stylisticTypeChecked`
30-
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
31-
32-
```js
33-
// eslint.config.js
34-
import react from 'eslint-plugin-react'
35-
36-
export default tseslint.config({
37-
// Set the react version
38-
settings: { react: { version: '18.3' } },
39-
plugins: {
40-
// Add the react plugin
41-
react,
42-
},
43-
rules: {
44-
// other rules...
45-
// Enable its recommended rules
46-
...react.configs.recommended.rules,
47-
...react.configs['jsx-runtime'].rules,
48-
},
49-
})
50-
```

apps/frontend/index.html

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<head>
55
<meta charset="UTF-8" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>LEMON</title>
7+
<title>$TITLE</title>
88
<style>
99
html {
1010
height: 100%;
@@ -22,7 +22,10 @@
2222
</style>
2323
<script type="text/javascript">
2424
window.env = {
25-
apiUrl: '//REPLACE_WITH_API_URL'
25+
apiUrl: '$API_URL',
26+
themeColor: '$THEME_COLOR',
27+
title: '$TITLE',
28+
logos: '$LOGOS',
2629
};
2730
</script>
2831
</head>

apps/frontend/manifest.json

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

apps/frontend/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"coverage": "vitest --coverage",
1414
"dev": "vite --host",
1515
"preview": "vite preview",
16-
"test": "vitest"
16+
"test": "vitest",
17+
"generate:icons": "pwa-asset-generator ./public/logo.svg ./public --type png --opaque false --favicon --maskable --manifest ./manifest.json --index ./index.html"
1718
},
1819
"dependencies": {
1920
"@fullcalendar/core": "^6.1.15",
@@ -35,6 +36,7 @@
3536
},
3637
"devDependencies": {
3738
"@anolilab/unplugin-favicons": "^1.0.5",
39+
"@sinclair/typebox": "^0.34.33",
3840
"@tanstack/router-devtools": "^1.114.31",
3941
"@tanstack/router-plugin": "^1.114.31",
4042
"@testing-library/jest-dom": "^6.6.3",

apps/frontend/public/logo.svg

+91
Loading

apps/frontend/src/configuration.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
function getEnv(key: keyof Exclude<typeof window.env, undefined>) {
22
if (window.env === undefined) return;
3-
if (window.env[key]?.startsWith("//")) return;
3+
if (window.env[key]?.startsWith("$")) return;
44

55
return window.env[key];
66
}
77

88
export const C = {
99
env: {
10-
apiUrl: getEnv("apiUrl") || "http://localhost:3000",
10+
apiUrl: getEnv("apiUrl") || import.meta.env.VITE_API_URL,
11+
themeColor: getEnv("themeColor") || import.meta.env.VITE_THEME_COLOR,
12+
title: getEnv("title") || import.meta.env.VITE_TITLE,
13+
logos: getEnv("logos") || import.meta.env.VITE_LOGOS,
1114
},
1215
ui: {
1316
maxDropdownHeight: 800,

apps/frontend/src/main.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import React, { StrictMode } from "react";
22
import { createRoot } from "react-dom/client";
33
import "@quassel/ui/style.css";
4-
import { ThemeProvider } from "@quassel/ui";
4+
import { ThemeProvider, defaultTheme, mergeThemeOverrides } from "@quassel/ui";
55
import { RouterProvider, createRouter } from "@tanstack/react-router";
66
import { routeTree } from "./routeTree.gen.ts";
77
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
8+
import { C } from "./configuration.ts";
89

910
const queryClient = new QueryClient();
1011
const router = createRouter({ routeTree, context: { queryClient }, defaultPreload: "intent", defaultPreloadStaleTime: 0 });
@@ -23,10 +24,13 @@ declare module "@tanstack/react-router" {
2324
}
2425
}
2526

27+
const currentTheme = mergeThemeOverrides(defaultTheme, { primaryColor: C.env.themeColor });
28+
if (import.meta.env.DEV) document.title = C.env.title;
29+
2630
createRoot(document.getElementById("root")!).render(
2731
<StrictMode>
2832
<QueryClientProvider client={queryClient}>
29-
<ThemeProvider>
33+
<ThemeProvider theme={currentTheme}>
3034
<RouterProvider router={router} notFoundMode="root" />
3135
<TanStackRouterDevtools router={router} position="bottom-right" />
3236
</ThemeProvider>

0 commit comments

Comments
 (0)