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
279 changes: 247 additions & 32 deletions client/package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"dependencies": {
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
Expand All @@ -25,13 +26,18 @@
"@radix-ui/react-tooltip": "^1.2.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"i18next": "^25.10.2",
"lucide-react": "^0.546.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-i18next": "^16.6.0",
"react-router-dom": "^7.9.4",
"serve": "^14.2.5",
"tailwind-merge": "^3.3.1"
},
"overrides": {
"serialize-javascript": "^7.0.4"
},
"devDependencies": {
"@eslint/js": "^9.38.0",
"@testing-library/jest-dom": "^6.9.1",
Expand Down
209 changes: 106 additions & 103 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { AuthProvider } from "@/contexts/AuthContext";
import { ThemeProvider } from "@/contexts/ThemeContext";
import { LanguageProvider } from "@/contexts/LanguageContext";
import { TooltipProvider } from "@/components/ui/tooltip";
import { ProtectedRoute } from "@/components/ProtectedRoute";
import { RootRoute } from "@/components/RootRoute";
Expand Down Expand Up @@ -35,112 +36,114 @@ function App() {
return (
<ErrorBoundary>
<ThemeProvider>
<AuthProvider>
<TooltipProvider>
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/auth/verify" element={<AuthVerifyPage />} />
<Route
path="/home"
element={
<ProtectedRoute requiredRole="TEACHER">
<HomePage />
</ProtectedRoute>
}
/>
<LanguageProvider>
<AuthProvider>
<TooltipProvider>
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/auth/verify" element={<AuthVerifyPage />} />
<Route
path="/home"
element={
<ProtectedRoute requiredRole="TEACHER">
<HomePage />
</ProtectedRoute>
}
/>

{/* Public Access Routes */}
<Route
path="/recommendations"
element={
<MainLayout>
<RecommendationsPage />
</MainLayout>
}
/>
<Route
path="/library"
element={
<MainLayout>
<LibraryPage />
</MainLayout>
}
/>
<Route
path="/activity-details/:id"
element={
<MainLayout>
<ActivityDetails />
</MainLayout>
}
/>
{/* Public Access Routes */}
<Route
path="/recommendations"
element={
<MainLayout>
<RecommendationsPage />
</MainLayout>
}
/>
<Route
path="/library"
element={
<MainLayout>
<LibraryPage />
</MainLayout>
}
/>
<Route
path="/activity-details/:id"
element={
<MainLayout>
<ActivityDetails />
</MainLayout>
}
/>

{/* Protected Routes */}
<Route
path="/favourites"
element={
<ProtectedLayout allowedRoles={["TEACHER", "ADMIN"]}>
<FavouritesPage />
</ProtectedLayout>
}
/>
<Route
path="/history"
element={
<ProtectedLayout allowedRoles={["TEACHER", "ADMIN"]}>
<SearchHistoryPage />
</ProtectedLayout>
}
/>
<Route
path="/upload"
element={
<ProtectedLayout requiredRole="ADMIN">
<ActivitySetupPage />
</ProtectedLayout>
}
/>
<Route
path="/activity-edit/:id"
element={
<ProtectedLayout requiredRole="ADMIN">
<ActivityEditPage />
</ProtectedLayout>
}
/>
<Route
path="/users"
element={
<ProtectedLayout requiredRole="ADMIN">
<UserManagementPage />
</ProtectedLayout>
}
/>
<Route
path="/account"
element={
<ProtectedLayout
allowedRoles={["ADMIN", "TEACHER", "GUEST"]}
>
<AccountDashboardPage />
</ProtectedLayout>
}
/>
{/* Protected Routes */}
<Route
path="/favourites"
element={
<ProtectedLayout allowedRoles={["TEACHER", "ADMIN"]}>
<FavouritesPage />
</ProtectedLayout>
}
/>
<Route
path="/history"
element={
<ProtectedLayout allowedRoles={["TEACHER", "ADMIN"]}>
<SearchHistoryPage />
</ProtectedLayout>
}
/>
<Route
path="/upload"
element={
<ProtectedLayout requiredRole="ADMIN">
<ActivitySetupPage />
</ProtectedLayout>
}
/>
<Route
path="/activity-edit/:id"
element={
<ProtectedLayout requiredRole="ADMIN">
<ActivityEditPage />
</ProtectedLayout>
}
/>
<Route
path="/users"
element={
<ProtectedLayout requiredRole="ADMIN">
<UserManagementPage />
</ProtectedLayout>
}
/>
<Route
path="/account"
element={
<ProtectedLayout
allowedRoles={["ADMIN", "TEACHER", "GUEST"]}
>
<AccountDashboardPage />
</ProtectedLayout>
}
/>

<Route path="/" element={<RootRoute />} />
<Route
path="/impressum"
element={
<MainLayout>
<ImpressumPage />
</MainLayout>
}
/>
</Routes>
</Router>
</TooltipProvider>
</AuthProvider>
<Route path="/" element={<RootRoute />} />
<Route
path="/impressum"
element={
<MainLayout>
<ImpressumPage />
</MainLayout>
}
/>
</Routes>
</Router>
</TooltipProvider>
</AuthProvider>
</LanguageProvider>
</ThemeProvider>
</ErrorBoundary>
);
Expand Down
28 changes: 20 additions & 8 deletions client/src/components/ActivityCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
import { Clock, Users } from "lucide-react";
import { FavouriteButton } from "@/components/favourites/FavouriteButton";
import type { Activity } from "@/types/activity";
import { useTranslation } from "react-i18next";

interface ActivityCardProps {
activity: Activity;
Expand All @@ -21,6 +22,13 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
isFavourited,
}) => {
const navigate = useNavigate();
const { t } = useTranslation();

const translateEnum = (category: string, value: string): string => {
const key = `enums.${category}.${value}`;
const translated = t(key);
return translated === key ? value : translated;
};

const ageRange =
activity.ageMin && activity.ageMax
Expand Down Expand Up @@ -98,15 +106,15 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
variant="outline"
className="text-xs px-2 py-0.5 bg-primary/5 text-primary border-primary/20 hover:bg-primary/10 transition-colors"
>
{activity.format}
{translateEnum("format", activity.format)}
</Badge>
)}
{activity.bloomLevel && (
<Badge
variant="secondary"
className="text-xs px-2 py-0.5 bg-secondary/50 text-secondary-foreground"
>
{activity.bloomLevel}
{translateEnum("bloomLevel", activity.bloomLevel)}
</Badge>
)}
</div>
Expand All @@ -117,18 +125,22 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
{activity.mentalLoad && (
<div className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-blue-500" />
<span className="text-muted-foreground">Mental:</span>
<span className="text-muted-foreground">
{t("activityDetails.mental")}:
</span>
<span className="font-medium text-card-foreground capitalize">
{activity.mentalLoad}
{translateEnum("energy", activity.mentalLoad)}
</span>
</div>
)}
{activity.physicalEnergy && (
<div className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-orange-500" />
<span className="text-muted-foreground">Physical:</span>
<span className="text-muted-foreground">
{t("activityDetails.physical")}:
</span>
<span className="font-medium text-card-foreground capitalize">
{activity.physicalEnergy}
{translateEnum("energy", activity.physicalEnergy)}
</span>
</div>
)}
Expand All @@ -144,7 +156,7 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
variant="outline"
className="text-xs px-2 py-0.5 bg-muted/50 text-muted-foreground border-muted-foreground/20"
>
{topic}
{translateEnum("topics", topic)}
</Badge>
))}
{activity.topics.length > 2 && (
Expand All @@ -166,7 +178,7 @@ export const ActivityCard: React.FC<ActivityCardProps> = ({
onClick={handleViewDetails}
className={`${compact ? "mt-1" : "w-full"} h-8 text-xs hover:bg-primary/5 hover:border-primary/20 transition-colors`}
>
View Details
{t("activityCard.viewDetails")}
</Button>
)}
</CardContent>
Expand Down
Loading
Loading