Skip to content
Open
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 .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(curl -I http://localhost:3000)"
],
"deny": [],
"ask": []
}
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,7 @@ Questions? Contact Mike Mitchell (Product Owner)
## License

UNLICENSED - Equal Experts Internal Project

## group 8

Pushing commit as test
19 changes: 19 additions & 0 deletions apps/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,25 @@
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500&display=swap" rel="stylesheet">
<script>
// Prevent FOUC (Flash of Unstyled Content) by applying theme before React loads
(function() {
try {
const storedScheme = localStorage.getItem('nam-survey-color-scheme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const colorScheme = storedScheme || (systemPrefersDark ? 'dark' : 'light');

// Apply data-mantine-color-scheme attribute to document element
document.documentElement.setAttribute('data-mantine-color-scheme', colorScheme);
} catch (e) {
// localStorage might be unavailable in private browsing mode
// Fallback to system preference
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-mantine-color-scheme', 'dark');
}
}
})();
</script>
</head>
<body>
<div id="root"></div>
Expand Down
68 changes: 68 additions & 0 deletions apps/frontend/src/components/ThemeToggle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ActionIcon, useMantineColorScheme } from '@mantine/core';
import { IconSun, IconMoon } from '@tabler/icons-react';

// Helper function to announce changes to screen readers
function announceToScreenReader(message: string): void {
const announcement = document.createElement('div');
announcement.setAttribute('role', 'status');
announcement.setAttribute('aria-live', 'polite');
announcement.setAttribute('aria-atomic', 'true');
announcement.style.position = 'absolute';
announcement.style.left = '-10000px';
announcement.style.width = '1px';
announcement.style.height = '1px';
announcement.style.overflow = 'hidden';
announcement.textContent = message;

document.body.appendChild(announcement);

setTimeout(() => {
document.body.removeChild(announcement);
}, 1000);
}

export const ThemeToggle: React.FC = () => {
const { colorScheme, setColorScheme } = useMantineColorScheme();
const isDark = colorScheme === 'dark';

const handleToggle = () => {
const newScheme = isDark ? 'light' : 'dark';
setColorScheme(newScheme);

// Persist to localStorage
localStorage.setItem('nam-survey-color-scheme', newScheme);

// Announce to screen readers
const announcement = newScheme === 'dark' ? 'Dark mode enabled' : 'Light mode enabled';
announceToScreenReader(announcement);
};

return (
<ActionIcon
onClick={handleToggle}
size="lg"
variant="subtle"
aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
aria-pressed={isDark}
title={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
style={{
transition: 'transform 0.2s ease',
}}
onMouseDown={(e) => {
e.currentTarget.style.transform = 'scale(0.95)';
}}
onMouseUp={(e) => {
e.currentTarget.style.transform = 'scale(1)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = 'scale(1)';
}}
>
{isDark ? (
<IconSun size={20} stroke={1.5} />
) : (
<IconMoon size={20} stroke={1.5} />
)}
</ActionIcon>
);
};
58 changes: 58 additions & 0 deletions apps/frontend/src/contexts/ThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useEffect, useRef } from 'react';
import { MantineProvider, MantineColorScheme, useMantineColorScheme } from '@mantine/core';
import { useColorScheme } from '@mantine/hooks';
import { theme } from '../theme/theme';

interface ThemeProviderProps {
children: React.ReactNode;
}

// Inner component that has access to Mantine's color scheme
const ThemeSync: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const systemColorScheme = useColorScheme();
const { setColorScheme } = useMantineColorScheme();
const isInitialized = useRef(false);

// Initialize color scheme on mount only
useEffect(() => {
if (!isInitialized.current) {
const storedScheme = localStorage.getItem('nam-survey-color-scheme');
const initialScheme = (storedScheme === 'light' || storedScheme === 'dark')
? storedScheme
: systemColorScheme;

setColorScheme(initialScheme);
isInitialized.current = true;
}
}, [setColorScheme, systemColorScheme]);

return <>{children}</>;
};

export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
return (
<MantineProvider theme={theme}>
<ThemeSync>{children}</ThemeSync>
</MantineProvider>
);
};

// Helper function to announce changes to screen readers
function announceToScreenReader(message: string): void {
const announcement = document.createElement('div');
announcement.setAttribute('role', 'status');
announcement.setAttribute('aria-live', 'polite');
announcement.setAttribute('aria-atomic', 'true');
announcement.style.position = 'absolute';
announcement.style.left = '-10000px';
announcement.style.width = '1px';
announcement.style.height = '1px';
announcement.style.overflow = 'hidden';
announcement.textContent = message;

document.body.appendChild(announcement);

setTimeout(() => {
document.body.removeChild(announcement);
}, 1000);
}
7 changes: 3 additions & 4 deletions apps/frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { MantineProvider } from '@mantine/core';
import { Notifications } from '@mantine/notifications';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import { theme } from './theme/theme';
import { ThemeProvider } from './contexts/ThemeContext';

import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<MantineProvider theme={theme}>
<ThemeProvider>
<Notifications position="top-right" />
<BrowserRouter>
<App />
</BrowserRouter>
</MantineProvider>
</ThemeProvider>
</React.StrictMode>
);
34 changes: 20 additions & 14 deletions apps/frontend/src/pages/AdminDashboardPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useState, useEffect } from 'react';
import { Container, Stack, Title, SimpleGrid, Alert, Image } from '@mantine/core';
import { Container, Stack, Title, SimpleGrid, Alert, Image, Box } from '@mantine/core';
import { IconAlertCircle } from '@tabler/icons-react';
import { MetricCard } from '../components/MetricCard';
import { RecentResponsesSection } from '../components/RecentResponsesSection';
import { ThemeToggle } from '../components/ThemeToggle';
import { getAdminMetrics, getAdminRecentResponses } from '../api/admin';
import { AdminMetricsResponse, AdminRecentResponsesResponse } from '../types/admin';

Expand Down Expand Up @@ -40,19 +41,24 @@ export default function AdminDashboardPage() {
return (
<Container size="lg" py="xl">
<Stack gap="xl">
{/* Header with Logo */}
<Stack gap="md" align="center">
<Image
src="https://www.equalexperts.com/wp-content/uploads/2024/10/2024-Logo.svg"
alt="Equal Experts Logo"
h={60}
w="auto"
fit="contain"
/>
<Title order={1} ta="center" c="#22567c">
Admin Dashboard
</Title>
</Stack>
{/* Header with Logo and Theme Toggle */}
<Box pos="relative">
<Box pos="absolute" top={0} right={0}>
<ThemeToggle />
</Box>
<Stack gap="md" align="center">
<Image
src="https://www.equalexperts.com/wp-content/uploads/2024/10/2024-Logo.svg"
alt="Equal Experts Logo"
h={60}
w="auto"
fit="contain"
/>
<Title order={1} ta="center" c="#22567c">
Admin Dashboard
</Title>
</Stack>
</Box>

{/* Error State */}
{error && (
Expand Down
38 changes: 23 additions & 15 deletions apps/frontend/src/pages/SurveyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import {
Loader,
Center,
Text,
Group,
Box,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconAlertCircle, IconCheck } from '@tabler/icons-react';
import { ProgressIndicator } from '../components/ProgressIndicator';
import { QuestionRenderer } from '../components/QuestionRenderer';
import { ThemeToggle } from '../components/ThemeToggle';
import { SurveyFormState } from '../types/survey';
import { submitSurvey } from '../api/survey';
import { SURVEY_QUESTIONS, TOTAL_QUESTIONS } from '../config/survey-questions';
Expand Down Expand Up @@ -141,21 +144,26 @@ export default function SurveyPage() {
return (
<Container size="md" py="xl">
<Stack gap="xl">
{/* Header */}
<Stack align="center" gap="md">
<Image
src="https://www.equalexperts.com/wp-content/uploads/2024/10/2024-Logo.svg"
alt="Equal Experts"
h={60}
w="auto"
/>
<Title order={1} ta="center" c="equalBlue.4">
NAM Conference Survey
</Title>
<Text size="lg" ta="center" c="dimmed">
Your feedback helps us improve future conferences. All questions are optional.
</Text>
</Stack>
{/* Header with Theme Toggle */}
<Box pos="relative">
<Box pos="absolute" top={0} right={0}>
<ThemeToggle />
</Box>
<Stack align="center" gap="md">
<Image
src="https://www.equalexperts.com/wp-content/uploads/2024/10/2024-Logo.svg"
alt="Equal Experts"
h={60}
w="auto"
/>
<Title order={1} ta="center" c="equalBlue.4">
NAM Conference Survey
</Title>
<Text size="lg" ta="center" c="dimmed">
Your feedback helps us improve future conferences. All questions are optional.
</Text>
</Stack>
</Box>

{/* Progress Indicator */}
<ProgressIndicator answered={calculateAnswered()} total={TOTAL_QUESTIONS} />
Expand Down
42 changes: 24 additions & 18 deletions apps/frontend/src/pages/ThankYouPage.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import { Container, Stack, Title, Text, Button, Image, Card } from '@mantine/core';
import { Container, Stack, Title, Text, Button, Image, Card, Box } from '@mantine/core';
import { ThemeToggle } from '../components/ThemeToggle';

export default function ThankYouPage() {
return (
<Container size="sm" py="xl">
<Card shadow="md" padding="xl" radius="md" withBorder>
<Stack align="center" gap="xl">
<Image
src="https://www.equalexperts.com/wp-content/uploads/2024/10/2024-Logo.svg"
alt="Equal Experts"
h={60}
w="auto"
/>
<Box pos="relative">
<Box pos="absolute" top={0} right={0}>
<ThemeToggle />
</Box>
<Stack align="center" gap="xl">
<Image
src="https://www.equalexperts.com/wp-content/uploads/2024/10/2024-Logo.svg"
alt="Equal Experts"
h={60}
w="auto"
/>

<Title order={1} ta="center" c="equalBlue.4">
Thank You!
Expand All @@ -25,16 +30,17 @@ export default function ThankYouPage() {
appreciate you taking the time to share your thoughts.
</Text>

<Button
variant="filled"
color="equalBlue"
size="lg"
component="a"
href="https://www.equalexperts.com"
>
Return to Equal Experts
</Button>
</Stack>
<Button
variant="filled"
color="equalBlue"
size="lg"
component="a"
href="https://www.equalexperts.com"
>
Return to Equal Experts
</Button>
</Stack>
</Box>
</Card>
</Container>
);
Expand Down
15 changes: 14 additions & 1 deletion apps/frontend/src/theme/theme.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MantineThemeOverride, createTheme } from '@mantine/core';
import { MantineThemeOverride, createTheme, MantineColorsTuple } from '@mantine/core';

export const theme: MantineThemeOverride = createTheme({
// Primary color: Equal Experts Blue
Expand Down Expand Up @@ -42,6 +42,19 @@ export const theme: MantineThemeOverride = createTheme({
'#121415', // 8
'#090a0a', // 9
],
// Dark mode color palette (Mantine standard dark colors with adjustments)
dark: [
'#C1C2C5', // 0 - lightest text
'#A6A7AB', // 1
'#909296', // 2
'#5C5F66', // 3
'#373A40', // 4 - borders
'#2C2E33', // 5
'#25262B', // 6 - secondary backgrounds
'#1A1B1E', // 7 - main background
'#141517', // 8
'#101113', // 9 - darkest
] as MantineColorsTuple,
},

// Typography
Expand Down
Loading