Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules
.next
out
.git
.env*
!.env.example
test-results
playwright-report

12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
NEXT_PUBLIC_API_BASE_URL=/api
NEXT_PUBLIC_DISPLAY_TIME_ZONE=UTC
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_APP_VERSION=local
NEXT_PUBLIC_ENABLE_TELEMETRY=false
NEXT_PUBLIC_FARO_URL=
NEXT_PUBLIC_UMAMI_SCRIPT_URL=
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
NEXT_PUBLIC_UMAMI_DOMAINS=
NEXT_PUBLIC_GRAFANA_DASHBOARD_URL=
NEXT_PUBLIC_UMAMI_DASHBOARD_URL=

29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Frontend checks
on:
pull_request:
push:
branches: [main, feature/frontend]
permissions:
contents: read
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm test
- run: npm run build
- run: npx playwright install --with-deps chromium
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: browser-test-report
path: playwright-report
20 changes: 20 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CodeQL
on:
workflow_dispatch:
pull_request:
push:
branches: [main]
permissions:
actions: read
contents: read
security-events: write
jobs:
analyze:
if: github.event.repository.private == false || vars.ENABLE_CODEQL == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
- uses: github/codeql-action/analyze@v3
22 changes: 22 additions & 0 deletions .github/workflows/image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Local-test image
on:
push:
branches: [main]
permissions:
contents: read
packages: write
jobs:
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/microsserv52/admin:sha-${{ github.sha }}
50 changes: 50 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*
!.env.example

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts

# Playwright
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
2 changes: 2 additions & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
24

27 changes: 27 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
repos:
- repo: local
hooks:
- id: formatting
name: Formatting
entry: npm run format:check
language: system
pass_filenames: false
always_run: true
- id: lint
name: ESLint
entry: npm run lint
language: system
pass_filenames: false
always_run: true
- id: types
name: TypeScript
entry: npm run typecheck
language: system
pass_filenames: false
always_run: true
- id: unit
name: Unit and contract tests
entry: npm test
language: system
pass_filenames: false
always_run: true
8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
.next
out
coverage
playwright-report
test-results
package-lock.json

24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM node:24-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ARG NEXT_PUBLIC_API_BASE_URL=/api
ARG NEXT_PUBLIC_DISPLAY_TIME_ZONE=UTC
ARG NEXT_PUBLIC_APP_ENV=development
ARG NEXT_PUBLIC_APP_VERSION=local
ARG NEXT_PUBLIC_ENABLE_TELEMETRY=false
ARG NEXT_PUBLIC_FARO_URL
ARG NEXT_PUBLIC_UMAMI_SCRIPT_URL
ARG NEXT_PUBLIC_UMAMI_WEBSITE_ID
ARG NEXT_PUBLIC_UMAMI_DOMAINS
ARG NEXT_PUBLIC_GRAFANA_DASHBOARD_URL
ARG NEXT_PUBLIC_UMAMI_DASHBOARD_URL
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

FROM nginx:1.28-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/out /usr/share/nginx/html/admin
EXPOSE 80

83 changes: 81 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,81 @@
# admin
Educational demo store administration interface
# Microsserv52 admin

English-language educational mock store frontend at `/admin/`. **No real
payments or transactions take place, and no goods are shipped.**

This independent repository is integrated at `platform/web/admin` as a Git
submodule. It uses Next.js App Router, TypeScript, MUI with Emotion, Tailwind,
TanStack Query, runtime validation and configurable Grafana Faro/Umami.
Admin additionally uses TanStack Table; store/admin use React Hook Form.

## Local development

Use Node.js 24 and npm. Copy `.env.example` to `.env.local` if local values are
needed; never commit credentials.

```bash
npm ci
npm run dev
```

The application has a fixed build-time base path `/admin/`. Run the platform
Compose environment for same-origin API/cookie testing. A standalone dev server
needs an explicitly configured API origin/proxy; do not weaken CORS or save JWTs
in browser storage to make a development shortcut work.

## Checks

```bash
npm run format:check
npm run lint
npm run typecheck
npm test
npm run build
npm run test:e2e
pre-commit run --all-files
```

Browser tests use labeled API fixtures and a local static test server. CI installs
Chromium before running them. For a running platform preview set
`PLAYWRIGHT_BASE_URL`. The tests validate UI contracts, not live backend behavior.

## Static output and containers

`npm run build` exports the site to `out/`. It requires no Next.js server,
server actions, BFF, or database connection at runtime. Detail screens use query
IDs because arbitrary runtime IDs cannot be pre-generated by static export.

The Dockerfile uses the official Node build image and serves the export with
Nginx. The platform Compose proxy reserves each zone's prefix and routes `/api/`
to Gateway. The frontend Kubernetes deployment is deferred. CDN provider,
production origins and environment values must be discussed with the owner
before deployment.

All `NEXT_PUBLIC_*` values are public, compiled at build time and require a new
build when changed. Set API URL, display timezone, app environment/version and
telemetry values using `.env.example`. Telemetry defaults to disabled until
hosting and policy are agreed. No secret belongs in a public variable.

## Authentication and privacy

Gateway owns the HttpOnly `ms52-session` cookie and browser session endpoints.
Mutations obtain a CSRF token first. Gateway and services enforce access;
frontend route guards only control presentation. Expired sessions require login.
Only cart product IDs/quantities and UI preferences are stored on the device.
The store PWA is online-only; no service worker caches private data or replays
orders. Every checkout is explicitly labeled as a demo.

## Design and integration documentation

The approved defaults and visual references live in the platform
[design direction](https://github.com/Microsserv52/platform/blob/feature/frontend-applications/docs/frontend/design.md).
See the platform [frontend documentation](https://github.com/Microsserv52/platform/blob/feature/frontend-applications/docs/frontend/index.md)
for routing, feature scope, package decisions, API prerequisites, local fixture
mode and verification evidence. antislop is applied during implementation.

Source was initialized with official `create-next-app@16.4.5`; browser testing
was initialized with official `create-playwright@1.17.139`. Each repository owns
its npm lockfile, checks, immutable image workflow and source history. CodeQL
on private repositories requires the organization's corresponding GitHub
security entitlement. CI configuration is included; remote workflow results
are not claimed until the branches are pushed and checks have run.
18 changes: 18 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";

const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);

export default eslintConfig;
12 changes: 12 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "export",
basePath: "/admin",
trailingSlash: true,
images: { unoptimized: true },
reactCompiler: true,
turbopack: {
rules: { "*.css": { loaders: ["@tailwindcss/turbopack"], as: "*.css" } },
},
};
export default nextConfig;
11 changes: 11 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
location = /health { access_log off; return 200 "ok"; add_header Content-Type text/plain; }
location /admin/_next/ { add_header Cache-Control "public, max-age=31536000, immutable"; try_files $uri =404; }
location / { try_files $uri $uri/ =404; add_header Cache-Control "no-cache"; }
error_page 404 /admin/404.html;
}

Loading
Loading