40227-vm/ref-frontend/src/components/DevModeBadge.tsx
Dmitri d4a5378adf Refactor: migrate frontend to Vite/React, add product backend modules
Frontend:
- Replace Next.js with Vite + React + TypeScript
- Add new component architecture (app-shell, sidebar, dashboard modules)
- Implement product modules: FRAME, safety protocols, walkthrough checkin,
  campus/staff attendance, personality quiz, sign language, classroom timer
- Add shadcn/ui component library with Tailwind CSS
- Remove legacy generated components, stores, and pages

Backend:
- Add product migrations: frame_entries, user_progress, safety_quiz_results,
  walkthrough_checkins, communication_events, personality_quiz_results,
  campus_attendance_config/summaries, staff_attendance_records, content_catalog
- Add corresponding models, services, and routes
- Implement cookie-based auth with refresh token rotation
- Add content catalog seeder with product content
- Migrate to ESLint flat config
- Switch from yarn to npm

Infrastructure:
- Update .gitignore for new tooling
- Add project documentation (CLAUDE.md, docs/)
- Remove deprecated config files and yarn.lock

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-09 15:18:23 +02:00

151 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react';
import useDevCompilationStatus from '../hooks/useDevCompilationStatus';
const DevModeBadge: React.FC = () => {
const [isVisible, setIsVisible] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(true);
const compilationStatus = useDevCompilationStatus();
const [badgeStyles, setBadgeStyles] = useState<React.CSSProperties>({
position: 'fixed',
bottom: '20px',
left: '70px',
background: 'rgba(0, 0, 0, 0.85)',
color: 'white',
padding: '15px',
borderRadius: '8px',
fontFamily: 'sans-serif',
fontSize: '14px',
lineHeight: '1.5',
textAlign: 'left',
zIndex: 2147483647,
boxShadow: '0 4px 10px rgba(0, 0, 0, 0.3)',
whiteSpace: 'pre-wrap',
transition: 'width 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), padding 0.3s ease-in-out, opacity 0.3s ease-in-out, background-color 0.3s ease-in-out', // Improved transition for width
opacity: 0,
pointerEvents: 'none',
width: '340px',
maxWidth: '340px',
height: 'auto',
overflow: 'hidden',
cursor: 'pointer',
});
const fullText = `🚧 Your app is running in development mode.
Current request is compiling and may take a few moments.
💡 Tip: Set up a stable environment to run your app in production mode—pages will load instantly without compilation delays.`;
const collapsedText = '🚧 DEV stage';
useEffect(() => {
if (compilationStatus === 'ready') {
setIsCollapsed(true);
} else {
setIsCollapsed(false);
}
}, [compilationStatus]);
useEffect(() => {
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') {
setIsVisible(true);
setBadgeStyles(prev => ({
...prev,
opacity: 1,
width: '120px',
maxWidth: '120px',
padding: '6px 10px',
borderRadius: '18px',
whiteSpace: 'nowrap',
fontSize: '12px',
cursor: 'pointer',
pointerEvents: 'auto',
}));
} else {
setIsVisible(false);
setBadgeStyles(prev => ({ ...prev, opacity: 0 }));
}
}, []);
useEffect(() => {
if (!isVisible) return;
if (isCollapsed) {
setBadgeStyles(prev => ({
...prev,
width: '140px',
maxWidth: '160px',
padding: '6px 20px',
borderRadius: '18px',
whiteSpace: 'nowrap',
fontSize: '12px',
}));
} else {
setBadgeStyles(prev => ({
...prev,
width: '340px',
maxWidth: '340px',
padding: '15px',
borderRadius: '8px',
whiteSpace: 'pre-wrap',
fontSize: '14px',
}));
}
}, [isCollapsed, isVisible]);
const handleToggleCollapse = (e: React.MouseEvent) => {
e.stopPropagation();
setIsCollapsed(prev => !prev);
};
if (!isVisible) {
return null;
}
return (
<div style={badgeStyles} onClick={isCollapsed ? handleToggleCollapse : undefined}>
<button
onClick={handleToggleCollapse}
style={{
position: 'absolute',
top: isCollapsed ? '3px' : '5px',
right: isCollapsed ? '2px' : '5px',
background: 'none',
border: 'none',
color: 'white',
fontSize: isCollapsed ? '10px' : '18px',
cursor: 'pointer',
padding: '2px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
lineHeight: '1',
width: '24px',
height: isCollapsed ? '24px' : '24px',
borderRadius: '50%',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
transition: 'background-color 0.2s ease, font-size 0.2s ease, width 0.2s ease, height 0.2s ease',
}}
aria-label={isCollapsed ? "Expand message" : "Collapse message"}
>
{isCollapsed ? '+' : '×'}
</button>
{!isCollapsed && (
<div style={{ marginRight: '20px' }}>
{fullText}
</div>
)}
{isCollapsed && (
<div style={{ marginRight: '10px' }}>
{collapsedText}
</div>
)}
</div>
);
};
export default DevModeBadge;