740 lines
38 KiB
Markdown
740 lines
38 KiB
Markdown
# SR System — Feature Implementation Plan
|
|
|
|
> Analysis date: 2026-05-26
|
|
> Scope: Calendar, Notifications, User Profile, ADMIN/MANAGER Management
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
| Feature | Existing Backend | New Backend | Frontend | Priority | Effort |
|
|
|---------|:---:|:---:|:---:|:---:|:---:|
|
|
| Calendar | **None** | Calendar aggregation endpoint | CalendarPage + CalendarWidget | P2 | 5d |
|
|
| Notifications | **Full** (read, mark-read, unread count) | Add `link_url` field to Notification entity | NotificationDropdown | P1 | 2d |
|
|
| User Profile | **Partial** (getMe, portfolio, dispatch-history) | ChangePassword endpoint + Extended EmployeeSummary | ProfilePage | P1 | 4d |
|
|
| ADMIN/MANAGER | **Partial** (employee CRUD, report listing) | Dispatch CRUD, CommonCode CRUD, Report summary aggregations | EmployeeEditModal, DispatchManager, ReportDashboard, SettingsPage | P1 (Reports), P2 (Dispatch), P2 (Settings) | 8d total |
|
|
|
|
**Total estimated effort: 19 days** (full-time, single dev). Parallel work possible — Admin features and Profile can be developed concurrently with Calendar and Notifications.
|
|
|
|
---
|
|
|
|
## 1. Calendar Feature
|
|
|
|
### 1.1 Current State Analysis
|
|
|
|
| Aspect | Status |
|
|
|--------|--------|
|
|
| Header Calendar icon | Route: `/issues` (no calendar relevance) |
|
|
| Backend calendar endpoint | **Does not exist** |
|
|
| Data with dates | `Ticket.dueDate`, `Report.targetDate`, `Project.startDate/endDate`, `DispatchHistory.startDate/endDate` |
|
|
| Frontend calendar page | **Does not exist** |
|
|
|
|
The Header component (line 65):
|
|
```tsx
|
|
<button onClick={() => router.push('/issues')} ...>
|
|
<Calendar className="w-5 h-5" />
|
|
</button>
|
|
```
|
|
|
|
### 1.2 What's Missing
|
|
|
|
1. **Backend: Calendar aggregation endpoint** — No single endpoint to fetch events across domains. Need:
|
|
- `GET /api/v1/calendar?start=YYYY-MM-DD&end=YYYY-MM-DD` — returns unified events
|
|
- Events aggregated from: Ticket due dates, Report target dates, Dispatch start/end dates
|
|
|
|
2. **Backend: Calendar event entity** — No dedicated schedule/schedule_type entity. Two options:
|
|
- **Option A (Recommended):** Aggregation endpoint that queries existing entities (Ticket, Report, DispatchHistory) and returns a unified DTO. Zero schema change.
|
|
- **Option B:** New `Schedule` entity with schedule_type (MEETING, DEADLINE, MILESTONE) — more flexible but adds complexity.
|
|
|
|
3. **Frontend: Calendar page** (`/app/(main)/calendar/page.tsx`) — Full calendar view with month/week/day modes.
|
|
|
|
4. **Frontend: Calendar event types** — Ticket due dates, report deadlines, dispatch milestones.
|
|
|
|
### 1.3 Design Proposal
|
|
|
|
#### 1.3.1 Backend: Calendar Aggregation Endpoint
|
|
|
|
**New DTO: `CalendarEventDto`**
|
|
```java
|
|
public record CalendarEventDto(
|
|
String eventType, // TICKET_DUE, REPORT_DUE, DISPATCH_START, DISPATCH_END
|
|
Long sourceId, // ticketId / reportId / dispatchId
|
|
String title,
|
|
String description,
|
|
LocalDate eventDate,
|
|
String color, // semantic color per type
|
|
String linkUrl // navigation URL (/issues/123, /work-reports/456, etc.)
|
|
) {}
|
|
```
|
|
|
|
**New Controller Method (in a new `CalendarController` or added to an existing controller):**
|
|
```java
|
|
@GetMapping("/api/v1/calendar")
|
|
@PreAuthorize("hasAnyRole('EMPLOYEE','MANAGER','ADMIN')")
|
|
ResponseEntity<ApiResponse<List<CalendarEventDto>>> getCalendar(
|
|
@RequestParam @DateTimeFormat(iso=ISO.DATE) LocalDate start,
|
|
@RequestParam @DateTimeFormat(iso=ISO.DATE) LocalDate end,
|
|
@CurrentUser Employee currentUser)
|
|
```
|
|
|
|
**New Service: `CalendarService`**
|
|
- Queries `TicketRepository.findByDueDateBetweenAndRequester_EmpId(start, end, empId)` (for employee) or all tickets (for manager/admin)
|
|
- Queries `ReportRepository.findByTargetDateBetweenAndUser_EmpId(start, end, empId)` or all
|
|
- Queries `DispatchHistoryRepository.findByStartDateBetween(start, end)` (only active, DISPATCHED)
|
|
- Merges results into `List<CalendarEventDto>`
|
|
|
|
**Event color mapping:**
|
|
| Event Type | Color | Icon |
|
|
|-----------|-------|------|
|
|
| TICKET_DUE | `#3B82F6` (blue) | Ticket |
|
|
| REPORT_DUE | `#10B981` (green) | FileText |
|
|
| DISPATCH_START | `#F59E0B` (amber) | Play |
|
|
| DISPATCH_END | `#EF4444` (red) | Stop |
|
|
|
|
#### 1.3.2 Frontend: Calendar Page
|
|
|
|
**Route:** `/calendar`
|
|
|
|
**Design:**
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ Calendar ◀ 2026.05 ▶ Today │
|
|
├──┬────┬────┬────┬────┬────┬────┐ │
|
|
│S │ M │ T │ W │ T │ F │ S │ │
|
|
├──┼────┼────┼────┼────┼────┼────┤ │
|
|
│ │ │ │ │ │ 1 │ 2 │ │
|
|
│ │ │ │ │ │ ● │ │ │
|
|
│ │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ │
|
|
│ │ ● │ ● │ │ ■ │ │ │ │
|
|
│ │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ │
|
|
│ │ │ ▲ │ ● │ │ │ ■ │ │
|
|
│ │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ │
|
|
│ │ │ │ ● │ │ │ │ │
|
|
│ │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ │
|
|
│ │ │ │ ■ │ │ │ │ │
|
|
├──┴────┴────┴────┴────┴────┴────┤ │
|
|
│ Legend: ● Report Due ■ Dispatch ▲ Ticket Due │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**Components to create:**
|
|
- `src/app/(main)/calendar/page.tsx` — Main calendar page
|
|
- `src/components/calendar/CalendarGrid.tsx` — Month grid view (no external library — custom CSS Grid, 7-column)
|
|
- `src/components/calendar/CalendarEvent.tsx` — Event chip with color dot + title (truncated)
|
|
- `src/components/calendar/EventDetailPanel.tsx` — Slide-over or modal showing event details
|
|
- `src/components/calendar/CalendarWidget.tsx` — Mini calendar widget for dashboard (optional, P3)
|
|
|
|
**Added to sidebar:**
|
|
```tsx
|
|
{ name: 'Calendar', href: '/calendar', icon: Calendar }
|
|
```
|
|
|
|
### 1.4 Implementation Plan — Calendar
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Create `CalendarEventDto` record | `backend/.../dto/CalendarEventDto.java` | 0.3d |
|
|
| 2 | Create `CalendarService` with aggregation logic | `backend/.../service/CalendarService.java` | 0.7d |
|
|
| 3 | Create `CalendarController` endpoint | `backend/.../controller/CalendarController.java` | 0.3d |
|
|
| 4 | Add `calendarService` to frontend services | `src/services/calendarService.ts` | 0.3d |
|
|
| 5 | Add `CalendarEvent` type to frontend types | `src/types/index.ts` | 0.2d |
|
|
| 6 | Build `CalendarGrid` component | `src/components/calendar/CalendarGrid.tsx` | 1.5d |
|
|
| 7 | Build `CalendarEvent` chip component | `src/components/calendar/CalendarEvent.tsx` | 0.5d |
|
|
| 8 | Build `EventDetailPanel` slide-over | `src/components/calendar/EventDetailPanel.tsx` | 0.5d |
|
|
| 9 | Build `/calendar` page | `src/app/(main)/calendar/page.tsx` | 0.5d |
|
|
| 10 | Update Header Calendar icon to `/calendar` | `src/components/layout/Header.tsx` | 0.1d |
|
|
| 11 | Add Calendar to Sidebar | `src/components/layout/Sidebar.tsx` | 0.1d |
|
|
| **Total** | | | **5 days** |
|
|
|
|
---
|
|
|
|
## 2. Notification System
|
|
|
|
### 2.1 Current State Analysis
|
|
|
|
| Aspect | Status |
|
|
|--------|--------|
|
|
| Backend API | ✅ Full: `GET /notifications`, `GET /notifications/unread`, `PUT /notifications/{id}/read` |
|
|
| Frontend notificationStore | ✅ Polls unread count (30s interval), fetches list, marks as read |
|
|
| Notification entity fields | `notificationId, type, title, message, isRead, createdAt` |
|
|
| Bell icon behavior | Calls `fetchNotifications()` on click but **no dropdown/modal renders** |
|
|
| Missing | `link_url` field — no way to navigate to related entity from notification |
|
|
|
|
### 2.2 What's Missing
|
|
|
|
1. **Frontend: NotificationDropdown component** — Bell click should show a dropdown listing recent notifications.
|
|
2. **Frontend: Click-to-navigate** — Clicking a notification should navigate to the relevant page (ticket detail, report, etc.).
|
|
3. **Backend: `linkUrl` field** — Notification entity has `type` but no link to the source entity.
|
|
|
|
### 2.3 Design Proposal
|
|
|
|
#### 2.3.1 Backend: Add linkUrl to Notification Entity
|
|
|
|
**Migration:**
|
|
```sql
|
|
ALTER TABLE tb_notification ADD COLUMN link_url VARCHAR(500);
|
|
```
|
|
|
|
**Entity update (`Notification.java`):**
|
|
```java
|
|
@Column(name = "link_url", length = 500)
|
|
private String linkUrl;
|
|
```
|
|
|
|
**Service update** — When creating notifications, set `linkUrl`:
|
|
| Type | linkUrl Example |
|
|
|------|----------------|
|
|
| TICKET_ASSIGNED | `/issues/123` |
|
|
| TICKET_RESOLVED | `/issues/123` |
|
|
| REPORT_APPROVED | `/work-reports/456` |
|
|
| REPORT_REJECTED | `/work-reports/456` |
|
|
| SLA_WARNING | `/issues/789` |
|
|
| EQUIPMENT_ASSIGNED | `/itam/123` |
|
|
|
|
#### 2.3.2 Frontend: Notification Dropdown
|
|
|
|
**Component: `NotificationDropdown.tsx`**
|
|
```
|
|
┌─────────────────────────────────┐
|
|
│ Notifications [x] │ ← Header: "Notifications" + close button
|
|
├─────────────────────────────────┤
|
|
│ 🔵 Ticket #123 assigned to you │ ← Unread: blue left border
|
|
│ 2 minutes ago → │
|
|
├─────────────────────────────────┤
|
|
│ ✅ Report #456 approved │ ← Read: no left border
|
|
│ 1 hour ago → │
|
|
├─────────────────────────────────┤
|
|
│ ⚠️ SLA Warning: #789 Overdue │ ← Critical: red left border
|
|
│ 3 hours ago → │
|
|
├─────────────────────────────────┤
|
|
│ │
|
|
│ View All ↗ │ ← Link to full notification page
|
|
└─────────────────────────────────┘
|
|
```
|
|
|
|
**Behavior:**
|
|
- Dropdown slides down from bell icon (absolute positioned)
|
|
- Shows latest 10 notifications
|
|
- Click notification → `markAsRead(id)` + navigate to `linkUrl`
|
|
- Click "View All" → `/notifications` page (optional, P3)
|
|
- Click outside → close dropdown
|
|
- Escape key → close dropdown
|
|
|
|
**Layout:**
|
|
- Max height: 400px, overflow-y: auto
|
|
- Each item: 12px padding, border-bottom, hover bg-gray-50
|
|
- Width: 360px
|
|
- Unread items: `bg-indigo-50/30`, blue dot (6px) left
|
|
- Categories: Icon + title (bold 13px) + time ago (gray 11px)
|
|
|
|
### 2.4 Implementation Plan — Notifications
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Add `linkUrl` column to Notification entity | `Notification.java` + migration | 0.3d |
|
|
| 2 | Update NotificationListener to set linkUrl | `backend/.../listener/NotificationListener.java` | 0.3d |
|
|
| 3 | Add `linkUrl` to frontend Notification type | `src/types/index.ts` | 0.1d |
|
|
| 4 | Build `NotificationDropdown` component | `src/components/layout/NotificationDropdown.tsx` | 1.0d |
|
|
| 5 | Integrate into Header (replace bell onClick) | `src/components/layout/Header.tsx` | 0.2d |
|
|
| 6 | Add mark-all-as-read endpoint (optional) | Backend + service | 0.2d |
|
|
| **Total** | | | **2 days** |
|
|
|
|
---
|
|
|
|
## 3. User Profile Management
|
|
|
|
### 3.1 Current State Analysis
|
|
|
|
| Aspect | Status |
|
|
|--------|--------|
|
|
| `GET /api/v1/employees/me` | ✅ Returns `EmployeeSummary` (empId, name, email, roleCode, dispatchStatus) |
|
|
| `GET /api/v1/employees/{empId}/dispatch-histories` | ✅ Returns `DispatchHistory[]` |
|
|
| `GET /api/v1/employees/{empId}/portfolio` | ✅ Returns `PortfolioResponse[]` (with reports + resolved tickets) |
|
|
| `PUT /api/v1/employees/{empId}` | ✅ Admin-only update endpoint |
|
|
| Change password endpoint | **Missing** |
|
|
| Extended profile info (phone, empStatus, joinedDate) | **Missing** from `/me` (EmployeeSummary is minimal) |
|
|
| `/profile` page | **Does not exist** |
|
|
|
|
### 3.2 What's Missing
|
|
|
|
1. **Backend: `PUT /api/v1/employees/me/password`** — Change password endpoint for current user.
|
|
2. **Backend: Extended `/me` response** — `EmployeeSummary` is too minimal. Need phone, empStatus, joinedDate, dispatchStatus label.
|
|
3. **Frontend: `/profile` page** — View/edit profile, change password, view dispatch history, view portfolio.
|
|
4. **Frontend: Profile editing** — Allow user to edit their own name, email, phone (or restrict to admin-only for certain fields).
|
|
|
|
### 3.3 Design Proposal
|
|
|
|
#### 3.3.1 Backend: Change Password Endpoint
|
|
|
|
**New DTO: `ChangePasswordRequest`**
|
|
```java
|
|
public record ChangePasswordRequest(
|
|
@NotBlank String currentPassword,
|
|
@NotBlank @Size(min=8) String newPassword
|
|
) {}
|
|
```
|
|
|
|
**New Endpoint in AuthController or EmployeeController:**
|
|
```java
|
|
@PutMapping("/api/v1/employees/me/password")
|
|
@PreAuthorize("hasAnyRole('EMPLOYEE','MANAGER','ADMIN')")
|
|
ResponseEntity<ApiResponse<Void>> changePassword(
|
|
@Valid @RequestBody ChangePasswordRequest request,
|
|
@CurrentUser Employee currentUser)
|
|
```
|
|
|
|
**Logic:**
|
|
1. Verify `currentPassword` matches stored hash
|
|
2. Encode `newPassword` and save
|
|
3. Invalidate existing tokens (optional — add `token_version` field)
|
|
|
|
#### 3.3.2 Backend: Extended EmployeeSummary
|
|
|
|
**Update `EmployeeSummary` to include:**
|
|
```java
|
|
public record EmployeeSummary(
|
|
Long empId,
|
|
String loginId,
|
|
String name,
|
|
String email,
|
|
String phone, // NEW
|
|
RoleCode roleCode,
|
|
EmpStatus empStatus, // NEW
|
|
DispatchStatus dispatchStatus,
|
|
LocalDate joinedDate, // NEW
|
|
LocalDate leftDate // NEW (nullable)
|
|
) {}
|
|
```
|
|
|
|
#### 3.3.3 Frontend: Profile Page Design
|
|
|
|
**URL:** `/profile`
|
|
|
|
**For EMPLOYEE (own profile):**
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ [Avatar] 김민수 │
|
|
│ 파견직원 · 재직중 · 파견중 │
|
|
│ │
|
|
│ ┌─ Personal Info ─────────────────────────────────┐ │
|
|
│ │ Login ID: minsu.kim │ │
|
|
│ │ Name: 김민수 │ │
|
|
│ │ Email: minsu@example.com │ │
|
|
│ │ Phone: 010-1234-5678 │ │
|
|
│ │ Joined: 2024-03-01 │ │
|
|
│ │ Status: ACTIVE (DISPATCHED) │ │
|
|
│ │ [Edit Info] [Change Password] │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─ Dispatch History ──────────────────────────────┐ │
|
|
│ │ Timeline with projects, dates, roles │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─ Portfolio Summary ─────────────────────────────┐ │
|
|
│ │ Aggregated stats: projects, tickets, reports │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**For MANAGER/ADMIN viewing another employee:**
|
|
- URL: `/profile/[empId]`
|
|
- Same layout but with employee selector/dropdown at top
|
|
- Manager can view but not edit (unless admin)
|
|
- Admin can edit fields
|
|
|
|
#### 3.3.4 Components to Create
|
|
|
|
| Component | Description |
|
|
|-----------|-------------|
|
|
| `src/app/(main)/profile/page.tsx` | Own profile view |
|
|
| `src/app/(main)/profile/[empId]/page.tsx` | View other employee's profile (MANAGER/ADMIN) |
|
|
| `src/components/profile/ProfileCard.tsx` | Personal info card with edit/change-password buttons |
|
|
| `src/components/profile/EditProfileModal.tsx` | Modal for editing personal info (admin only or self?) |
|
|
| `src/components/profile/ChangePasswordModal.tsx` | Modal with current/new/confirm password fields |
|
|
| `src/components/profile/DispatchTimeline.tsx` | Reuse timeline from /dispatch page |
|
|
| `src/components/profile/PortfolioStats.tsx` | Summary stats cards (total projects, total tickets resolved, etc.) |
|
|
|
|
### 3.4 Implementation Plan — User Profile
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Create `ChangePasswordRequest` DTO | Backend dto | 0.1d |
|
|
| 2 | Add `changePassword` to AuthService | `AuthService.java` | 0.3d |
|
|
| 3 | Add `PUT /employees/me/password` endpoint | `AuthController.java` or `EmployeeController.java` | 0.2d |
|
|
| 4 | Extend `EmployeeSummary` to include phone, empStatus, joinedDate | `EmployeeSummary.java` + `EmployeeService.toSummary()` | 0.2d |
|
|
| 5 | Add `getMeFull()` and `changePassword()` to employeeService | `src/services/employeeService.ts` | 0.2d |
|
|
| 6 | Build `ProfileCard` component | `src/components/profile/ProfileCard.tsx` | 0.5d |
|
|
| 7 | Build `ChangePasswordModal` component | `src/components/profile/ChangePasswordModal.tsx` | 0.5d |
|
|
| 8 | Build `EditProfileModal` component (admin-only for role/status changes; self-service for email/phone) | `src/components/profile/EditProfileModal.tsx` | 0.5d |
|
|
| 9 | Build `PortfolioStats` component | `src/components/profile/PortfolioStats.tsx` | 0.3d |
|
|
| 10 | Build `/profile` page | `src/app/(main)/profile/page.tsx` | 0.8d |
|
|
| 11 | Build `/profile/[empId]` page (manager/admin view) | `src/app/(main)/profile/[empId]/page.tsx` | 0.4d |
|
|
| 12 | Make user avatar/name in Header clickable → `/profile` | `src/components/layout/Header.tsx` | 0.1d |
|
|
| **Total** | | | **4 days** |
|
|
|
|
---
|
|
|
|
## 4. ADMIN/MANAGER Management Features
|
|
|
|
### 4.1 Current State Analysis
|
|
|
|
#### 4.1.1 Employee CRUD
|
|
|
|
| Operation | Backend | Frontend |
|
|
|-----------|:---:|:---:|
|
|
| List/View | ✅ `GET /employees` (MANAGER/ADMIN) | ✅ `/employees` page with table view |
|
|
| Create | ✅ `POST /employees` (ADMIN) | ✅ Modal in `/employees` page |
|
|
| Update | ✅ `PUT /employees/{id}` (ADMIN) | ❌ Only `MoreHorizontal` button with no handler |
|
|
| Delete | ✅ `DELETE /employees/{id}` (ADMIN) | ❌ No delete UI |
|
|
| Search | ✅ `GET /employees/search` | ✅ Search bar in `/employees` |
|
|
|
|
**Gap:** Edit and delete functionality missing from frontend. The `MoreHorizontal` button (line 454-456 in employees/page.tsx) is a blank `<button>` with no click handler.
|
|
|
|
#### 4.1.2 Dispatch Assignment
|
|
|
|
| Operation | Backend | Frontend |
|
|
|-----------|:---:|:---:|
|
|
| Create dispatch | ❌ No endpoint | ❌ No page |
|
|
| List dispatch by employee | ✅ `GET /employees/{id}/dispatch-histories` | ❌ Only shown in employee's own /dispatch page |
|
|
| List all dispatches | ❌ No endpoint | ❌ No management view |
|
|
| Link employee to project | ❌ No endpoint | ❌ |
|
|
|
|
**Gap:** There is NO controller/service method for creating a DispatchHistory. The DispatchHistoryService exists but only has repository calls, no create/update methods. MANAGER/ADMIN have no way to assign employees to projects.
|
|
|
|
#### 4.1.3 Report Monitoring Dashboard
|
|
|
|
| Operation | Backend | Frontend |
|
|
|-----------|:---:|:---:|
|
|
| List all reports | ✅ ReportController (MANAGER/ADMIN see all) | ✅ Works via work-reports page |
|
|
| Report review/approval | ✅ `PUT /reports/{id}/review` | ❌ No approval UI for managers |
|
|
| Pending count | ❌ No aggregate endpoint | Dashboard fetches `totalElements` from page=1 query (approximate) |
|
|
| Report statistics | ❌ No stats endpoint | ❌ |
|
|
|
|
**Gap:** Manager needs a dedicated report monitoring page showing all submitted reports in a table with quick approve/reject actions. Dashboard count is a workaround.
|
|
|
|
#### 4.1.4 System Settings (Common Codes)
|
|
|
|
| Operation | Backend | Frontend |
|
|
|-----------|:---:|:---:|
|
|
| List codes by group | ✅ `GET /common-codes/{groupCode}` | ❌ No page |
|
|
| Create/Update/Delete codes | ❌ No endpoints | ❌ |
|
|
|
|
**Gap:** Common code management is a pure ADMIN function. There are no CRUD endpoints for common codes and no settings page.
|
|
|
|
### 4.2 Design Proposals
|
|
|
|
#### 4.2.1 Employee Edit/Delete
|
|
|
|
**Frontend: `EmployeeEditModal.tsx`**
|
|
- Opens on `MoreHorizontal` click
|
|
- Edit: name, email, roleCode, empStatus, dispatchStatus, joinedDate
|
|
- Delete: Confirm dialog → soft delete
|
|
- After save → refresh employee list
|
|
|
|
**Add to `/employees` page:**
|
|
```tsx
|
|
// Replace blank MoreHorizontal button:
|
|
<button onClick={() => setEditingEmployee(emp)}>
|
|
<MoreHorizontal className="w-4 h-4" />
|
|
</button>
|
|
```
|
|
|
|
#### 4.2.2 Dispatch Management
|
|
|
|
**Backend needs:**
|
|
|
|
**New Controller: `DispatchHistoryController.java`**
|
|
```java
|
|
@PostMapping("/api/v1/dispatch-histories")
|
|
@PreAuthorize("hasAnyRole('MANAGER','ADMIN')")
|
|
ResponseEntity<ApiResponse<DispatchHistory>> create(CreateDispatchRequest request)
|
|
|
|
@PutMapping("/api/v1/dispatch-histories/{dispatchId}")
|
|
@PreAuthorize("hasAnyRole('MANAGER','ADMIN')")
|
|
ResponseEntity<ApiResponse<DispatchHistory>> update(Long dispatchId, UpdateDispatchRequest request)
|
|
|
|
@GetMapping("/api/v1/dispatch-histories")
|
|
@PreAuthorize("hasAnyRole('MANAGER','ADMIN')")
|
|
ResponseEntity<ApiResponse<Page<DispatchHistory>>> findAll(Pageable pageable, @RequestParam(required=false) Long empId, @RequestParam(required=false) Long projectId)
|
|
```
|
|
|
|
**New DTOs:**
|
|
```java
|
|
public record CreateDispatchRequest(
|
|
@NotNull Long empId,
|
|
@NotNull Long projectId,
|
|
@NotNull LocalDate startDate,
|
|
LocalDate endDate,
|
|
@NotBlank String roleTitle,
|
|
@NotNull Integer contributionRate,
|
|
@NotBlank String individualTech,
|
|
@NotBlank String performedTasks
|
|
) {}
|
|
```
|
|
|
|
**Frontend: Dispatch Management Page**
|
|
|
|
**URL:** `/dispatch/manage` (MANAGER/ADMIN only)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ Dispatch Management [+ New Dispatch] │
|
|
├─────────┬──────────┬─────────┬─────────┬────────────┤
|
|
│ Employee│ Project │ Role │ Period │ Actions │
|
|
├─────────┼──────────┼─────────┼─────────┼────────────┤
|
|
│ 김민수 │ Samsung │ Backend │ 24.03~ │ [Edit][Del]│
|
|
│ 이영희 │ LG CNS │ Frontend│ 24.06~ │ [Edit][Del]│
|
|
│ ... │ ... │ ... │ ... │ ... │
|
|
└─────────┴──────────┴─────────┴─────────┴────────────┘
|
|
```
|
|
|
|
**Create Dispatch modal:**
|
|
- Employee selector (autocomplete search)
|
|
- Project selector (dropdown from `/projects/my` or all projects)
|
|
- Date range picker
|
|
- Role title, contribution rate, tech stack, tasks
|
|
|
|
#### 4.2.3 Report Monitoring Dashboard
|
|
|
|
**URL:** `/work-reports/review` (MANAGER/ADMIN only)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ Report Review Dashboard │
|
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
│ │Submitted │ │Pending │ │Approved │ │Rejected │ │
|
|
│ │ 12 │ │ 5 │ │ 45 │ │ 3 │ │
|
|
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
|
├─────────────────────────────────────────────────────┤
|
|
│ [Filter: SUBMITTED ▼] [Date: 2026-05 ▼] [Search...]│
|
|
├─────────┬─────────┬──────────┬─────────┬────────────┤
|
|
│ Employee│ Type │ Date │ Status │ Action │
|
|
├─────────┼─────────┼──────────┼─────────┼────────────┤
|
|
│ 김민수 │ DAILY │ 05-26 │ SUBMIT │[✅][❌][👁] │
|
|
│ 이영희 │ WEEKLY │ 05-20 │ SUBMIT │[✅][❌][👁] │
|
|
│ 박철수 │ MONTHLY │ 05-01 │ SUBMIT │[✅][❌][👁] │
|
|
└─────────┴─────────┴──────────┴─────────┴────────────┘
|
|
```
|
|
|
|
**Action buttons:**
|
|
- ✅ Approve → `PUT /reports/{id}/review { status: "APPROVED" }`
|
|
- ❌ Reject → Opens rejection modal with required feedback text
|
|
- 👁 View → Navigate to report detail
|
|
|
|
**New Backend Endpoint (optional):**
|
|
```java
|
|
@GetMapping("/api/v1/reports/summary")
|
|
@PreAuthorize("hasAnyRole('MANAGER','ADMIN')")
|
|
ResponseEntity<ApiResponse<ReportSummary>> getSummary()
|
|
// Returns counts by status: { submitted, approved, rejected, draft }
|
|
```
|
|
|
|
#### 4.2.4 System Settings (Common Codes)
|
|
|
|
**Backend needs:**
|
|
|
|
**New endpoints in `CommonCodeController.java`:**
|
|
```java
|
|
@PostMapping
|
|
@PreAuthorize("hasRole('ADMIN')")
|
|
ResponseEntity<ApiResponse<CommonCode>> create(CommonCode code)
|
|
|
|
@PutMapping("/{codeId}")
|
|
@PreAuthorize("hasRole('ADMIN')")
|
|
ResponseEntity<ApiResponse<CommonCode>> update(Long codeId, CommonCode code)
|
|
|
|
@DeleteMapping("/{codeId}")
|
|
@PreAuthorize("hasRole('ADMIN')")
|
|
ResponseEntity<ApiResponse<Void>> delete(Long codeId)
|
|
```
|
|
|
|
**Frontend: Settings Page**
|
|
|
|
**URL:** `/settings` (ADMIN only)
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ System Settings │
|
|
│ ┌──────────────────────┬───────────────────────────┐ │
|
|
│ │ Common Codes │ Code Group: [Role ▼] │ │
|
|
│ │ ├ Group List │ ┌──────┬──────┬────┬────┐ │ │
|
|
│ │ │ • Role │ │ Code │ Name │Sort│ │ │ │
|
|
│ │ │ • Report Type │ ├──────┼──────┼────┼────┤ │ │
|
|
│ │ │ • Ticket Category │ │EMPLOY│파견직원│ 1 │[✏️][🗑]│ │
|
|
│ │ │ • Equipment Type │ │MANAG│본사관리│ 2 │[✏️][🗑]│ │
|
|
│ │ │ • ... │ │ADMIN│최고 │ 3 │[✏️][🗑]│ │
|
|
│ │ │ │ └──────┴──────┴────┴────┘ │ │
|
|
│ │ │ │ [+ Add Code] │ │
|
|
│ └──────────────────────┴───────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────┐ │
|
|
│ │ Other Settings │ │
|
|
│ │ □ Enable email notifications │ │
|
|
│ │ □ Enable Slack notifications │ │
|
|
│ │ Default SLA hours: [48 ▼] │ │
|
|
│ │ [Save] │ │
|
|
│ └──────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 4.3 Implementation Plan — ADMIN/MANAGER Features
|
|
|
|
#### Phase A: Report Monitoring Dashboard (P1, ~2 days)
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Add `ReportSummary` DTO + `GET /reports/summary` endpoint | Backend | 0.3d |
|
|
| 2 | Add `reportService.getSummary()` to frontend services | `src/services/reportService.ts` | 0.1d |
|
|
| 3 | Build report review dashboard page | `src/app/(main)/work-reports/review/page.tsx` | 1.0d |
|
|
| 4 | Add approve/reject inline actions | Same as above + modal | 0.4d |
|
|
| 5 | Add navigation from dashboard "Pending Reports" card to review page | `src/app/(main)/page.tsx` | 0.1d |
|
|
| 6 | Add sidebar link for MANAGER/ADMIN (conditional render) | `src/components/layout/Sidebar.tsx` | 0.1d |
|
|
|
|
#### Phase B: Employee Edit/Delete (P1, ~1.5 days, already mostly built on backend)
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Build `EmployeeEditModal` component | `src/components/employees/EmployeeEditModal.tsx` | 0.5d |
|
|
| 2 | Add delete confirmation dialog | Same component | 0.2d |
|
|
| 3 | Wire `MoreHorizontal` button to open edit modal | `src/app/(main)/employees/page.tsx` | 0.3d |
|
|
| 4 | Refresh list after edit/delete | Same page | 0.2d |
|
|
| 5 | Add employee detail view (linking to `/profile/[empId]`) | Same page | 0.3d |
|
|
|
|
#### Phase C: Dispatch Management (P2, ~3 days)
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Create `CreateDispatchRequest` + `UpdateDispatchRequest` DTOs | Backend | 0.2d |
|
|
| 2 | Create `DispatchHistoryController` with CRUD endpoints | Backend | 0.5d |
|
|
| 3 | Add `dispatchHistoryService.getAll()`, `.create()`, `.update()`, `.delete()` to frontend | `src/services/dispatchHistoryService.ts` (new) | 0.3d |
|
|
| 4 | Build dispatch management page | `src/app/(main)/dispatch/manage/page.tsx` | 1.0d |
|
|
| 5 | Build create/edit dispatch modal with employee search + project select | Same + modal component | 0.8d |
|
|
| 6 | Add sidebar link (MANAGER/ADMIN only) | `src/components/layout/Sidebar.tsx` | 0.1d |
|
|
|
|
#### Phase D: System Settings (P2, ~1.5 days)
|
|
|
|
| Step | Task | File(s) | Effort |
|
|
|------|------|---------|--------|
|
|
| 1 | Add POST/PUT/DELETE to CommonCodeController | Backend | 0.3d |
|
|
| 2 | Add create/update/delete to `commonCodeService` | `src/services/commonCodeService.ts` (new) | 0.2d |
|
|
| 3 | Build settings page with code group list + editor | `src/app/(main)/settings/page.tsx` | 1.0d |
|
|
| 4 | Add sidebar link (ADMIN only) | `src/components/layout/Sidebar.tsx` | 0.1d |
|
|
|
|
---
|
|
|
|
## 5. Consolidated File List
|
|
|
|
### 5.1 Backend — Files to Create
|
|
|
|
| File | Feature | Description |
|
|
|------|---------|-------------|
|
|
| `dto/CalendarEventDto.java` | Calendar | Calendar event aggregation DTO |
|
|
| `service/CalendarService.java` | Calendar | Aggregation logic from Ticket, Report, Dispatch |
|
|
| `controller/CalendarController.java` | Calendar | `GET /api/v1/calendar` endpoint |
|
|
| `dto/ChangePasswordRequest.java` | Profile | Password change request DTO |
|
|
| `dto/CreateDispatchRequest.java` | Dispatch | Dispatch creation request DTO |
|
|
| `dto/UpdateDispatchRequest.java` | Dispatch | Dispatch update request DTO |
|
|
| `controller/DispatchHistoryController.java` | Dispatch | Full CRUD for dispatch histories |
|
|
| `service/DispatchManagementService.java` | Dispatch | Management-level dispatch operations |
|
|
|
|
### 5.2 Backend — Files to Modify
|
|
|
|
| File | Feature | Change |
|
|
|------|---------|--------|
|
|
| `entity/Notification.java` | Notifications | Add `linkUrl` column |
|
|
| `dto/EmployeeSummary.java` | Profile | Add phone, empStatus, joinedDate, leftDate fields |
|
|
| `service/AuthService.java` | Profile | Add `changePassword()` method |
|
|
| `controller/AuthController.java` or `EmployeeController.java` | Profile | Add `PUT /employees/me/password` endpoint |
|
|
| `service/EmployeeService.java` | Profile | Update `toSummary()` to include new fields |
|
|
| `controller/ReportController.java` | Reports | Add `GET /reports/summary` endpoint |
|
|
| `controller/CommonCodeController.java` | Settings | Add POST, PUT, DELETE endpoints |
|
|
| `service/CommonCodeService.java` | Settings | Add create, update, delete methods |
|
|
|
|
### 5.3 Frontend — Files to Create
|
|
|
|
| File | Feature | Description |
|
|
|------|---------|-------------|
|
|
| `src/services/calendarService.ts` | Calendar | `GET /calendar?start=&end=` |
|
|
| `src/services/dispatchHistoryService.ts` | Dispatch | Full CRUD for dispatch histories |
|
|
| `src/services/commonCodeService.ts` | Settings | CRUD for common codes |
|
|
| `src/components/calendar/CalendarGrid.tsx` | Calendar | Monthly calendar grid component |
|
|
| `src/components/calendar/CalendarEvent.tsx` | Calendar | Event chip component |
|
|
| `src/components/calendar/EventDetailPanel.tsx` | Calendar | Event detail modal |
|
|
| `src/components/layout/NotificationDropdown.tsx` | Notifications | Bell dropdown component |
|
|
| `src/components/profile/ProfileCard.tsx` | Profile | Personal info display/edit card |
|
|
| `src/components/profile/ChangePasswordModal.tsx` | Profile | Password change modal |
|
|
| `src/components/profile/EditProfileModal.tsx` | Profile | Profile edit modal |
|
|
| `src/components/profile/PortfolioStats.tsx` | Profile | Portfolio summary stats |
|
|
| `src/components/employees/EmployeeEditModal.tsx` | Admin | Employee edit + delete modal |
|
|
| `src/components/dispatch/DispatchFormModal.tsx` | Admin | Create/edit dispatch form |
|
|
| `src/app/(main)/calendar/page.tsx` | Calendar | Calendar page |
|
|
| `src/app/(main)/profile/page.tsx` | Profile | Own profile page |
|
|
| `src/app/(main)/profile/[empId]/page.tsx` | Profile | Employee profile (manager view) |
|
|
| `src/app/(main)/work-reports/review/page.tsx` | Reports | Report review dashboard |
|
|
| `src/app/(main)/dispatch/manage/page.tsx` | Dispatch | Dispatch management page |
|
|
| `src/app/(main)/settings/page.tsx` | Settings | System settings page |
|
|
|
|
### 5.4 Frontend — Files to Modify
|
|
|
|
| File | Feature | Change |
|
|
|------|---------|--------|
|
|
| `src/types/index.ts` | All | Add `Notification.linkUrl`, `CalendarEvent`, `ReportSummary`, `CalendarEvent` types |
|
|
| `src/services/index.ts` | All | Export new services |
|
|
| `src/services/employeeService.ts` | Profile | Add `getMeFull()`, `changePassword()` |
|
|
| `src/services/reportService.ts` | Reports | Add `getSummary()` |
|
|
| `src/components/layout/Header.tsx` | Calendar + Profile + Notifications | Calendar → `/calendar`, integrate NotificationDropdown, avatar → `/profile` |
|
|
| `src/components/layout/Sidebar.tsx` | All | Add Calendar, Report Review, Settings links (with RBAC) |
|
|
| `src/app/(main)/page.tsx` | Reports | Link "Pending Reports" card to `/work-reports/review` |
|
|
| `src/app/(main)/employees/page.tsx` | Admin | Wire MoreHorizontal to edit modal |
|
|
|
|
---
|
|
|
|
## 6. Recommended Implementation Order
|
|
|
|
```
|
|
Week 1: Quick Wins
|
|
Day 1-2: Notifications (Dropdown UI + linkUrl) ← P1, ~2d
|
|
Day 3-5: User Profile (ProfilePage + ChangePassword) ← P1, ~4d
|
|
|
|
Week 2: Calendar + Report Management
|
|
Day 1-3: Employee Edit/Delete (EmployeeEditModal) ← P1, ~1.5d
|
|
Day 3-5: Report Monitoring Dashboard ← P1, ~2d
|
|
|
|
Week 3: Calendar + Dispatch
|
|
Day 1-5: Calendar (Backend aggregation + Full Calendar page) ← P2, ~5d
|
|
|
|
Week 4: Admin Features
|
|
Day 1-3: Dispatch Management (CRUD endpoints + page) ← P2, ~3d
|
|
Day 4-5: System Settings (CommonCode CRUD + Settings page) ← P2, ~1.5d
|
|
```
|
|
|
|
**Alternative — parallel by developer:**
|
|
- Dev A: Calendar (5d) + Settings (1.5d) = 6.5d
|
|
- Dev B: Notifications (2d) + Profile (4d) = 6d
|
|
- Dev C: Employee Edit (1.5d) + Report Dashboard (2d) + Dispatch (3d) = 6.5d
|
|
- **Total calendar time: ~1.5 weeks with 3 developers**
|
|
|
|
---
|
|
|
|
## 7. Risk Assessment
|
|
|
|
| Risk | Impact | Mitigation |
|
|
|------|--------|------------|
|
|
| Calendar aggregation performance with large datasets | Medium | Add DB indexes on `due_date`, `target_date`; paginate results |
|
|
| Notification polling load | Low | Already 30s interval — acceptable. Could go to 60s after 30min idle |
|
|
| Password change breaks existing sessions | Medium | Option A: Invalidate all tokens on password change. Option B: Add `tokenVersion` to user and check on JWT validation |
|
|
| DispatchHistory creation without project exists validation | Medium | Backend must validate project exists and is not completed/hold |
|
|
| Common code changes break existing dropdowns | High | Never hard-delete common codes — add `is_active` toggle instead. Frontend caches codes on load |
|
|
| Concurrent employee edits by multiple admins | Low | Add `@Version` optimistic lock to Employee entity |
|
|
|
|
---
|
|
|
|
## 8. Quick Wins (Low Effort, High Impact)
|
|
|
|
If you only have 1 day, do these:
|
|
|
|
1. **Notification Dropdown** (~2h) — Make the bell icon functional. Just build the dropdown UI, no backend changes needed. Notifications already have type/title/message.
|
|
2. **Header Calendar fix** (~5min) — Change `onClick={() => router.push('/issues')}` to `onClick={() => router.push('/calendar')}` even before building the calendar page — at least the button has a logical destination.
|
|
3. **Header avatar → /profile** (~5min) — Wrap the avatar/name in a Link to `/profile`.
|
|
4. **Employee MoreHorizontal → Edit** (~2h) — Wire the existing `PUT /employees/{id}` backend to a simple edit form.
|