Skip to content

jussipalanen/jussispace-frontend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JussiSpace — Property Rental Platform

A modern property rental platform for apartments and office spaces.

Browse curated listings, explore properties with photo galleries and interactive maps, pick your dates, and book in minutes. Authenticated users get a personal dashboard to track orders and manage their profile. An AI assistant is available throughout the app whenever you need help.

Admins get a full back-office panel — manage properties with Google Maps location picking, handle orders, and oversee users — all from one place.

Built with React 19 and TypeScript, bundled by Vite, styled with Tailwind CSS, tested end-to-end with Playwright, and deployed as a static site to AWS S3. Available in English and Finnish.


URLs

Environment URL
Local (dev) http://localhost:5173
Staging http://jussispace-staging.s3-website.eu-north-1.amazonaws.com
Production http://jussispace-production.s3-website.eu-north-1.amazonaws.com

Tech stack

Layer Tool
Framework React 19
Language TypeScript 5.9
Bundler Vite 8
Styles Tailwind CSS
E2E Tests Playwright
Linter ESLint 9 + typescript-eslint
Container Docker (multi-stage) + nginx
CI GitHub Actions

Environment variables

All client-exposed variables must be prefixed with VITE_. Vite inlines them into the JS bundle at build time — they are not available at runtime.

Variable Description Default
VITE_APP_TITLE App display title jussispace-frontend
VITE_APP_ENV Environment name development
VITE_API_URL Backend API base URL http://localhost:8000/api
VITE_JUSSILOG_BACKEND_API_URL Jussilog backend API URL http://localhost:8000/api
VITE_JUSSI_AIBOT_API_URL AI chatbot backend URL http://localhost:8080
VITE_AI_SECRET_KEY Secret key for AI service
VITE_GOOGLE_MAP_API_KEY Google Maps API key
VITE_STORAGE_URL File/asset storage base URL

File loading order (Vite)

File Committed Purpose
.env.example Yes Documents all available variables
.env.development Yes Safe defaults for local dev
.env.production No Production values — never commit
.env No Local overrides, takes precedence
.env.local No Machine-local overrides

Accessing env vars in code

Use src/env.ts instead of import.meta.env directly — it provides typed, defaulted access:

import env from './env'

console.log(env.apiUrl)   // VITE_API_URL
console.log(env.appTitle) // VITE_APP_TITLE

Production Docker builds

Because Vite inlines vars at build time, the production image must receive them as Docker build args:

# Using .env.production on your machine
VITE_API_URL=https://api.example.com ./dev prod:up

# Or passing directly
docker build \
  --target production \
  --build-arg VITE_API_URL=https://api.example.com \
  --build-arg VITE_APP_TITLE=jussispace-frontend \
  -t jussispace-frontend .

In CI, store these as GitHub Actions secrets and pass them as build-args in your workflow.


Local development (without Docker)

npm install
npm run dev        # http://localhost:5173
npm run build      # production build → dist/
npm run preview    # serve dist/ locally
npm run lint       # ESLint

Docker setup

The project uses a multi-stage Dockerfile with two runtime targets:

Target Base Purpose
development node:22-alpine Vite dev server with hot-reload
production nginx:1.27-alpine Optimised static file serving

./dev — command helper

Make it executable once:

chmod +x ./dev

Development commands

./dev up           # build & start dev container → http://localhost:5173
./dev down         # stop dev container
./dev restart      # restart dev container
./dev logs         # tail dev logs
./dev shell        # open shell inside dev container

Production commands

./dev prod:up      # build & start nginx container → http://localhost:80
./dev prod:down
./dev prod:restart
./dev prod:logs

Deploy commands

./dev deploy           # build & deploy to S3 production (jussispace-production)
./dev deploy:staging   # build & deploy to S3 staging (jussispace-staging)

General

./dev build        # build all images without starting
./dev ps           # list running containers
./dev prune        # remove stopped containers and dangling images

AWS S3 deployment

The app is deployed as a static site to Amazon S3 (eu-north-1 / Stockholm) via scripts/deploy.sh.

Buckets

Environment Bucket URL
Production jussispace-production http://jussispace-production.s3-website.eu-north-1.amazonaws.com
Staging jussispace-staging http://jussispace-staging.s3-website.eu-north-1.amazonaws.com

Bucket setup

Both buckets must be created once before deploying. The steps below use the AWS CLI — alternatively, see the Terraform setup section to provision everything as code.

1. Create the buckets

aws s3api create-bucket \
  --bucket jussispace-production \
  --region eu-north-1 \
  --create-bucket-configuration LocationConstraint=eu-north-1

aws s3api create-bucket \
  --bucket jussispace-staging \
  --region eu-north-1 \
  --create-bucket-configuration LocationConstraint=eu-north-1

2. Enable static website hosting

aws s3 website s3://jussispace-production \
  --index-document index.html \
  --error-document index.html

aws s3 website s3://jussispace-staging \
  --index-document index.html \
  --error-document index.html

3. Disable "Block Public Access"

aws s3api put-public-access-block \
  --bucket jussispace-production \
  --public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

aws s3api put-public-access-block \
  --bucket jussispace-staging \
  --public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

4. Attach a public-read bucket policy

for BUCKET in jussispace-production jussispace-staging; do
  aws s3api put-bucket-policy \
    --bucket "$BUCKET" \
    --policy "{
      \"Version\": \"2012-10-17\",
      \"Statement\": [{
        \"Effect\": \"Allow\",
        \"Principal\": \"*\",
        \"Action\": \"s3:GetObject\",
        \"Resource\": \"arn:aws:s3:::$BUCKET/*\"
      }]
    }"
done

Terraform setup

An alternative to the manual steps above — provisions both buckets as code using terraform/main.tf (already in this repo).

Apply:

./dev tf:init
./dev tf:apply

Or manually:

cd terraform
terraform init
terraform apply

If buckets already exist, import them into Terraform state first:

cd terraform
terraform import 'aws_s3_bucket.site["jussispace-production"]' jussispace-production
terraform import 'aws_s3_bucket.site["jussispace-staging"]' jussispace-staging

Show bucket URLs:

./dev tf:output

Notes:

  • prevent_destroy = true is set on the buckets — terraform destroy will error out to protect against accidental deletion.
  • Versioning is enabled on both buckets, allowing rollback if a bad deploy overwrites files.

Cost note: S3 static hosting costs are minimal — typically < $0.01/month for a low-traffic site. You pay only for storage ($0.023/GB in eu-north-1) and data transfer out ($0.09/GB). There are no compute or load-balancer charges.


Deploy locally

Requires AWS CLI configured with valid credentials.

./dev deploy            # deploy to production
./dev deploy:staging    # deploy to staging

# With API URL override
VITE_API_URL=https://api.example.com ./dev deploy

Deploy via GitHub Actions

Deployments are triggered by pushing a tag to the repository:

git tag 1.0.0-production && git push origin 1.0.0-production   # → production
git tag 1.0.0-staging    && git push origin 1.0.0-staging      # → staging
Workflow Trigger tag Bucket
.github/workflows/deploy.yml *-production jussispace-production
.github/workflows/deploy-staging.yml *-staging jussispace-staging

Required GitHub secrets

Secret Description
AWS_ACCESS_KEY_ID AWS access key ID
AWS_SECRET_ACCESS_KEY AWS secret access key
VITE_API_URL Production API base URL
STAGING_VITE_API_URL Staging API base URL
VITE_APP_TITLE App display title
VITE_STORAGE_URL File/asset storage base URL
VITE_GOOGLE_MAP_API_KEY Google Maps API key
VITE_JUSSI_AIBOT_API_URL AI chatbot backend URL
VITE_AI_SECRET_KEY Secret key for AI service
CLOUDFRONT_DISTRIBUTION_ID (optional) Production CloudFront distribution ID
STAGING_CLOUDFRONT_DISTRIBUTION_ID (optional) Staging CloudFront distribution ID

E2E testing with Playwright

Install Playwright (first time only)

nvm use
npm install -D @playwright/test
npx playwright install --with-deps
cp .env.example .env.development

Run tests

./dev test          # headless (all browsers)
./dev test:ui       # interactive Playwright UI mode
./dev test:debug    # step-through debugger
./dev test:report   # open last HTML report

Or via npm directly:

npm run test:e2e
npm run test:e2e:ui
npm run test:e2e:debug
npm run test:e2e:report

Test files

All e2e specs live in the e2e/ directory with the .spec.ts extension.

e2e/
└── app.spec.ts    # base smoke tests

Playwright auto-starts the Vite dev server (webServer in playwright.config.ts) before running tests, so no manual server management is needed.

Browsers covered

  • Chromium (Desktop)
  • Firefox (Desktop)
  • WebKit / Safari (Desktop)
  • Pixel 5 (Mobile Chrome)
  • iPhone 12 (Mobile Safari)

GitHub Actions CI

Three workflows run on every push and pull request to master/main:

Workflow File What it does
Lint & Type Check .github/workflows/lint.yml ESLint + tsc --noEmit
Security & Vulnerabilities .github/workflows/security.yml npm audit, Trivy FS scan, Trivy Docker image scan, SARIF upload to Security tab
Playwright E2E .github/workflows/playwright.yml Runs tests in Chromium, Firefox, WebKit in parallel; uploads HTML report as artifact

The security workflow also runs on a weekly schedule (Mondays 08:00 UTC) to catch newly disclosed CVEs.


nginx (production)

The production image uses a custom nginx.conf with:

  • Gzip compression for JS, CSS, SVG, fonts
  • Long-term caching (Cache-Control: public, immutable, max-age=1y) for Vite-hashed assets
  • No-cache on index.html to always serve the latest deploy
  • SPA fallback (try_files $uri /index.html) for client-side routing
  • Security headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy

Project structure

.
├── e2e/                        # Playwright e2e tests
│   └── app.spec.ts
├── public/                     # Static assets (favicons, site.webmanifest)
├── src/                        # Application source
│   ├── main.tsx
│   ├── App.tsx
│   ├── api.ts                  # API client
│   ├── env.ts                  # Typed env variable accessors
│   ├── i18n.ts                 # i18n translations
│   ├── index.css
│   ├── navigate.ts             # Navigation utility
│   ├── components/
│   │   ├── AppHeader.tsx
│   │   ├── ChatbotWidget.tsx   # AI chatbot widget
│   │   ├── DateRangePicker.tsx
│   │   ├── MapPicker.tsx       # Google Maps location picker
│   │   └── PropertyMap.tsx     # Google Maps property display
│   ├── contexts/
│   │   └── LanguageContext.tsx
│   └── views/
│       ├── Admin.tsx
│       ├── AdminOrderForm.tsx
│       ├── AdminOrders.tsx
│       ├── AdminProperties.tsx
│       ├── AdminPropertyForm.tsx
│       ├── AdminUserForm.tsx
│       ├── AdminUsers.tsx
│       ├── Checkout.tsx
│       ├── Listings.tsx
│       ├── Login.tsx
│       ├── MyOrders.tsx
│       ├── Profile.tsx
│       └── PropertyDetail.tsx
├── .github/workflows/
│   ├── lint.yml
│   ├── security.yml
│   ├── playwright.yml
│   ├── deploy.yml              # S3 production deploy (tag: *-production)
│   └── deploy-staging.yml      # S3 staging deploy (tag: *-staging)
├── terraform/
│   └── main.tf                 # S3 bucket provisioning (production + staging)
├── scripts/
│   └── deploy.sh               # S3 deploy script (used by ./dev deploy)
├── dev                         # Docker + test CLI helper
├── Dockerfile                  # Multi-stage build
├── docker-compose.yml          # Dev + prod services
├── nginx.conf                  # Production nginx config
├── playwright.config.ts        # Playwright configuration
├── vite.config.ts
├── tsconfig.json
└── package.json

About

React + Vite frontend for a property rental and office management platform.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors