Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
18 changes: 18 additions & 0 deletions .github/workflows/build-and-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,27 @@ on:
- main
pull_request:
jobs:
lint-and-typecheck:
name: Lint and Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run type-check
run: npm run type-check
- name: Run lint
run: npm run lint
build-and-push-docker-image:
name: Build Docker image and publish
runs-on: ubuntu-latest
needs: lint-and-typecheck
steps:
- name: Checkout code
uses: actions/checkout@v3
Expand Down
133 changes: 133 additions & 0 deletions TYPESCRIPT_MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# TypeScript Migration Guide

This project has been migrated to TypeScript with Redux Toolkit. This document provides guidance for developers working with the new Redux setup.

## Redux Toolkit Store Structure

The Redux store is now managed using Redux Toolkit with the following structure:

```
src/
store/
index.ts # Store configuration
hooks.ts # Typed hooks (useAppSelector, useAppDispatch)
slices/
appSlice.ts # App state (search, filters, terms)
coursesSlice.ts # Course data and search
instructorsSlice.ts # Instructor data and search
subjectsSlice.ts # Subject data and search
gradesSlice.ts # Grade distributions
exploreSlice.ts # Explore page data
```

## Using TypeScript with Redux

### Typed Hooks

Use the typed hooks instead of plain `useDispatch` and `useSelector`:

```typescript
import { useAppDispatch, useAppSelector } from './store/hooks';

function MyComponent() {
const dispatch = useAppDispatch();
const courses = useAppSelector((state) => state.courses);
// ...
}
```

### Dispatching Actions

All async actions use `createAsyncThunk`:

```typescript
import { fetchCourse } from './store/slices/coursesSlice';

function MyComponent() {
const dispatch = useAppDispatch();

useEffect(() => {
dispatch(fetchCourse('some-uuid'));
}, [dispatch]);
}
```

### Accessing State with Selectors

Use the exported selectors from each slice:

```typescript
import { selectCourse } from './store/slices/coursesSlice';
import { useAppSelector } from './store/hooks';

function CourseDetails({ uuid }) {
const course = useAppSelector(selectCourse(uuid));
// ...
}
```

## TypeScript Configuration

### Strict Mode

The project uses strict TypeScript settings:
- `strict: true`
- `noImplicitAny: true`
- `strictNullChecks: true`
- All strict checks enabled

### Type Checking

Run type checking:
```bash
npm run type-check
```

### Linting

Run ESLint:
```bash
npm run lint
```

Note: The lint configuration currently allows `any` types as warnings during the migration. These should be gradually replaced with proper types.

## CI/CD Integration

The GitHub Actions workflow now includes:
1. **Type checking** - Ensures no TypeScript errors
2. **Linting** - Checks code quality and style
3. **Build** - Builds the project

All checks must pass before merging PRs.

## Migration Status

### Completed
- ✅ All Redux slices migrated to TypeScript
- ✅ Redux Toolkit with `createAsyncThunk` for all async operations
- ✅ Typed `RootState` and `AppDispatch`
- ✅ Typed hooks (`useAppSelector`, `useAppDispatch`)
- ✅ CI/CD pipeline with type-check and lint
- ✅ Build system configured for TypeScript

### Future Work
- Migrate React components to TypeScript
- Add proper types for API responses
- Replace `any` types with proper interfaces
- Add unit tests for slices
- Consider RTK Query for data fetching

## Best Practices

1. **Always use typed hooks** - Never use plain `useDispatch` or `useSelector`
2. **Use selectors** - Export and use selector functions from slices
3. **Type your components** - As you touch components, convert them to TypeScript
4. **Avoid `any`** - Use proper types or create interfaces for complex data
5. **Test your changes** - Run `npm run type-check` and `npm run build` before committing

## Resources

- [Redux Toolkit Documentation](https://redux-toolkit.js.org/)
- [TypeScript Documentation](https://www.typescriptlang.org/)
- [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/)
65 changes: 65 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';

export default tseslint.config(
{ ignores: ['build', 'node_modules'] },
{
extends: [js.configs.recommended],
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
'react-hooks': reactHooks,
},
rules: {
...reactHooks.configs.recommended.rules,
},
},
{
extends: [js.configs.recommended, ...tseslint.configs.strictTypeChecked],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unsafe-assignment': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',
'@typescript-eslint/no-invalid-void-type': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
},
},
);
Loading