diff --git a/.github/workflows/production_check.yml b/.github/workflows/production_check.yml index 4e8bdca6..2e03a015 100644 --- a/.github/workflows/production_check.yml +++ b/.github/workflows/production_check.yml @@ -1,21 +1,25 @@ name: Production test on: - pull_request: # run only on created pull request on main - branches: + pull_request: # run on pull requests into the deployed branch (dev) and main + branches: + - dev - main - + jobs: build: - name: run lint and tests + name: run lint and production build runs-on: ubuntu-latest steps: - - name: check out code - uses: actions/checkout@v2 + - name: check out code + uses: actions/checkout@v4 - name: setup node - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm - name: install dependencies - run: npm install + run: npm ci - name: running lint run: npm run lint - - name: run eslint - run: npm run alltests \ No newline at end of file + - name: production build + run: npm run build diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 00000000..8a037897 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,80 @@ +# Deployment & Testing + +## Where the site is hosted + +| Domain | Points to | What it serves | +| ---------------------------------------------- | ----------------------------- | -------------------------------------------------------------- | +| [quantsoc.org](https://quantsoc.org) | `dev.website-dk1.pages.dev` | **The live site** - the latest build of the `dev` branch | +| [staging.quantsoc.org](https://staging.quantsoc.org) | `dev.website-dk1.pages.dev` | The same as quantsoc.org - **not** a separate staging site | +| [dev.quantsoc.org](https://dev.quantsoc.org) | `website-dk1.pages.dev` | An old, outdated build (Cloudflare's "production" deployment) | +| [mtg.quantsoc.org](https://mtg.quantsoc.org) | | The Mock Trading Game - a separate app, not in this repo | + +The live site at quantsoc.org is a build (`npm run build`) of the **`dev` branch**. The `main` branch is 100s of commits out of date and is not what's live. + +> ⚠️ **Pushing or merging to `dev` deploys straight to the live site.** + +**Firebase Hosting:** The repo uses Firebase only for the Firestore database. Do **not** run `firebase init` or `firebase deploy`, at best it does nothing, at worst it creates a second, copy of the site. + +### Cloudflare Pages setup + +Found in the Cloudflare dashboard -> Compute -> Workers & Pages -> the project -> Settings. + +| Setting | Value | +| ---------------------- | ----------------------------------------------- | +| Project name | `quantsoc-dev` (to confirm, see below) | +| Pages address | `website-dk1.pages.dev` | +| Automatic deployments | Enabled (every push to GitHub triggers a build) | +| Production branch | `main` (see below) | +| Build command | `npm run build` | +| Build output directory | `dist` | +| Root directory | (empty - the repo root) | +| Build comments | Enabled (preview links are posted on PRs) | + +### How `dev` ends up live + +Cloudflare treats `main` as the production branch, and every other branch gets its own preview deployment at `.website-dk1.pages.dev`. quantsoc.org is not attached to the production deployment. Instead, its DNS record points at the `dev` branch's preview address, `dev.website-dk1.pages.dev`, which always serves the newest build of `dev`. So: + +- **Pushing to `dev`** updates `quantsoc.org` (and `staging.quantsoc.org`). +- **Pushing to `main`** only updates `website-dk1.pages.dev` and `dev.quantsoc.org`. It does not affect the live site. +- **Pushing any other branch** creates a preview at `.website-dk1.pages.dev`. Branch names are shortened and made lowercase in the address, and `/` becomes `-`. + +## Testing a change safely + +There is no automated test suite, so testing means building the site and looking at it. + +1. **Work on a branch**, never directly on `dev`: + ```bash + git checkout dev && git pull + git checkout -b feat/no-ref/short-description + ``` +2. **Develop** with `npm run start` (http://localhost:5173). +3. **Check the production build**, because this is what actually gets deployed: + ```bash + npm run build # must finish with no errors + npm run preview # serves the build at http://localhost:4173 + ``` + Click through every page you touched, and also check: + - a narrow window (or your browser's device toolbar) for mobile layout + - every link and button you added + - the browser console for errors +4. **Lint the files you changed**: `npx eslint path/to/File.jsx`. +5. **Push the branch and open a pull request into `dev`.** The GitHub Actions check (`.github/workflows/production_check.yml`) runs lint and a production build on every PR into `dev`. Cloudflare Pages will also build the branch and comment a **preview URL** on the PR (staging.quantsoc.org can't be used for this - it shows the live site). That URL is a real deployment that is *not* the live site - the best place for a final check, and to share with the exec. +6. **Merge into `dev`** once someone else has reviewed it. This is the step that goes live. + +## Deploying + +**Merging the PR into `dev` is the deployment** - Cloudflare builds and publishes it automatically. Watch the build in the Cloudflare dashboard (the project -> Deployments), then check https://quantsoc.org. You may need a hard refresh (Cmd/Ctrl + Shift + R) to see changes. + +There is no need to build or upload anything by hand. + +## Rolling back + +Revert the bad commit on `dev` and push: + +```bash +git checkout dev && git pull +git revert +git push +``` + +Cloudflare rebuilds the live site within a few minutes. You can find the `` of the bad commit with `git log`, or in the Cloudflare dashboard -> the project -> Deployments. diff --git a/README.md b/README.md index 3f5a7f5e..ace33c78 100644 --- a/README.md +++ b/README.md @@ -8,36 +8,76 @@ [![Production test](https://github.com/QuantSoc/website/actions/workflows/production_check.yml/badge.svg)](https://github.com/QuantSoc/website/actions/workflows/production_check.yml) -This repo contains the source code for the frontend of the main website of the πŸ“ˆUNSW Quantitative Finance and Trading Society (Quantsoc), powered by [ReactJs](https://reactjs.org/). +This repo contains the source code for the main website of the πŸ“ˆ UNSW Quantitative Finance and Trading Society (QuantSoc), live at **[quantsoc.org](https://quantsoc.org)**. +It is a single-page [React](https://reactjs.org/) app built with [Vite](https://vitejs.dev/). Articles and job listings are read from a [Firebase Firestore](https://firebase.google.com/docs/firestore) database; everything else is static. - +**Deploying:** Read [DEPLOYMENT.md](DEPLOYMENT.md) first. The live site is built from the `dev` branch. ## Quick start -Once you have a rather recent version of `npm` installed on a (preferably) Unix based system, simply run: -``` -$ npm install -$ npm run prepare -``` -Then you may run a development setting via: -``` -$ npm run start -``` -## Tests and linting -The cleansing process will run before each push, however, you can run the linting and test manually by doing: -``` -$ npm run lint -$ npm run test +You need [Node.js](https://nodejs.org/) 20 or newer (the dev container uses Node 20) and `npm`. + +```bash +npm ci # install the exact dependency versions from package-lock.json +npm run prepare # install the git hooks (husky) - only needed once per clone +npm run start # start the dev server at http://localhost:5173 ``` -## Running in production -To run a quick production build, simply do: +The dev server reloads automatically when you save a file. Nothing you do locally affects the live website. + +**Note:** the site reads articles and job listings from the **real** Firestore database, even locally. The site only ever *reads* data, so this is safe, but anything you see in those sections is live data. + +## Scripts + +| Command | What it does | +| ----------------- | ------------------------------------------------------------------------------------- | +| `npm run start` | Dev server at http://localhost:5173 | +| `npm run build` | Production build into `dist/` | +| `npm run preview` | Serves the `dist/` build at http://localhost:4173 - use this to check a build locally | +| `npm run lint` | Runs ESLint | +| `npm run prepare` | Installs the husky git hooks | + +There is currently **no automated test suite** (there is no `npm run test`). Testing is done by building and checking the site by hand - see [DEPLOYMENT.md](DEPLOYMENT.md#testing-a-change-safely). + +## Project structure + ``` -$ npm run build -$ npm run serve +src/ +β”œβ”€β”€ App.jsx # Routes: /, /articles, /resources, /contact, /mathsprint +β”œβ”€β”€ firebase.config.js # Firebase/Firestore client setup (see below) +β”œβ”€β”€ assets/ # Images; sponsor logos live in assets/sponsor-logos/ +β”œβ”€β”€ components/ # Components shared by more than one page (NavBar, Footer, ...) +β”œβ”€β”€ routes/ # One folder per page. Components used by only one page live in that page's folder +β”œβ”€β”€ hooks/ +└── styles/ # Global .less styles and colour constants (styles/constants.less) +legacy/ # Old Python backend - not used by the current site ``` -## Resources -For conventions, module structure, advanced configuration or cooler stuff, check out the [wiki](https://github.com/QuantSoc/website/wiki) page. -To learn React, check out the [React documentation](https://reactjs.org/). +Import aliases (`components/...`, `routes/...`, `assets/...`, `styles/...`, `hooks/...`) are defined in `vite.config.js`. + +## Common tasks + +### Updating sponsors + +Sponsors are hard-coded in `src/routes/LandingPage/SponsorshipSection/SponsorshipSection.jsx`. + +1. Put the logo in `src/assets/sponsor-logos/`. Prefer an SVG, otherwise a PNG with a transparent background. The site has a **white background**, so use a dark/coloured version of the logo, not a white one. +2. Import the logo at the top of `SponsorshipSection.jsx`. +3. Add a `` line under the right tier (`principal` or `major`). Logos appear in the order they are listed. +4. Run `npm run start` and check the Sponsors section on both desktop and a narrow (mobile) window. + +### Workshop materials + +Workshop slides and code are published in the [QuantSoc/workshop-materials](https://github.com/QuantSoc/workshop-materials) repo. + +### Updating Jobs Listings & Articles + +All the Job Listings are stored in the [Firebase Firestore](https://firebase.google.com/docs/firestore) database. Simply log into Firebase Hosting using quantsoc email OAuth, and then end it manually from there. + +## Firebase + +`src/firebase.config.js` connects the site to the `quantsoc-website-admin` Firebase project. It is only used to **read** the `articles` and `jobs` Firestore collections. The site is **not** hosted on Firebase (see [DEPLOYMENT.md](DEPLOYMENT.md)). + +The API key in that file is a public client identifier, not a secret (every visitor's browser downloads it). What actually protects the data is the project's Firestore security rules, which are managed in the Firebase console. They should allow public reads of `articles` and `jobs` only, and no public writes. + diff --git a/src/assets/sponsor-logos/jump.png b/src/assets/sponsor-logos/jump.png new file mode 100644 index 00000000..f384901b Binary files /dev/null and b/src/assets/sponsor-logos/jump.png differ diff --git a/src/assets/sponsor-logos/nine-mile.svg b/src/assets/sponsor-logos/nine-mile.svg new file mode 100644 index 00000000..4f3b318f --- /dev/null +++ b/src/assets/sponsor-logos/nine-mile.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/sponsor-logos/qrt.svg b/src/assets/sponsor-logos/qrt.svg new file mode 100644 index 00000000..5cda590a --- /dev/null +++ b/src/assets/sponsor-logos/qrt.svg @@ -0,0 +1 @@ +qrt \ No newline at end of file diff --git a/src/components/Footer/Footer.jsx b/src/components/Footer/Footer.jsx index 8d6f1555..777e5c5c 100644 --- a/src/components/Footer/Footer.jsx +++ b/src/components/Footer/Footer.jsx @@ -45,8 +45,13 @@ const Footer = () => {
  • - - Board Login + + Workshop Materials
  • diff --git a/src/components/WorkshopsSection/WorkshopsSection.jsx b/src/components/WorkshopsSection/WorkshopsSection.jsx new file mode 100644 index 00000000..028d53ca --- /dev/null +++ b/src/components/WorkshopsSection/WorkshopsSection.jsx @@ -0,0 +1,29 @@ +import { BsGithub } from 'react-icons/bs'; +import './index.less'; + +const WORKSHOP_MATERIALS_URL = 'https://github.com/QuantSoc/workshop-materials'; + +const WorkshopsSection = ({ className = '', showHeader = true }) => { + return ( +
    + {showHeader &&

    Workshops

    } + + +
    +
    Workshop Materials
    +
    + Slides, notebooks and code from our workshops, all on GitHub. +
    +
    + View on GitHub β†’ +
    +
    + ); +}; + +export default WorkshopsSection; diff --git a/src/components/WorkshopsSection/index.less b/src/components/WorkshopsSection/index.less new file mode 100644 index 00000000..a8e32a8d --- /dev/null +++ b/src/components/WorkshopsSection/index.less @@ -0,0 +1,64 @@ +@import '../../styles/constants.less'; + +.workshops-section__header { + margin-bottom: 1rem; +} + +.workshops-card { + display: flex; + align-items: center; + gap: 1.5rem; + margin: 10px; + padding: 2rem; + border-radius: 15px; + background: linear-gradient(130deg, @black-purple, @darkest-purple); + color: white; + text-decoration: none; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); + transition: box-shadow 0.3s ease, transform 0.3s ease-out; + + &:hover { + color: white; + box-shadow: inset 0 0 20px @quantsoc-primary, 0 0 20px @quantsoc-primary; + transform: scale(1.02); + } +} + +// Override the global `svg { height: 20px }` rule from the sponsorship section +.workshops-card svg.workshops-card__icon { + flex-shrink: 0; + width: 64px; + height: 64px; +} + +.workshops-card__text { + flex: 1; + text-align: left; +} + +.workshops-card__title { + font-size: 28px; + font-weight: 500; + font-family: 'Consolas', 'Courier New', Courier, monospace; +} + +.workshops-card__subtext { + margin-top: 0.25rem; + color: @quantsoc-primary; +} + +.workshops-card__cta { + flex-shrink: 0; + font-weight: 500; +} + +@media (max-width: 768px) { + .workshops-card { + flex-direction: column; + text-align: center; + } + + .workshops-card__text { + text-align: center; + } +} diff --git a/src/routes/LandingPage/LandingPage.jsx b/src/routes/LandingPage/LandingPage.jsx index bef47e97..d5872d7c 100644 --- a/src/routes/LandingPage/LandingPage.jsx +++ b/src/routes/LandingPage/LandingPage.jsx @@ -3,6 +3,7 @@ import HeroSection from './HeroSection/HeroSection'; import SponsorshipSection from './SponsorshipSection/SponsorshipSection'; // import AboutUsPage from 'routes/AboutUsPage'; import GamesSection from 'components/GamesSection/GameSection'; +import WorkshopsSection from 'components/WorkshopsSection/WorkshopsSection'; import './index.less'; @@ -21,6 +22,7 @@ const LandingPage = () => {
    + See More β†’
    diff --git a/src/routes/LandingPage/SponsorshipSection/Sponsor/Sponsor.jsx b/src/routes/LandingPage/SponsorshipSection/Sponsor/Sponsor.jsx index 37fa66f4..b703aed9 100644 --- a/src/routes/LandingPage/SponsorshipSection/Sponsor/Sponsor.jsx +++ b/src/routes/LandingPage/SponsorshipSection/Sponsor/Sponsor.jsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import "./index.less"; -const Sponsor = ({ logoSrc, sponsorLink, logoSrcInv }) => { +const Sponsor = ({ logoSrc, sponsorLink, logoSrcInv, wide = false }) => { const [isDarkMode, setIsDarkMode] = useState(false); useEffect(() => { @@ -15,7 +15,7 @@ const Sponsor = ({ logoSrc, sponsorLink, logoSrcInv }) => { }, []); return ( -
    +
    {
    - +
    - - + + - + + + +

    Want to sponsor us? Get in touch with our team and we would be more than delighted to discuss partnership opportunities and potential collaborations!

    -
    Sponsor Us diff --git a/src/routes/LandingPage/index.less b/src/routes/LandingPage/index.less index 4944c8da..a8d39459 100644 --- a/src/routes/LandingPage/index.less +++ b/src/routes/LandingPage/index.less @@ -28,6 +28,11 @@ } +// Leave room for the "See More" link, which is pulled up by its negative margin +#resources .workshops-section { + margin-bottom: 3.5rem; +} + .more-resources-button { display:block; width: 100%; diff --git a/src/routes/ResourcesPage/ResourcesPage.jsx b/src/routes/ResourcesPage/ResourcesPage.jsx index de2faf46..fb88f7d8 100644 --- a/src/routes/ResourcesPage/ResourcesPage.jsx +++ b/src/routes/ResourcesPage/ResourcesPage.jsx @@ -2,6 +2,7 @@ import HeroSection from './HeroSection/HeroSection'; import JobListings from './JobListingsSection/JobListings'; import GamesSection from 'components/GamesSection/GameSection'; +import WorkshopsSection from 'components/WorkshopsSection/WorkshopsSection'; import './index.less'; @@ -13,7 +14,9 @@ const ResourcesPage = () => { {/* separate div to ensure universal focusability of anchor */} - {/* */} +
    + +