Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions TELECOM_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Telecom Technician Job Scheduler

A full-stack application for managing technician-to-job assignments in the telecom domain, featuring an interactive Gantt chart for field supervisors and admins.

## Architecture

- **Backend**: Spring Boot 3.2 + MySQL + JPA
- **Frontend**: React 18 + dhtmlx-gantt

## Features

- Territory and market-based search for technician schedules
- Interactive Gantt chart showing daily technician-job mappings
- Color-coded task types: jobs (by type), travel time, breaks, return home
- Drag-and-drop to reschedule assignments
- Double-click to update job status
- Full CRUD REST APIs for all entities
- Auto-seeded demo data

## Data Model

- **Territory**: Geographic regions (Ohio, Texas, California)
- **Market**: Sub-regions within a territory (Columbus, Cleveland, Dallas)
- **Technician**: Field technicians with skills (copper, fiber, modem, router, networking)
- **Job**: Work orders with type, address, and duration
- **Assignment**: Technician-job mappings with time slots, travel time, and status

## Quick Start

### Prerequisites
- Java 17+
- Maven 3.6+
- MySQL 8.0+
- Node.js 18+

### Backend Setup

```bash
# Create MySQL database
mysql -u root -e "CREATE DATABASE techjob_gantt; CREATE USER 'techjob'@'localhost' IDENTIFIED BY 'techjob123'; GRANT ALL ON techjob_gantt.* TO 'techjob'@'localhost';"

# Build and run
cd backend
mvn spring-boot:run
```

Backend runs on http://localhost:8080

### Frontend Setup

```bash
cd frontend
npm install
npm start
```

Frontend runs on http://localhost:3000

## API Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/territories | List all territories |
| GET | /api/markets?territoryId={id} | List markets by territory |
| GET/POST/PUT/DELETE | /api/technicians | CRUD technicians |
| GET/POST/PUT/DELETE | /api/jobs | CRUD jobs |
| GET/POST/PUT/DELETE | /api/assignments | CRUD assignments |
| GET | /api/gantt?territoryId={id}&marketIds={ids}&date={date} | Gantt chart data |
56 changes: 56 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>

<groupId>com.telecom</groupId>
<artifactId>gantt-scheduler</artifactId>
<version>1.0.0</version>
<name>Telecom Gantt Scheduler</name>
<description>Technician to Job scheduling with Gantt chart for telecom domain</description>

<properties>
<java.version>17</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.telecom.gantt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GanttSchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(GanttSchedulerApplication.class, args);
}
}
26 changes: 26 additions & 0 deletions backend/src/main/java/com/telecom/gantt/config/CorsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.telecom.gantt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;

@Configuration
public class CorsConfig {

@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("http://localhost:3000"));
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowCredentials(true);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return new CorsFilter(source);
}
}
210 changes: 210 additions & 0 deletions backend/src/main/java/com/telecom/gantt/config/DataSeeder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package com.telecom.gantt.config;

import com.telecom.gantt.model.*;
import com.telecom.gantt.repository.*;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;

@Component
public class DataSeeder implements CommandLineRunner {

private final TerritoryRepository territoryRepository;
private final MarketRepository marketRepository;
private final TechnicianRepository technicianRepository;
private final JobRepository jobRepository;
private final AssignmentRepository assignmentRepository;

public DataSeeder(TerritoryRepository territoryRepository, MarketRepository marketRepository,
TechnicianRepository technicianRepository, JobRepository jobRepository,
AssignmentRepository assignmentRepository) {
this.territoryRepository = territoryRepository;
this.marketRepository = marketRepository;
this.technicianRepository = technicianRepository;
this.jobRepository = jobRepository;
this.assignmentRepository = assignmentRepository;
}

@Override
public void run(String... args) {
if (territoryRepository.count() > 0) return;

// Territories
Territory ohio = territoryRepository.save(new Territory("Ohio", "OH"));
Territory texas = territoryRepository.save(new Territory("Texas", "TX"));
Territory california = territoryRepository.save(new Territory("California", "CA"));

// Markets for Ohio
Market columbus = marketRepository.save(new Market("Columbus", "COL", ohio));
Market cleveland = marketRepository.save(new Market("Cleveland", "CLE", ohio));
Market cincinnati = marketRepository.save(new Market("Cincinnati", "CIN", ohio));

// Markets for Texas
Market dallas = marketRepository.save(new Market("Dallas", "DAL", texas));
Market houston = marketRepository.save(new Market("Houston", "HOU", texas));

// Markets for California
Market losAngeles = marketRepository.save(new Market("Los Angeles", "LA", california));
Market sanFrancisco = marketRepository.save(new Market("San Francisco", "SF", california));

// Technicians - Columbus
Technician t1 = createTechnician("TECH-001", "John Smith", "john.smith@telecom.com",
"123 Main St, Columbus, OH 43215", "copper,fiber,modem", columbus);
Technician t2 = createTechnician("TECH-002", "Sarah Johnson", "sarah.johnson@telecom.com",
"456 Oak Ave, Columbus, OH 43220", "fiber,router,networking", columbus);
Technician t3 = createTechnician("TECH-003", "Mike Davis", "mike.davis@telecom.com",
"789 Elm Rd, Columbus, OH 43201", "copper,modem,router", columbus);

// Technicians - Cleveland
Technician t4 = createTechnician("TECH-004", "Emily Brown", "emily.brown@telecom.com",
"321 Pine St, Cleveland, OH 44101", "fiber,networking", cleveland);
Technician t5 = createTechnician("TECH-005", "David Wilson", "david.wilson@telecom.com",
"654 Cedar Ln, Cleveland, OH 44102", "copper,fiber,modem,router", cleveland);

// Technicians - Cincinnati
Technician t6 = createTechnician("TECH-006", "Lisa Anderson", "lisa.anderson@telecom.com",
"987 Maple Dr, Cincinnati, OH 45201", "fiber,router,networking", cincinnati);

// Technicians - Dallas
Technician t7 = createTechnician("TECH-007", "James Taylor", "james.taylor@telecom.com",
"111 Peach St, Dallas, TX 75201", "copper,fiber,modem", dallas);
Technician t8 = createTechnician("TECH-008", "Amanda Martinez", "amanda.martinez@telecom.com",
"222 Birch Ave, Dallas, TX 75202", "fiber,router,networking", dallas);

// Jobs - Columbus
Job j1 = createJob("JOB-1001", "FIBER", "Fiber installation - residential", "100 High St, Columbus, OH 43215", 90, columbus);
Job j2 = createJob("JOB-1002", "COPPER", "Copper line repair", "200 Broad St, Columbus, OH 43215", 60, columbus);
Job j3 = createJob("JOB-1003", "MODEM", "Modem setup and configuration", "300 State St, Columbus, OH 43215", 45, columbus);
Job j4 = createJob("JOB-1004", "ROUTER", "Router installation - business", "400 Front St, Columbus, OH 43215", 60, columbus);
Job j5 = createJob("JOB-1005", "FIBER", "Fiber upgrade - residential", "500 Third St, Columbus, OH 43220", 75, columbus);
Job j6 = createJob("JOB-1006", "NETWORKING", "Network troubleshooting", "600 Fourth St, Columbus, OH 43220", 90, columbus);
Job j7 = createJob("JOB-1007", "COPPER", "Copper line installation", "700 Fifth St, Columbus, OH 43201", 60, columbus);
Job j8 = createJob("JOB-1008", "MODEM", "Modem replacement", "800 Sixth St, Columbus, OH 43201", 30, columbus);

// Jobs - Cleveland
Job j9 = createJob("JOB-2001", "FIBER", "Fiber installation - business", "100 Euclid Ave, Cleveland, OH 44101", 120, cleveland);
Job j10 = createJob("JOB-2002", "ROUTER", "Router upgrade", "200 Superior Ave, Cleveland, OH 44101", 45, cleveland);
Job j11 = createJob("JOB-2003", "NETWORKING", "Network setup - office", "300 Prospect Ave, Cleveland, OH 44102", 90, cleveland);
Job j12 = createJob("JOB-2004", "COPPER", "Copper repair - emergency", "400 Carnegie Ave, Cleveland, OH 44102", 60, cleveland);

// Jobs - Cincinnati
Job j13 = createJob("JOB-3001", "FIBER", "Fiber installation", "100 Vine St, Cincinnati, OH 45201", 90, cincinnati);
Job j14 = createJob("JOB-3002", "ROUTER", "Router config - business", "200 Main St, Cincinnati, OH 45201", 60, cincinnati);

// Jobs - Dallas
Job j15 = createJob("JOB-4001", "FIBER", "Fiber installation", "100 Commerce St, Dallas, TX 75201", 90, dallas);
Job j16 = createJob("JOB-4002", "MODEM", "Modem setup", "200 Elm St, Dallas, TX 75201", 45, dallas);

// Use today's date for assignments
LocalDate today = LocalDate.now();

// John Smith (TECH-001) - Columbus - Full day schedule
createAssignment(t1, null, today, time(today, 7, 0), time(today, 7, 30), 30, "COMPLETED", "TRAVEL", "Travel from home to first job");
createAssignment(t1, j1, today, time(today, 7, 30), time(today, 9, 0), 0, "COMPLETED", "JOB", null);
createAssignment(t1, null, today, time(today, 9, 0), time(today, 9, 20), 20, "COMPLETED", "TRAVEL", "Travel to next job");
createAssignment(t1, j2, today, time(today, 9, 20), time(today, 10, 20), 0, "IN_PROGRESS", "JOB", null);
createAssignment(t1, null, today, time(today, 10, 20), time(today, 10, 35), 15, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t1, j3, today, time(today, 10, 35), time(today, 11, 20), 0, "SCHEDULED", "JOB", null);
createAssignment(t1, null, today, time(today, 11, 20), time(today, 12, 0), 0, "SCHEDULED", "BREAK", "Lunch break");
createAssignment(t1, null, today, time(today, 12, 0), time(today, 12, 15), 15, "SCHEDULED", "TRAVEL", "Travel to afternoon job");
createAssignment(t1, j8, today, time(today, 12, 15), time(today, 12, 45), 0, "SCHEDULED", "JOB", null);
createAssignment(t1, null, today, time(today, 12, 45), time(today, 13, 15), 30, "SCHEDULED", "RETURN_HOME", "Return home");

// Sarah Johnson (TECH-002) - Columbus
createAssignment(t2, null, today, time(today, 7, 30), time(today, 8, 0), 30, "COMPLETED", "TRAVEL", "Travel from home");
createAssignment(t2, j5, today, time(today, 8, 0), time(today, 9, 15), 0, "COMPLETED", "JOB", null);
createAssignment(t2, null, today, time(today, 9, 15), time(today, 9, 30), 15, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t2, j6, today, time(today, 9, 30), time(today, 11, 0), 0, "SCHEDULED", "JOB", null);
createAssignment(t2, null, today, time(today, 11, 0), time(today, 11, 30), 0, "SCHEDULED", "BREAK", "Lunch break");
createAssignment(t2, null, today, time(today, 11, 30), time(today, 11, 50), 20, "SCHEDULED", "TRAVEL", "Travel to afternoon job");
createAssignment(t2, j4, today, time(today, 11, 50), time(today, 12, 50), 0, "SCHEDULED", "JOB", null);
createAssignment(t2, null, today, time(today, 12, 50), time(today, 13, 20), 30, "SCHEDULED", "RETURN_HOME", "Return home");

// Mike Davis (TECH-003) - Columbus
createAssignment(t3, null, today, time(today, 8, 0), time(today, 8, 25), 25, "COMPLETED", "TRAVEL", "Travel from home");
createAssignment(t3, j7, today, time(today, 8, 25), time(today, 9, 25), 0, "IN_PROGRESS", "JOB", null);
createAssignment(t3, null, today, time(today, 9, 25), time(today, 9, 45), 20, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t3, j2, today, time(today, 9, 45), time(today, 10, 45), 0, "SCHEDULED", "JOB", "Copper line inspection");
createAssignment(t3, null, today, time(today, 10, 45), time(today, 11, 15), 0, "SCHEDULED", "BREAK", "Lunch break");
createAssignment(t3, null, today, time(today, 11, 15), time(today, 11, 45), 30, "SCHEDULED", "RETURN_HOME", "Return home");

// Emily Brown (TECH-004) - Cleveland
createAssignment(t4, null, today, time(today, 7, 0), time(today, 7, 35), 35, "COMPLETED", "TRAVEL", "Travel from home");
createAssignment(t4, j9, today, time(today, 7, 35), time(today, 9, 35), 0, "IN_PROGRESS", "JOB", null);
createAssignment(t4, null, today, time(today, 9, 35), time(today, 9, 50), 15, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t4, j11, today, time(today, 9, 50), time(today, 11, 20), 0, "SCHEDULED", "JOB", null);
createAssignment(t4, null, today, time(today, 11, 20), time(today, 12, 0), 0, "SCHEDULED", "BREAK", "Lunch break");
createAssignment(t4, null, today, time(today, 12, 0), time(today, 12, 35), 35, "SCHEDULED", "RETURN_HOME", "Return home");

// David Wilson (TECH-005) - Cleveland
createAssignment(t5, null, today, time(today, 7, 30), time(today, 8, 0), 30, "COMPLETED", "TRAVEL", "Travel from home");
createAssignment(t5, j10, today, time(today, 8, 0), time(today, 8, 45), 0, "COMPLETED", "JOB", null);
createAssignment(t5, null, today, time(today, 8, 45), time(today, 9, 5), 20, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t5, j12, today, time(today, 9, 5), time(today, 10, 5), 0, "SCHEDULED", "JOB", null);
createAssignment(t5, null, today, time(today, 10, 5), time(today, 10, 35), 0, "SCHEDULED", "BREAK", "Lunch break");
createAssignment(t5, null, today, time(today, 10, 35), time(today, 11, 5), 30, "SCHEDULED", "RETURN_HOME", "Return home");

// Lisa Anderson (TECH-006) - Cincinnati
createAssignment(t6, null, today, time(today, 7, 0), time(today, 7, 20), 20, "COMPLETED", "TRAVEL", "Travel from home");
createAssignment(t6, j13, today, time(today, 7, 20), time(today, 8, 50), 0, "IN_PROGRESS", "JOB", null);
createAssignment(t6, null, today, time(today, 8, 50), time(today, 9, 10), 20, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t6, j14, today, time(today, 9, 10), time(today, 10, 10), 0, "SCHEDULED", "JOB", null);
createAssignment(t6, null, today, time(today, 10, 10), time(today, 10, 40), 0, "SCHEDULED", "BREAK", "Lunch break");
createAssignment(t6, null, today, time(today, 10, 40), time(today, 11, 0), 20, "SCHEDULED", "RETURN_HOME", "Return home");

// James Taylor (TECH-007) - Dallas
createAssignment(t7, null, today, time(today, 7, 0), time(today, 7, 40), 40, "COMPLETED", "TRAVEL", "Travel from home");
createAssignment(t7, j15, today, time(today, 7, 40), time(today, 9, 10), 0, "IN_PROGRESS", "JOB", null);
createAssignment(t7, null, today, time(today, 9, 10), time(today, 9, 30), 20, "SCHEDULED", "TRAVEL", "Travel to next job");
createAssignment(t7, j16, today, time(today, 9, 30), time(today, 10, 15), 0, "SCHEDULED", "JOB", null);
createAssignment(t7, null, today, time(today, 10, 15), time(today, 10, 55), 40, "SCHEDULED", "RETURN_HOME", "Return home");

System.out.println("Seed data loaded successfully!");
}

private Technician createTechnician(String techId, String name, String email, String address, String skills, Market market) {
Technician t = new Technician();
t.setTechId(techId);
t.setName(name);
t.setEmail(email);
t.setAddress(address);
t.setSkills(skills);
t.setMarket(market);
return technicianRepository.save(t);
}

private Job createJob(String jobId, String type, String description, String address, int duration, Market market) {
Job j = new Job();
j.setJobId(jobId);
j.setType(type);
j.setDescription(description);
j.setAddress(address);
j.setEstimatedDurationMinutes(duration);
j.setStatus("OPEN");
j.setMarket(market);
return jobRepository.save(j);
}

private Assignment createAssignment(Technician tech, Job job, LocalDate date,
LocalDateTime start, LocalDateTime end,
int travelMins, String status, String taskType, String notes) {
Assignment a = new Assignment();
a.setTechnician(tech);
a.setJob(job);
a.setAssignmentDate(date);
a.setStartTime(start);
a.setEndTime(end);
a.setTravelTimeMinutes(travelMins);
a.setStatus(status);
a.setTaskType(taskType);
a.setNotes(notes);
return assignmentRepository.save(a);
}

private LocalDateTime time(LocalDate date, int hour, int minute) {
return LocalDateTime.of(date, LocalTime.of(hour, minute));
}
}
Loading