# 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 ``` ### 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>> 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` **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> 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 ` ``` #### 4.2.2 Dispatch Management **Backend needs:** **New Controller: `DispatchHistoryController.java`** ```java @PostMapping("/api/v1/dispatch-histories") @PreAuthorize("hasAnyRole('MANAGER','ADMIN')") ResponseEntity> create(CreateDispatchRequest request) @PutMapping("/api/v1/dispatch-histories/{dispatchId}") @PreAuthorize("hasAnyRole('MANAGER','ADMIN')") ResponseEntity> update(Long dispatchId, UpdateDispatchRequest request) @GetMapping("/api/v1/dispatch-histories") @PreAuthorize("hasAnyRole('MANAGER','ADMIN')") ResponseEntity>> 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> 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> create(CommonCode code) @PutMapping("/{codeId}") @PreAuthorize("hasRole('ADMIN')") ResponseEntity> update(Long codeId, CommonCode code) @DeleteMapping("/{codeId}") @PreAuthorize("hasRole('ADMIN')") ResponseEntity> 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.