A coding challenge solution demonstrating best practices in code organization, readability, and maintainability through a production-ready recruitment platform.
Objective: Create an admin page that displays a list of job candidates with the ability to filter by first and last name.
This solution showcases:
- Clean, maintainable code architecture
- Scalable system design aligned with product goals
- Integration with AWS AppSync GraphQL API
- Production-ready deployment with Docker
- Node.js 18+
- Docker Desktop (for containerized development)
- β React 19 - Modern component-based architecture with hooks
- β Apollo Client 4 - GraphQL integration with AWS AppSync endpoint
- β AWS AppSync - Managed GraphQL API with introspection enabled
- β Table Display - Responsive candidate list with clean UI
- β Search Functionality - Real-time filtering by first and last name
- Next.js 15 - React framework with App Router and Server Components for improved performance
- TypeScript 5.8 - Strict mode enabled for type safety and maintainability
- @apollo/client-integration-nextjs - Official Next.js 13+ integration for SSR support
- Tailwind CSS 4 + shadcn/ui - Production-ready styling with accessible component library
- NextAuth.js v5 + PostgreSQL + Drizzle ORM - Secure admin panel with role-based access control
- ESLint 9 + Prettier - Enforced code standards and consistent formatting
- Docker - Containerized development and production deployment
- π Table Display - Clean, responsive candidate list with sortable columns
- π Search Filter - Real-time filtering by first and last name with debouncing (case-sensitive)
- π GraphQL Integration - AWS AppSync endpoint with proper authorization
- βοΈ React Implementation - Modern hooks-based architecture
- π Authentication System - Secure login with NextAuth.js and session management
- π³ Docker Deployment - Production-ready containerized setup
- π Secure API Keys - GraphQL API keys concealed server-side with Next.js SSR, never exposed to client
-
Clone the repository
git clone https://github.com/skillwork-mdimitrov/hr-admin-panel-challenge.git cd hr-admin-panel-challenge -
Install Docker Desktop
-
Set up environment variables
cd frontend cp .env-example .env -
Generate authentication secret
npx auth secret
- Copy the generated secret from the newly created
.env.localfile - Paste it into
.envasAUTH_SECRET="your-secret-here" β οΈ Remove any extra newlines in the.envfile if needed
- Copy the generated secret from the newly created
-
Replace API_KEY with the Doppler shared secret
- Open the
.envfile - Replace the
API_KEYvalue with the Doppler shared secret
- Open the
-
Start Docker Desktop
- Make sure Docker Desktop is running before proceeding
- You should see the Docker icon in your system tray
-
Build and start containers
docker compose build --no-cache frontend docker compose up -d frontend
-
Seed the database (creates default admin user)
docker compose exec frontend pnpm exec tsx scripts/seed-runner.ts
-
Access the application
- Open http://localhost:3000
- Login with:
- Username:
admin - Password:
password
- Username:
-
Install dependencies locally (for IDE IntelliSense and ESLint)
npm install -g pnpm@latest-10
pnpm installAvailable as npm scripts in frontend/package.json:
# Stop containers (preserves data)
docker compose rm -s -f frontend
# Stop and remove all data (β οΈ DESTRUCTIVE)
docker compose down -v
# Rebuild containers
docker compose build --no-cache frontend
# Force recreate containers
docker compose up -d --force-recreate frontend
# Seed database
docker compose exec frontend pnpm exec tsx scripts/seed-runner.tsConnect to PostgreSQL for direct database access:
- Host:
localhost - Port:
5434 - Database:
frontend - User:
postgres - Password: (from
.envfile)
- Docker errors on Windows: Ensure line endings are set to CRLF
- Port conflicts: Make sure ports 3000 and 5434 are available
- Auth secret issues: Ensure no extra newlines in
.envfile
- Single Responsibility: Each component has one clear purpose
- DRY Principle: Shared logic extracted into hooks and utilities
- Readable Code: Clear naming conventions and inline documentation
- Maintainable: Logical file structure and consistent patterns
- Endpoint: AWS AppSync GraphQL endpoint (configured via environment variables)
- Authorization:
x-api-keyheader authentication - Introspection: Enabled for type-safe development
query GetCandidates($filter: ModelCandidateFilterInput) {
listCandidates(filter: $filter) {
items {
id
firstName
lastName
email
phoneNumber
status
score
createdAt
updatedAt
}
nextToken
}
}The search functionality leverages GraphQL's filtering capabilities:
// Debounced search input (500ms delay)
const debouncedSearch = useDebounce(searchInput, 500);
// Build GraphQL filter with OR condition
const filter = debouncedSearch.trim()
? {
or: [
{ firstName: { contains: debouncedSearch.trim() } },
{ lastName: { contains: debouncedSearch.trim() } }
]
}
: undefined;
// Apollo Client query with variables
const { data, error, networkStatus } = useQuery(GET_CANDIDATES, {
variables: { filter },
notifyOnNetworkStatusChange: true,
});All GraphQL operations are fully typed using TypedDocumentNode:
export const GET_CANDIDATES: TypedDocumentNode<
CandidatesData,
GetCandidatesQueryVariables
> = gql`...`;π Full API Documentation: frontend/docs/graphql-schema.md
This project is configured for Docker deployment with the standalone output mode.
# Build production image
docker compose build --no-cache frontend
# Run in production mode
# (Comment out 'target: dev' in docker-compose.yaml first)
docker compose up -d frontendThis project is licensed under the MIT License - see the LICENSE file for details.
Built with attention to code quality, user experience, and scalability π






