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
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"permissions": {
"allow": [
"Bash(cd:*)"
"Bash(cd:*)",
"Bash(git -C \"C:\\\\Users\\\\Oling\\\\documents\\\\courseconnect\" log --oneline -20)"
]
}
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ courseconnect-c6a7b-firebase-adminsdk-dqqis-af57e2e045.json
playwright-report
test-results
playwright/.auth
playwright.accounts.json
playwright.accounts.json
nul
76 changes: 76 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

CourseConnect is a web application for managing teaching position applications (TAs, UPIs, Graders) and course information. It serves students (submit/track applications), faculty (review applications, manage courses), and department leaders (manage users/courses, upload data via spreadsheets).

## Tech Stack

- **Frontend:** React 19, Next.js 15 (App Router), TypeScript
- **UI:** Material UI (MUI) 6, Tailwind CSS 4
- **Backend:** Firebase/Firestore, Firebase Cloud Functions
- **State:** React Query (TanStack), React Context
- **Forms:** React Hook Form

## Common Commands

```bash
# Development
npm run dev # Start dev server at localhost:3000
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint

# Testing (Playwright E2E)
npm run test:e2e # Run all E2E tests
npm run test:e2e:ui # Run tests with Playwright UI
npm run test:e2e:debug # Debug tests
npx playwright show-report # View test report after failure

# Firebase Functions (from /functions directory)
npm run build # Compile TypeScript
npm run serve # Run functions locally with emulator
npm run deploy # Deploy functions to Firebase
```

## Architecture

```
src/
├── app/ # Next.js App Router pages
│ ├── admin-applications/ # Admin: manage all applications
│ ├── admincourses/ # Admin: course management
│ ├── announcements/ # Announcements feature
│ ├── applications/ # Student application submission
│ ├── courses/ # Course viewing
│ ├── dashboard/ # Main dashboard
│ ├── faculty/ # Faculty management
│ ├── users/ # Admin: user management
│ └── layout.tsx # Root layout with providers
├── components/ # Reusable React components
├── contexts/ # React contexts (Auth, Announcements)
├── firebase/ # Firebase config and utilities
├── hooks/ # Custom React hooks (useGetItems, etc.)
├── types/ # TypeScript type definitions
└── utils/ # Utility functions

functions/ # Firebase Cloud Functions (email notifications)
tests/e2e/ # Playwright E2E tests
```

## Key Patterns

- **Path alias:** Use `@/*` to import from `src/*`
- **Providers:** AuthProvider and AnnouncementsProvider wrap the app in `layout.tsx`
- **Data fetching:** React Query hooks in `src/hooks/` for Firestore operations
- **E2E test mode:** Feature flags via `NEXT_PUBLIC_E2E` env var and localStorage

## Pre-commit Hooks

Husky runs ESLint and Prettier on staged files automatically. If commits fail, check lint errors with `npm run lint`.

## Firebase Functions

Cloud Functions in `functions/src/index.ts` handle email notifications (application confirmations, status updates, faculty notifications). Uses Nodemailer with configured SMTP credentials.
38 changes: 33 additions & 5 deletions src/app/announcements/AnnouncementSections.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
/* components/DashboardSections.tsx */
import { PrimaryButton } from '@/components/Buttons/PrimaryButton';
import { NavbarItem } from '@/types/navigation';
import { Role } from '@/types/User';
import { useState } from 'react';
import { useMemo, useState } from 'react';

import AnnouncementDialog from './AnnouncementDialogue';
import AnnouncementsRow from './AnnouncementsRow';

import { usePostAnnouncement } from '@/hooks/Announcements/usePostAnnouncement';
import { useAnnouncements } from '@/contexts/AnnouncementsContext';
import { useFetchAnnouncementsForAccount } from '@/hooks/Announcements/useFetchAnnouncements';

import { Announcement } from '@/types/announcement';
import { Announcement, AudienceRole } from '@/types/announcement';

export default function AnnouncementSections({ role }: { role: Role }) {
export default function AnnouncementSections({
role,
uemail,
}: {
role: Role;
uemail: string;
}) {
const [open, setOpen] = useState(false);
const { postAnnouncement, posting, error: postError } = usePostAnnouncement();

const {
read,
unread,
loading,
loadingMore,
hasMore,
error: fetchError,
refresh,
} = useAnnouncements();
loadMore,
} = useFetchAnnouncementsForAccount({
userRole: role,
userEmail: uemail,
userDepartment: 'ECE', // TODO: make real
channel: 'inApp',
realtime: true,
});

async function handleSubmit(draft: Announcement) {
await postAnnouncement({
Expand Down Expand Up @@ -125,6 +141,18 @@ export default function AnnouncementSections({ role }: { role: Role }) {
)}
</div>
)}

{hasMore ? (
<div className="flex justify-center">
<button
className="px-3 py-2 text-sm border rounded hover:bg-gray-50 disabled:opacity-50"
onClick={() => loadMore()}
disabled={loadingMore}
>
{loadingMore ? 'Loading…' : 'Load more'}
</button>
</div>
) : null}
</div>
);
}
3 changes: 2 additions & 1 deletion src/app/announcements/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { markAnnouncementsSeen } from '@/hooks/Announcements/markAnnouncementAsS

const AnnouncementsPage: FC = () => {
const [user, role, loading, error] = useUserInfo();
const uemail = user?.email;

// prevent double-call in dev Strict Mode
const didMarkRef = useRef(false);
Expand All @@ -26,7 +27,7 @@ const AnnouncementsPage: FC = () => {

return (
<PageLayout mainTitle="Announcements" navItems={getNavItems(role)}>
<AnnouncementSections role={role} />
<AnnouncementSections role={role} uemail={uemail} />
</PageLayout>
);
};
Expand Down
14 changes: 14 additions & 0 deletions src/app/applications/applicationSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import SemesterSelect from '@/components/SemesterSelect/SemesterSelect';
import { useSemesterData } from '@/hooks/Courses/useSemesterData';
import { CoursesGrid } from '@/components/CoursesGrid/CoursesGrid';
import { type SemesterName } from '@/hooks/useSemesterOptions';
import SchoolIcon from '@mui/icons-material/School';
export default function ApplicationSections({
role,
navItems,
Expand Down Expand Up @@ -45,6 +46,19 @@ export default function ApplicationSections({
))}
</div>
</div>

<div className="mb-5">
<h1 className="text-h6 mb-3 ">Supervised Teaching</h1>
<div className="flex flex-wrap gap-6">
<DashboardCard
key={'/applications/supervised-teaching'}
icon={SchoolIcon}
label={'Supervised Teaching'}
to={'/applications/supervised-teaching'}
/>
</div>
</div>

<div>
<h1 className="text-h6 mb-3 ">Research</h1>
<p className="text-sm">No available applications at this time.</p>
Expand Down
Loading