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
2 changes: 2 additions & 0 deletions apps/workshopper/AUTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ An agent or developer must replicate the exact SAML configuration we built for W
* Copy the SAML configuration block (including `server.forward-headers-strategy: framework`) into their `application.yml`.
* Create the `SecurityConfig.java` to enable `.saml2Login()` and explicitly permit access to `/saml2/service-provider-metadata/**`.
* Modify their `application.yml` and `compose.prod.yaml` to mount the exact same central certificate folder (`/opt/hestia/secrets/saml:/app/certs:ro`) and read them via environment variables.
* **CRITICAL:** Fix the Docker DNS collision in their frontend `nginx.conf`! Both currently proxy to `http://server`, which randomly resolves to the wrong backend on the shared `hestia-edge` network. Change this to their exact unique container names (e.g., `proxy_pass http://examlense-server:8081;` and `proxy_pass http://learninggoalhub-server:8080;`).
* **CRITICAL:** Update their frontend `nginx.conf` file to proxy `location ~ ^/(v3/api-docs|swagger-ui|saml2|login/saml2/sso)` to the backend server. Otherwise, NGINX will block the SAML traffic!
* **NOTE:** While editing `compose.prod.yaml`, it is highly recommended to proactively change `POSTGRES_HOST: postgres` to their unique database container names (`examlense-postgres` or `learninggoalhub-postgres`) to prevent future DNS collisions if the database ever joins the edge network.
* Once deployed, verify that they each generate their own live metadata URLs (e.g., `https://hestia-test.aet.cit.tum.de/learninggoalhub/saml2/service-provider-metadata/tum`).

**2. Host the Privacy Policy:**
Expand Down
2 changes: 1 addition & 1 deletion apps/workshopper/compose.prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ services:
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_JPA_HIBERNATE_DDL_AUTO: update
POSTGRES_HOST: postgres
POSTGRES_HOST: workshopper-postgres
POSTGRES_PORT: 5432
POSTGRES_DB: ${POSTGRES_DB:-workshopper}
POSTGRES_USER: ${POSTGRES_USER:-workshopper}
Expand Down
4 changes: 2 additions & 2 deletions apps/workshopper/frontend/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ server {
}

location /api/ {
proxy_pass http://server:8081;
proxy_pass http://workshopper-server:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Expand All @@ -34,7 +34,7 @@ server {
}

location ~ ^/(v3/api-docs|swagger-ui|saml2|login/saml2/sso) {
proxy_pass http://server:8081;
proxy_pass http://workshopper-server:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Expand Down
27 changes: 8 additions & 19 deletions apps/workshopper/frontend/src/components/SessionsDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
import { getSessionStatus } from "@/lib/utils";
import { GraduationCap, BookOpen, Clock, Loader2, Target, Plus, Trash2, Settings, ArrowRight, Play, Archive, FileText, ChevronRight, X, Edit2, CheckCircle2, FileEdit, MoreVertical, Pencil, Presentation, ChevronDown, Sparkles } from "lucide-react";
import hestiaLogoLight from "@/assets/logos/wordmark-light.svg";
import hestiaLogoDark from "@/assets/logos/wordmark-dark.svg";
Expand Down Expand Up @@ -297,34 +298,22 @@ export default function SessionsDashboard({ onNewSession, onNewLecture, onNewSes
{(() => {
const isFinished = session.status === "complete" && session.currentStep === "finished";
const isReadyForPrep = session.status === "complete" && session.currentStep !== "finished";
const isDraft = session.status !== "complete";
// G-1: status text inside icon container so state is readable in grayscale
const statusShort = isFinished ? "Done" : isReadyForPrep ? "Ready" : "Draft";
const status = getSessionStatus(session);
return (
<div className={`flex-shrink-0 w-10 h-10 rounded-xl flex flex-col items-center justify-center transition-colors gap-0 ${isFinished ? "bg-emerald-500/15 text-emerald-500"
: isReadyForPrep ? "bg-blue-500/15 text-blue-500"
: "bg-amber-500/15 text-amber-500"
}`}>
{isFinished ? <CheckCircle2 className="h-4 w-4" /> : isReadyForPrep ? <Sparkles className="h-4 w-4" /> : <FileEdit className="h-4 w-4" />}
<span className="text-[8px] font-bold uppercase tracking-wide leading-none mt-0.5">{statusShort}</span>
<div className={`flex-shrink-0 w-10 h-10 rounded-xl flex flex-col items-center justify-center transition-colors gap-0 ${status.iconClass}`}>
{status.isFinished ? <CheckCircle2 className="h-4 w-4" /> : status.isReadyForPrep ? <Sparkles className="h-4 w-4" /> : <FileEdit className="h-4 w-4" />}
<span className="text-[8px] font-bold uppercase tracking-wide leading-none mt-0.5">{status.statusShort}</span>
</div>
);
})()}
<div className="flex-1 min-w-0">
{(() => {
const isFinished = session.status === "complete" && session.currentStep === "finished";
const isReadyForPrep = session.status === "complete" && session.currentStep !== "finished";
const statusLabel = isFinished ? "Completed" : isReadyForPrep ? "Ready for preparation" : "In progress";
const statusClass = isFinished
? "bg-emerald-500/15 text-emerald-600 dark:text-emerald-400"
: isReadyForPrep
? "bg-blue-500/15 text-blue-600 dark:text-blue-400"
: "bg-amber-500/15 text-amber-600 dark:text-amber-400";
const status = getSessionStatus(session);
return (
<div className="flex items-center gap-2 mb-0.5">
<span className="font-display font-semibold text-foreground truncate">{session.title || "Workshop Session"}</span>
<span className={`flex-shrink-0 text-xs font-medium px-2 py-0.5 rounded-full font-body ${statusClass}`}>
{statusLabel}
<span className={`flex-shrink-0 text-xs font-medium px-2 py-0.5 rounded-full font-body ${status.statusClass}`}>
{status.statusLabel}
</span>
</div>
);
Expand Down
Loading
Loading