Skip to content

Commit 996b932

Browse files
committed
feat: Preview mode
1 parent f38baaf commit 996b932

File tree

7 files changed

+247
-21
lines changed

7 files changed

+247
-21
lines changed

app/app/iranyitopult/page.tsx

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useUserRole } from "@/contexts/user-role-context"
1515
import { useAuth } from "@/contexts/auth-context"
1616
import { useApiQuery } from "@/lib/api-helpers"
1717
import { apiClient } from "@/lib/api"
18+
import type { UserRole } from "@/contexts/user-role-context"
1819
import {
1920
Users,
2021
Clock,
@@ -42,7 +43,8 @@ import {
4243
GraduationCap,
4344
User,
4445
Loader2,
45-
Settings
46+
Settings,
47+
Eye
4648
} from "lucide-react"
4749

4850
// Function to get dynamic welcome message based on time of day and season
@@ -102,6 +104,20 @@ function getDynamicWelcomeMessage(firstName: string = 'Felhasználó'): string {
102104
}
103105
}
104106

107+
// Function to get role display name in Hungarian
108+
function getRoleDisplayName(role: UserRole | null): string {
109+
switch (role) {
110+
case 'admin':
111+
return 'adminisztrátor'
112+
case 'class-teacher':
113+
return 'osztályfőnök'
114+
case 'student':
115+
return 'diák'
116+
default:
117+
return 'ismeretlen'
118+
}
119+
}
120+
105121
// Admin Widget Components - Database Admin Style
106122
function ActiveUsersWidget() {
107123
const { isAuthenticated } = useAuth()
@@ -889,10 +905,18 @@ function FirstStepsWidget() {
889905
}
890906

891907
export default function Page() {
892-
const { currentRole } = useUserRole()
908+
const { currentRole, isPreviewMode, actualUserRole } = useUserRole()
893909
const { user } = useAuth()
894910
const [welcomeMessage, setWelcomeMessage] = useState('')
895911

912+
// Debug logging
913+
console.log('🎭 Dashboard state:', {
914+
currentRole,
915+
actualUserRole,
916+
isPreviewMode,
917+
calculation: `${actualUserRole} !== null && ${actualUserRole} !== ${currentRole} && ${actualUserRole} === 'admin'`
918+
})
919+
896920
// Update welcome message on component mount and every minute
897921
useEffect(() => {
898922
const updateMessage = () => {
@@ -1011,6 +1035,31 @@ export default function Page() {
10111035
<SidebarInset>
10121036
<SiteHeader />
10131037
<div className="flex-1 space-y-4 p-4 md:p-6">
1038+
{/* Preview Mode Banner */}
1039+
{isPreviewMode && (
1040+
<Card className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/30">
1041+
<CardContent className="pt-4">
1042+
<div className="flex items-center gap-3">
1043+
<div className="p-2 bg-blue-500 rounded-lg">
1044+
<Eye className="h-4 w-4 text-white" />
1045+
</div>
1046+
<div className="flex-1">
1047+
<h3 className="font-semibold text-blue-900 dark:text-blue-100">
1048+
Előnézeti mód
1049+
</h3>
1050+
<p className="text-sm text-blue-700 dark:text-blue-200">
1051+
Ön {getRoleDisplayName(actualUserRole)} jogosultsággal rendelkezik, de jelenleg a(z) {getRoleDisplayName(currentRole)} nézetet tekinti meg előnézetként.
1052+
Az itt látható adatok valós információk, de csak megtekintés céljából szolgálnak.
1053+
</p>
1054+
</div>
1055+
<Badge variant="secondary" className="bg-blue-100 text-blue-800 border-blue-200 dark:bg-blue-900 dark:text-blue-200 dark:border-blue-700">
1056+
Előnézet
1057+
</Badge>
1058+
</div>
1059+
</CardContent>
1060+
</Card>
1061+
)}
1062+
10141063
{/* Header Section */}
10151064
<Card>
10161065
<CardContent className="">

components/role-synchronizer.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,30 @@ import { useUserRole } from '@/contexts/user-role-context'
66

77
export function RoleSynchronizer() {
88
const { permissions, getCurrentRole, isLoading, error } = usePermissions()
9-
const { setRole, currentRole } = useUserRole()
9+
const { setRole, currentRole, initializeUserRole, actualUserRole } = useUserRole()
1010
const lastSyncedRole = useRef<string | null>(null)
1111
const syncInProgress = useRef(false)
12+
const hasInitialized = useRef(false)
1213

1314
useEffect(() => {
1415
// Only sync role when permissions are loaded and we have valid permissions
1516
if (!isLoading && permissions && !syncInProgress.current) {
1617
try {
1718
const primaryRole = getCurrentRole()
1819

20+
// Initialize the actual user role on first load
21+
if (!hasInitialized.current && actualUserRole === null) {
22+
console.log(`🎭 Initializing actual user role: ${primaryRole}`)
23+
initializeUserRole(primaryRole)
24+
hasInitialized.current = true
25+
26+
// Don't sync current role on first initialization if it matches primary role
27+
if (currentRole === primaryRole) {
28+
console.log(`🎭 Current role already matches primary role: ${primaryRole}`)
29+
return
30+
}
31+
}
32+
1933
// Only update role if it's different from current AND it's different from last synced
2034
// This prevents infinite loops and unnecessary role changes
2135
if (primaryRole !== currentRole && primaryRole !== lastSyncedRole.current) {
@@ -43,7 +57,7 @@ export function RoleSynchronizer() {
4357
console.warn('⚠️ Permissions error, preserving current role to prevent logout:', currentRole)
4458
// Don't sync roles when there's an error to prevent unwanted logouts
4559
}
46-
}, [isLoading, permissions, error, getCurrentRole, currentRole, setRole])
60+
}, [isLoading, permissions, error, getCurrentRole, currentRole, setRole, initializeUserRole, actualUserRole])
4761

4862
// This component doesn't render anything
4963
return null

components/site-header.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button"
55
import { Separator } from "@/components/ui/separator"
66
import { SidebarTrigger } from "@/components/ui/sidebar"
77
import { Clapperboard } from "lucide-react"
8+
import { useUserRole } from "@/contexts/user-role-context"
89

910
// Map of routes to page names based on the sidebar data
1011
const routeToPageName: Record<string, string> = {
@@ -28,8 +29,23 @@ function getCurrentPageName(pathname: string): string {
2829

2930
export function SiteHeader() {
3031
const pathname = usePathname()
32+
const { isPreviewMode, currentRole, actualUserRole } = useUserRole()
3133
const currentPageName = getCurrentPageName(pathname)
3234

35+
// Function to get role display name in Hungarian
36+
const getRoleDisplayName = (role: string | null): string => {
37+
switch (role) {
38+
case 'admin':
39+
return 'adminisztrátor'
40+
case 'class-teacher':
41+
return 'osztályfőnök'
42+
case 'student':
43+
return 'diák'
44+
default:
45+
return 'ismeretlen'
46+
}
47+
}
48+
3349
return (
3450
<header className="flex h-16 md:h-14 shrink-0 items-center gap-2 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 transition-[width,height] ease-linear">
3551
<div className="flex items-center w-full gap-3 px-4 lg:gap-4 lg:px-6">
@@ -48,6 +64,14 @@ export function SiteHeader() {
4864
<span className="px-1.5 py-0.5 text-[10px] font-semibold bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 rounded border border-orange-200 dark:border-orange-700">
4965
BETA
5066
</span>
67+
{isPreviewMode && (
68+
<span
69+
className="px-1.5 py-0.5 text-[10px] font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 rounded border border-blue-200 dark:border-blue-700"
70+
title={`Előnézeti mód: ${getRoleDisplayName(actualUserRole)} nézi meg a(z) ${getRoleDisplayName(currentRole)} nézetet`}
71+
>
72+
ELŐNÉZET
73+
</span>
74+
)}
5175
</div>
5276
</div>
5377
</div>

components/team-switcher.tsx

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface Team {
2626
logo: React.ElementType
2727
plan: string
2828
role: UserRole
29+
isPreview?: boolean
2930
}
3031

3132
export function TeamSwitcher({
@@ -34,12 +35,63 @@ export function TeamSwitcher({
3435
teams: Team[]
3536
}) {
3637
const { isMobile } = useSidebar()
37-
const { currentRole, setRole } = useUserRole()
38+
const { currentRole, setRole, isPreviewMode, actualUserRole } = useUserRole()
3839
const { permissions, getAvailableRoles, isLoading } = usePermissions()
3940

40-
// Filter teams based on user permissions
41+
// Filter teams based on user permissions and admin preview logic
4142
const availableRoles = getAvailableRoles()
42-
const allowedTeams = teams.filter(team => availableRoles.includes(team.role))
43+
44+
// Check if user is an admin (any type)
45+
const isAnyAdmin = permissions?.permissions?.is_admin ||
46+
permissions?.permissions?.is_system_admin ||
47+
permissions?.permissions?.is_teacher_admin ||
48+
permissions?.permissions?.is_developer_admin ||
49+
permissions?.role_info?.admin_type === 'system_admin' ||
50+
permissions?.role_info?.admin_type === 'teacher' ||
51+
permissions?.role_info?.admin_type === 'dev' ||
52+
permissions?.role_info?.admin_type === 'developer' ||
53+
permissions?.role_info?.primary_role === 'developer_admin'
54+
55+
// Check if user is both admin and "ofő" (class teacher)
56+
const isAdminAndClassTeacher = isAnyAdmin && (
57+
permissions?.permissions?.is_osztaly_fonok ||
58+
permissions?.role_info?.special_role === 'class_teacher'
59+
)
60+
61+
let allowedTeams = teams.filter(team => {
62+
if (!isAnyAdmin) {
63+
// Non-admins only see their available roles
64+
return availableRoles.includes(team.role)
65+
}
66+
67+
// Admins can always see admin role
68+
if (team.role === 'admin') {
69+
return true
70+
}
71+
72+
// Admins can always preview student role
73+
if (team.role === 'student') {
74+
return true
75+
}
76+
77+
// For class-teacher role:
78+
// - If admin is also a class teacher (ofő+admin), show it as actual role
79+
// - If admin is not a class teacher, show it as preview
80+
if (team.role === 'class-teacher') {
81+
return true // Always show for admins (either as actual or preview)
82+
}
83+
84+
return availableRoles.includes(team.role)
85+
})
86+
87+
// Mark preview status for admin users
88+
allowedTeams = allowedTeams.map(team => ({
89+
...team,
90+
isPreview: isAnyAdmin && team.role !== 'admin' && (
91+
team.role === 'student' ||
92+
(team.role === 'class-teacher' && !isAdminAndClassTeacher)
93+
)
94+
}))
4395

4496
const activeTeam = allowedTeams.find(team => team.role === currentRole) || allowedTeams[0]
4597

@@ -76,11 +128,18 @@ export function TeamSwitcher({
76128
<div className="grid flex-1 text-left text-sm leading-tight">
77129
<div className="flex items-center gap-2">
78130
<span className="truncate font-medium">{activeTeam.name}</span>
131+
{activeTeam.isPreview && (
132+
<span className="px-1 py-0.5 text-[9px] font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 rounded border border-blue-200 dark:border-blue-700">
133+
ELŐNÉZET
134+
</span>
135+
)}
79136
<span className="px-1 py-0.5 text-[9px] font-semibold bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 rounded border border-orange-200 dark:border-orange-700">
80137
BETA
81138
</span>
82139
</div>
83-
<span className="truncate text-xs">{activeTeam.plan}</span>
140+
<span className="truncate text-xs">
141+
{activeTeam.isPreview ? `${activeTeam.plan} (Előnézet)` : activeTeam.plan}
142+
</span>
84143
</div>
85144
</SidebarMenuButton>
86145
</SidebarMenuItem>
@@ -107,11 +166,18 @@ export function TeamSwitcher({
107166
<div className="grid flex-1 text-left text-sm leading-tight">
108167
<div className="flex items-center gap-2">
109168
<span className="truncate font-medium">{activeTeam.name}</span>
169+
{activeTeam.isPreview && (
170+
<span className="px-1 py-0.5 text-[9px] font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 rounded border border-blue-200 dark:border-blue-700">
171+
ELŐNÉZET
172+
</span>
173+
)}
110174
<span className="px-1 py-0.5 text-[9px] font-semibold bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200 rounded border border-orange-200 dark:border-orange-700">
111175
BETA
112176
</span>
113177
</div>
114-
<span className="truncate text-xs">{activeTeam.plan}</span>
178+
<span className="truncate text-xs">
179+
{activeTeam.isPreview ? `${activeTeam.plan} (Előnézet)` : activeTeam.plan}
180+
</span>
115181
</div>
116182
<ChevronsUpDown className="ml-auto" />
117183
</SidebarMenuButton>
@@ -134,7 +200,14 @@ export function TeamSwitcher({
134200
<div className="flex size-6 items-center justify-center rounded-md border">
135201
<team.logo className="size-3.5 shrink-0" />
136202
</div>
137-
{team.name}
203+
<div className="flex-1 flex items-center gap-2">
204+
<span>{team.name}</span>
205+
{team.isPreview && (
206+
<span className="px-1 py-0.5 text-[8px] font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 rounded border border-blue-200 dark:border-blue-700">
207+
ELŐNÉZET
208+
</span>
209+
)}
210+
</div>
138211
<DropdownMenuShortcut>{index + 1}</DropdownMenuShortcut>
139212
</DropdownMenuItem>
140213
))}

contexts/permissions-context.tsx

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -276,26 +276,29 @@ export function PermissionsProvider({ children }: { children: React.ReactNode })
276276
const { permissions: perms, role_info, display_properties } = permissions
277277
const roles: AvailableRole[] = []
278278

279-
// Check if user is a system admin or teacher admin (médiatanár) or general admin
279+
// Check if user is a system admin or teacher admin (médiatanár) or developer admin or general admin
280280
const isSystemAdmin = perms?.is_system_admin || role_info?.primary_role === 'system_admin' || role_info?.admin_type === 'system_admin'
281281
const isTeacherAdmin = perms?.is_teacher_admin || role_info?.admin_type === 'teacher'
282+
const isDeveloperAdmin = perms?.is_developer_admin || role_info?.admin_type === 'dev' || role_info?.admin_type === 'developer' || role_info?.primary_role === 'developer_admin'
282283
const isGeneralAdmin = perms?.is_admin
283284

284285
console.log('🔍 Role Analysis:', {
285286
isSystemAdmin,
286287
isTeacherAdmin,
288+
isDeveloperAdmin,
287289
isGeneralAdmin,
288290
primary_role: role_info?.primary_role,
289291
admin_type: role_info?.admin_type,
290292
is_system_admin: perms?.is_system_admin,
291293
is_teacher_admin: perms?.is_teacher_admin,
294+
is_developer_admin: perms?.is_developer_admin,
292295
is_admin: perms?.is_admin,
293296
is_osztaly_fonok: perms?.is_osztaly_fonok,
294297
special_role: role_info?.special_role
295298
})
296299

297-
// Admin role: Rendszeradmin OR Tanár-admin (Médiatanár) OR általános admin szerepkör
298-
if (isSystemAdmin || isTeacherAdmin || isGeneralAdmin) {
300+
// Admin role: Rendszeradmin OR Tanár-admin (Médiatanár) OR Developer admin OR általános admin szerepkör
301+
if (isSystemAdmin || isTeacherAdmin || isDeveloperAdmin || isGeneralAdmin) {
299302
roles.push('admin')
300303
}
301304

@@ -344,6 +347,7 @@ export function PermissionsProvider({ children }: { children: React.ReactNode })
344347
// Multiple roles available - prioritize based on hierarchy and role types
345348
const isSystemAdmin = perms?.is_system_admin || role_info?.primary_role === 'system_admin' || role_info?.admin_type === 'system_admin'
346349
const isTeacherAdmin = perms?.is_teacher_admin || role_info?.admin_type === 'teacher' || perms?.is_admin
350+
const isDeveloperAdmin = perms?.is_developer_admin || role_info?.admin_type === 'dev' || role_info?.admin_type === 'developer' || role_info?.primary_role === 'developer_admin'
347351
const isGeneralAdmin = perms?.is_admin
348352
const isClassTeacher = perms?.is_osztaly_fonok || role_info?.special_role === 'class_teacher'
349353

@@ -352,32 +356,37 @@ export function PermissionsProvider({ children }: { children: React.ReactNode })
352356
return 'admin'
353357
}
354358

355-
// Priority 2: Médiatanár (teacher admin) gets admin role
359+
// Priority 2: Developer Admin gets admin role
360+
if (availableRoles.includes('admin') && isDeveloperAdmin) {
361+
return 'admin'
362+
}
363+
364+
// Priority 3: Médiatanár (teacher admin) gets admin role
356365
if (availableRoles.includes('admin') && isTeacherAdmin) {
357366
return 'admin'
358367
}
359368

360-
// Priority 3: General admin gets admin role
369+
// Priority 4: General admin gets admin role
361370
if (availableRoles.includes('admin') && isGeneralAdmin) {
362371
return 'admin'
363372
}
364373

365-
// Priority 4: Pure osztályfőnök (class teacher without admin rights) gets class-teacher role
366-
if (availableRoles.includes('class-teacher') && isClassTeacher && !isTeacherAdmin && !isSystemAdmin && !isGeneralAdmin) {
374+
// Priority 5: Pure osztályfőnök (class teacher without admin rights) gets class-teacher role
375+
if (availableRoles.includes('class-teacher') && isClassTeacher && !isTeacherAdmin && !isSystemAdmin && !isDeveloperAdmin && !isGeneralAdmin) {
367376
return 'class-teacher'
368377
}
369378

370-
// Priority 4: If user has both admin and class-teacher roles (médiatanár + osztályfőnök), default to admin
379+
// Priority 6: If user has both admin and class-teacher roles (médiatanár + osztályfőnök), default to admin
371380
if (availableRoles.includes('admin')) {
372381
return 'admin'
373382
}
374383

375-
// Priority 5: Fallback to class-teacher if available
384+
// Priority 7: Fallback to class-teacher if available
376385
if (availableRoles.includes('class-teacher')) {
377386
return 'class-teacher'
378387
}
379388

380-
// Priority 6: Fallback to student
389+
// Priority 8: Fallback to student
381390
if (availableRoles.includes('student')) {
382391
return 'student'
383392
}

0 commit comments

Comments
 (0)