Skip to content

feat: Telecom Technician-Job Scheduler with Gantt Chart (Spring Boot + React)#213

Open
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1775640384-telecom-gantt-scheduler-v2
Open

feat: Telecom Technician-Job Scheduler with Gantt Chart (Spring Boot + React)#213
devin-ai-integration[bot] wants to merge 2 commits into
mainfrom
devin/1775640384-telecom-gantt-scheduler-v2

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 8, 2026

Summary

Adds a full-stack telecom technician-to-job scheduling application with an interactive Gantt chart UI. This is a greenfield project added alongside the existing repo contents.

Backend (backend/): Spring Boot 3.2 + MySQL + JPA

  • JPA entities: Territory → Market → Technician, Job, Assignment
  • REST controllers with CRUD for all entities + a dedicated /api/gantt endpoint that assembles the schedule view
  • DataSeeder auto-populates demo data (3 territories, 7 markets, 8 technicians, 16 jobs, ~60 assignments) using LocalDate.now()

Frontend (frontend/): React 18 + dhtmlx-gantt

  • SearchPanel: territory dropdown → cascading market multi-select → date picker
  • GanttChart: renders technician rows with color-coded task bars (jobs, travel, breaks); supports drag-move/resize to reschedule and double-click to update status — both persist via PUT /api/assignments/{id}

Updates since last revision

  • Fixed Hibernate lazy-loading serialization error — Added @JsonIgnoreProperties({"market", "hibernateLazyInitializer", "handler"}) to Assignment.technician and Assignment.job relationships. Without this, PUT /api/assignments/{id} threw InvalidDefinitionException for ByteBuddyInterceptor when serializing the response.
  • Stabilized market filter re-renders — Converted the marketIds array prop to a stable string key (marketIds.join(',')) in the GanttChart useEffect dependency array. Previously, React's reference comparison on the array caused the effect to not re-fire correctly when switching market selections.

Demo Recording

Telecom Gantt Scheduler Demo

View original video (rec-be5932a5cbc14f1a97372b3f7a26de2c-edited.mp4)

Tested: Ohio territory (6 technicians), Columbus market filter (3 technicians), Texas territory switch (2 technicians), drag-to-reschedule with backend persistence.

Review & Testing Checklist for Human

  • Hardcoded DB credentialsapplication.properties contains techjob/techjob123 in plaintext. Acceptable for local demo only; must be externalized (env vars / Spring profiles) before any shared deployment.
  • NullPointerException risk in controllersTechnicianController.create, JobController.create, MarketController.create cast Map<String, Object> values via .toString() with no null checks. Send a payload missing a required field and verify the error response is reasonable.
  • JPA lazy-loading on Technician endpoints — The Assignment entity serialization is now fixed, but Technician.market is still LAZY with FetchType.EAGER only on the Assignment side. Test GET /api/technicians directly and verify no LazyInitializationException occurs.
  • End-to-end test — Start MySQL, run mvn spring-boot:run in backend/, run npm start in frontend/, select Ohio territory, pick today's date, click Search, and verify the Gantt chart renders with technician rows and colored task bars. Drag a task bar and confirm the change persists on page refresh.

Notes

  • No automated tests (unit or integration) are included for either backend or frontend.
  • The DataSeeder seeds assignments for LocalDate.now() only — the Gantt chart will appear empty for any other date.
  • Status update UX uses window.prompt() — functional but not production-quality.
  • CORS is hardcoded to http://localhost:3000; will need adjustment for any non-local deployment.
  • This project is added to the app_dotnet-angular-microservices repo as a separate backend/ + frontend/ tree; it has no relationship to the existing .NET/Angular codebase.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/52bccc96e664447f943e6b972a8a0c71


Open with Devin

- Spring Boot 3.2 backend with MySQL, JPA entities for Territory, Market, Technician, Job, Assignment
- REST APIs for CRUD operations on all entities
- Gantt chart API endpoint for technician-job schedule data
- React 18 frontend with dhtmlx-gantt for interactive Gantt chart
- Territory/market search panel with date picker
- Drag-and-drop rescheduling with backend persistence
- Color-coded task types (job types, travel, breaks)
- Auto-seeded demo data for Ohio, Texas, California territories
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

… market filter

- Add @JsonIgnoreProperties on Assignment entity relationships to prevent
  ByteBuddyInterceptor serialization errors when updating assignments via API
- Convert marketIds array to stable string key in GanttChart useEffect
  dependency to prevent unnecessary re-renders
Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +39 to +41
} else {
assignments = assignmentRepository.findByAssignmentDate(date);
technicians = new ArrayList<>();
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 GanttService returns empty data when no territory/market filter despite fetching assignments

In GanttService.getGanttData, the else branch (lines 39-41) fetches assignments from the database via findByAssignmentDate(date), but sets technicians to an empty ArrayList. Since the loop at line 50 iterates only over technicians to build the response, no Gantt tasks are ever produced — the fetched assignments are silently discarded. The API endpoint at GanttController.java:23-24 allows both territoryId and marketIds to be omitted (required = false), so this branch is reachable. Any caller hitting /api/gantt?date=... without filters receives an empty response even when assignments exist for that date.

Prompt for agents
In GanttService.getGanttData, the else branch (lines 39-41) fetches assignments via findByAssignmentDate(date) but sets technicians to an empty list. Since the loop at line 50 iterates only over technicians to build the result, no tasks are ever produced from those assignments.

The fix should either:
1. Also fetch all technicians in the else branch (e.g., technicianRepository.findAll()) so assignments are actually rendered, OR
2. Derive the technician list from the fetched assignments (extracting unique technicians from the assignment list), OR
3. Return an empty response intentionally — but then the assignments should not be fetched either, to avoid unnecessary DB queries.

Option 2 would be the most robust as it ensures the technician list is always consistent with the assignments fetched.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

const [markets, setMarkets] = useState([]);
const [selectedTerritory, setSelectedTerritory] = useState('');
const [selectedMarkets, setSelectedMarkets] = useState([]);
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Default date uses UTC instead of local timezone, showing wrong date for US users in evening

At SearchPanel.js:9, the default date is computed as new Date().toISOString().split('T')[0]. toISOString() converts to UTC, so for users in negative UTC offset timezones (e.g., US Eastern at UTC-5, Pacific at UTC-8), the default date will be tomorrow's date during evening/night hours. For example, a user in US Eastern at 8 PM on April 1 would see April 2 as the default date, since 8 PM EDT = midnight UTC April 2. This is a telecom field supervisor tool likely used in US timezones, so this will regularly show the wrong default date.

Suggested change
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
const [selectedDate, setSelectedDate] = useState(() => {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
});
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants