Web app and landing page for CampusPulse, a UPT campus companion (timetable, exams, campus
map, reminders). A student project for the Mobile Systems and Applications course; it is not an
official UPT app. The mobile app lives in mobile
and the backend in api.
- Stack: React 19 · Vite · TypeScript · Tailwind CSS v4 · React Router · i18next
- Languages: English (default) and Romanian; the switch is in the header and remembered on the device
- Package manager: Bun
- Bun (the package manager and script runner)
- Git, and a GitHub account with access to the
CampusPulse-roorg - VS Code (recommended): accept the suggested extensions, Prettier and EditorConfig
(
.vscode/extensions.json), so files are formatted on save the same way CI checks them.
Cloned on Windows before line endings were fixed (repo hygiene, #5)? Your files may have CRLF
endings on disk. Run bun run format, or delete the folder and clone again.
bun install
bun dev # http://localhost:5173bun run build # tsc -b && vite build -> dist/
bun run preview # serve the production build locally
bun run lint # oxlint
bun run format # prettier, rewrites files
bun run format:check # what CI runsCI (.github/workflows/ci.yml) runs lint, the format check and build on every PR and on pushes to main.
| Path | What |
|---|---|
src/main.tsx |
Routes (React Router) |
src/pages/ |
One component per page |
src/components/ |
Shared components |
src/Layout.tsx |
Header and footer around every page |
src/index.css |
Tailwind, page-wide styles, the import of the generated tokens |
design/ |
tokens.json: the design tokens, one source for web and mobile (docs/design-system.md) |
src/design/ |
tokens.css, generated from tokens.json by bun run design:tokens |
src/i18n/ |
every user-facing text: en.json and ro.json (same keys), language setup, dates |
src/api/ |
API client: schema.d.ts generated by bun run api:types from the API's contract |
public/ |
Served as is: favicon, _redirects |
| Path | What |
|---|---|
/ |
landing page (placeholder) |
/app |
the web app (placeholder) |
/privacy |
privacy policy (draft; Google Play needs this URL) |
/account-deletion |
how to delete your account (draft; Google Play needs this URL) |
Cloudflare Pages, project campuspulse-web, by direct upload. It's a static SPA:
public/_redirects sends every path to index.html, so client-side routes work on reload.
| Trigger | Environment | URL | API it talks to |
|---|---|---|---|
| a pull request from this repo | its preview | https://pr-<number>.campuspulse-web.pages.dev (linked in a PR comment) |
stage |
a merge to main |
staging | https://staging.campuspulse-web.pages.dev | stage |
a published, non-prerelease GitHub Release vX.Y.Z cut from main |
production | https://campuspulse.thorsp.net | prod |
- Releasing: see Releasing. A manual run (Actions → Deploy → Run workflow) can
target staging or production, from
mainonly. - Previews:
preview-build.ymlbuilds the PR with no secrets;preview-deploy.ymlthen uploads it frommain's copy of the workflow, so nothing a PR changes runs next to the Cloudflare token. PRs from forks get no preview. - The Cloudflare token is a secret of the
cloudflare-pagesenvironment, usable only frommainandv*tags; only admins can createv*tags and releases. The jobs that hold it check out onlymain's.github/deploy(wrangler, pinned with a lockfile), install it without scripts, and see the token only in the upload step. - API address:
src/config.tsexportsapiBase, read fromVITE_API_BASE, which the workflows set per target. Without it (local development) the app talks to the stage API. - API types:
bun run api:typesdownloads the API's committed contract (https://raw.githubusercontent.com/CampusPulse-ro/api/main/openapi/v1.json) intosrc/api/openapi.jsonand generatessrc/api/schema.d.tsfrom that copy. Run it after the API changes and commit both: builds use the copy, so they're reproducible and a contract change shows as a diff. - A build must name its API:
bun run buildrefuses to run withoutVITE_API_BASE. CI and the workflows set it; locally, putVITE_API_BASE=https://campuspulse-api-stage.thorsp.netin.env.local.bun devwithout it uses the stage API.
A release is what puts the web app on production (https://campuspulse.thorsp.net).
- Tags are
vX.Y.Z(semver). Only admins can createv*tags, and therefore releases (a tag ruleset). - Cut one from
main:Publishing it (not as a pre-release or draft), or later promoting a pre-release to a full release, makesgh release create vX.Y.Z --target main --generate-notes
deploy.ymlbuild that tag against the prod API and deploy it to production. - The API goes first. A production build checks that
src/api/openapi.json(the contract the app was generated from) matches what the prod API serves, and fails with "release the API first" if it doesn't. So a release that needs a new API version waits for that API release. - Release notes are generated from the merged PRs and grouped by label
(
.github/release.yml): Features (enhancement), Fixes (bug), Dependencies (dependenciesor Dependabot), Other changes. Label your PR so it lands in the right group.
CONTRIBUTING.md: the branch → PR → squash-merge workflow and the code standardsdocs/ai-development.md: how to work with AI tools on this repodocs/ai-context.md: the project context to give an AI at the start of a chat