| Layer | Technology |
|---|---|
| Frontend | React 18 + Vite + TypeScript |
| Styling | Bootstrap 5 |
| Form Validation | React Hook Form + Zod |
| HTTP Client | Axios |
| Backend | .NET 8 Web API |
| Data Access | ADO.NET (raw SQL — no ORM) |
| Database | SQL Server 2022 |
- Add a new department
- Edit department name
- Delete department (blocked if employees are assigned)
- Department ID is auto-converted to uppercase
- Add a new employee
- Edit employee details
- Delete employee
- Age is auto-calculated from date of birth (frontend live preview + backend computed property)
- Department assigned via dropdown populated from the departments list
- Form validation at every layer (HTML, Zod, .NET Model Annotations, Database Constraints)
- Toast notifications for success and error feedback
- Loading spinner while data is being fetched
- Confirm dialog before any delete action
- Reusable components — FormInput, FormSelect, PageHeader, TableSpinner, ConfirmDeleteModal
- React Router for client-side navigation
- CORS configured for local development
Pacifickode-Practicaltest/
├── Controllers/
│ ├── DepartmentController.cs
│ └── EmployeeController.cs
├── Data/
│ ├── DepartmentRepository.cs
│ └── EmployeeRepository.cs
├── Models/
│ ├── Department.cs
│ └── Employee.cs
├── Properties/
├── pacifickode-ui/ → React + Vite + TypeScript
│ ├── src/
│ │ ├── api/
│ │ │ ├── departmentApi.ts
│ │ │ └── employeeApi.ts
│ │ ├── components/
│ │ │ ├── ConfirmDeleteModal.tsx
│ │ │ ├── FormInput.tsx
│ │ │ ├── FormSelect.tsx
│ │ │ ├── Navbar.tsx
│ │ │ ├── PageHeader.tsx
│ │ │ └── TableSpinner.tsx
│ │ ├── pages/
│ │ │ ├── Departments.tsx
│ │ │ └── Employees.tsx
│ │ ├── schemas/
│ │ │ ├── departmentSchema.ts
│ │ │ └── employeeSchema.ts
│ │ ├── types/
│ │ │ └── index.ts
│ │ ├── utils/
│ │ │ └── errorHelper.ts
│ │ ├── App.tsx
│ │ ├── main.tsx
│ │ └── index.css
│ ├── .env.example
│ └── vite.config.ts
├── .gitignore
├── appsettings.json
├── appsettings.Development.json.example
├── Pacifickode-Practicaltest.csproj
├── Pacifickode-Practicaltest.slnx
├── PacificKode.sql → Database schema script
├── Program.cs
└── README.md
Make sure you have the following installed:
- Open SSMS and connect to your SQL Server instance
- Open the file
database/schema.sql - Press F5 to run the script
- This will create the
PracticalTestDBdatabase with the following:DepartmentstableEmployeestable- All constraints (PK, FK, UNIQUE, CHECK)
- Indexes for performance
-
Open
PracticalTest.APIin Visual Studio 2022 -
Copy the example config file:
appsettings.Development.json.example → appsettings.Development.json
- Open
appsettings.Development.jsonand update the connection string with your SQL Server instance name:
{
"ConnectionStrings": {
"DefaultConnection": "Server=YOUR_SERVER_NAME;Database=PracticalTestDB;Trusted_Connection=True;TrustServerCertificate=True;"
}
}To find your server name — open SSMS and check the server name shown in the connection dialog
-
Press F5 to run the API
-
Note the port number from the browser that opens (e.g.
http://localhost:5000) -
Verify the API is working by visiting:
http://localhost:5000/api/department
http://localhost:5000/api/employee
Both should return [] (empty array) if the database is empty.
- Navigate to the frontend folder:
cd pacifickode-ui- Install dependencies:
npm install- Copy the example environment file:
.env.example → .env
- Open
.envand update the API URL with the port from Step 2:
VITE_BACKEND_API_URL=http://localhost:5000/api
- Start the development server:
npm run dev- Open your browser and go to:
http://localhost:5173
| Column | Type | Constraints |
|---|---|---|
| DepartmentId | NVARCHAR(10) | PRIMARY KEY |
| DepartmentName | NVARCHAR(100) | NOT NULL |
| Column | Type | Constraints |
|---|---|---|
| EmployeeId | INT IDENTITY(1,1) | PRIMARY KEY |
| FirstName | NVARCHAR(50) | NOT NULL |
| LastName | NVARCHAR(50) | NOT NULL |
| NVARCHAR(100) | NOT NULL, UNIQUE | |
| DateOfBirth | DATE | NOT NULL |
| Salary | DECIMAL(18,2) | NOT NULL, >= 0 |
| DepartmentId | NVARCHAR(10) | FOREIGN KEY → Departments |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/department |
Get all departments |
| POST | /api/department |
Add a new department |
| PUT | /api/department/{departmentId} |
Update a department |
| DELETE | /api/department/{departmentId} |
Delete a department |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/employee |
Get all employees |
| POST | /api/employee |
Add a new employee |
| PUT | /api/employee/{employeeId} |
Update an employee |
| DELETE | /api/employee/{employeeId} |
Delete an employee |
User fills form
↓
HTML attributes → maxLength, min, max, date picker restrictions
↓
Zod schema → required, format, range, age check (18+)
↓
.NET Model Annotations → Required, StringLength, EmailAddress, Range
↓
Controller checks → duplicate Department ID, duplicate Email
↓
Database constraints → PK, FK, UNIQUE, CHECK, NOT NULL
| Variable | Description | Example |
|---|---|---|
VITE_BACKEND_API_URL |
Base URL of the .NET API | http://localhost:5000/api |
Note: All Vite environment variables must start with
VITE_to be accessible in the browser.
appsettings.Development.jsonis gitignored — never commit your connection string.envis gitignored — never commit your environment variables- Use the
.examplefiles as templates when setting up on a new machine - The
Agefield is computed — it is not stored in the database, it is calculated fromDateOfBirthon every API response - Deleting a department that has employees assigned will be blocked by the foreign key constraint
You need both servers running at the same time:
| Server | Command | URL |
|---|---|---|
| Backend | Press F5 in Visual Studio | http://localhost:5000 |
| Frontend | npm run dev |
http://localhost:5173 |