Skip to content

Commit 9ca53cb

Browse files
authored
feat(scheduling): enhance scheduling features with nested work types and team-based calendars (#1329)
* feat(scheduling): enhance scheduling features with nested work types and team-based calendars - **Team Model Updates**: Added `color` field to the `Team` model for team-based calendar color coding. - **Work Types Enhancements**: Introduced support for nested work types within `WorkTypeSchedule`, displaying additional case details (e.g., multiple work types) in scheduling components. - **Scheduling Calendar Features**: Integrated `@schedule-x/calendar-controls` for advanced calendar controls and unified time slot rounding. - **Improved Team Calendar Integration**: Dynamically set team-specific colors on calendar views based on the `color` attribute from the Team model. - **UI/UX Improvements**: Enhanced worksite scheduling UI, clarified view labels, and added nested work type information to interactive components like maps and dialogs. - **Data Exports**: Simplified export logic for CSV, PDF, and ICS formats to handle nested work types and team data. - **Model Consistency**: Extended `WorkTypeSchedule` types to include nested properties for work types and teams. - **Event Scheduling Simplification**: Allowed scheduling multiple work types with a single payload for better performance and flexibility. - **Optimized Initialization**: Consolidated scheduling initialization logic into a single `initialize` function. This commit enhances scheduling functionality for better worksite management, improved calendar-based workflows, and streamlined event handling. * chore(workflows): update `download-artifact` action to v4 - Updated `actions/download-artifact` from v3 to v4 in `.github/workflows/main.yml`. - Updated `actions/download-artifact` from v3 to v4 in `.github/workflows/e2e.yml`. This improves compatibility with the latest `actions/download-artifact` features and ensures better long-term maintenance. * chore(ci): update `upload-artifact` action to v4 in workflows - Bumped `actions/upload-artifact` from v3 to v4 in `.github/workflows/main.yml` - Updated usage in "Upload blob report to GitHub Actions Artifacts" - Updated usage in "Upload HTML report" - Bumped `actions/upload-artifact` from v3 to v4 in `.github/workflows/e2e.yml` - Updated usage in "Upload blob report to GitHub Actions Artifacts" - Updated usage in "Upload Test Result" - Updated usage in "Upload HTML report" Overall: Ensures workflows leverage the latest version of `upload-artifact` for improved performance and compatibility.
1 parent 9f0f6a0 commit 9ca53cb

File tree

12 files changed

+250
-132
lines changed

12 files changed

+250
-132
lines changed

.github/workflows/e2e.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ jobs:
103103

104104
- name: 📤 Upload blob report to GitHub Actions Artifacts
105105
if: always()
106-
uses: actions/upload-artifact@v3
106+
uses: actions/upload-artifact@v4
107107
with:
108108
name: all-blob-reports-${{ matrix.stage }}
109109
path: blob-report
@@ -116,7 +116,7 @@ jobs:
116116
117117
- name: Upload Test Result
118118
if: always()
119-
uses: actions/upload-artifact@v3
119+
uses: actions/upload-artifact@v4
120120
with:
121121
name: matrix-outputs
122122
path: test-result-${{ matrix.stage }}-${{ matrix.project }}.txt
@@ -131,7 +131,7 @@ jobs:
131131
steps:
132132
- uses: actions/checkout@v4
133133
- name: Download all artifacts
134-
uses: actions/download-artifact@v3
134+
uses: actions/download-artifact@v4
135135
with:
136136
name: matrix-outputs
137137
path: test-results
@@ -218,7 +218,7 @@ jobs:
218218
run: pnpm install
219219

220220
- name: 📥 Download blob reports from GitHub Actions Artifacts
221-
uses: actions/download-artifact@v3
221+
uses: actions/download-artifact@v4
222222
with:
223223
name: all-blob-reports-${{ matrix.stage }}
224224
path: all-blob-reports
@@ -274,7 +274,7 @@ jobs:
274274
cat report.md >> $GITHUB_STEP_SUMMARY # report.md is generated from markdown reporter
275275
276276
- name: 📤 Upload HTML report (${{ matrix.stage }})
277-
uses: actions/upload-artifact@v3
277+
uses: actions/upload-artifact@v4
278278
with:
279279
name: e2e-report-${{ matrix.stage }}-${{ github.run_id }}-attempt_${{ github.run_attempt }}
280280
path: playwright-report

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ jobs:
209209

210210
- name: 📤 Upload blob report to GitHub Actions Artifacts
211211
if: always()
212-
uses: actions/upload-artifact@v3
212+
uses: actions/upload-artifact@v4
213213
with:
214214
name: all-blob-reports
215215
path: blob-report
@@ -236,7 +236,7 @@ jobs:
236236
run: pnpm install
237237

238238
- name: 📥 Download blob reports from GitHub Actions Artifacts
239-
uses: actions/download-artifact@v3
239+
uses: actions/download-artifact@v4
240240
with:
241241
name: all-blob-reports
242242
path: all-blob-reports
@@ -258,7 +258,7 @@ jobs:
258258
cat report.md >> $GITHUB_STEP_SUMMARY # report.md is generated from markdown reporter
259259
260260
- name: 📤 Upload HTML report
261-
uses: actions/upload-artifact@v3
261+
uses: actions/upload-artifact@v4
262262
with:
263263
name: e2e-report-${{ github.run_id }}-attempt-${{ github.run_attempt }}
264264
path: playwright-report

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"@pixi/ticker": "6.5.10",
6666
"@pixi/utils": "6.5.10",
6767
"@schedule-x/calendar": "^2.18.0",
68+
"@schedule-x/calendar-controls": "^2.19.0",
6869
"@schedule-x/current-time": "^2.19.0",
6970
"@schedule-x/drag-and-drop": "^2.18.0",
7071
"@schedule-x/events-service": "^2.18.0",

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/scheduling/AddScheduleDialog.vue

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ interface WorkType {
221221
interface Worksite {
222222
id: number;
223223
case_number: string;
224+
name: string;
224225
work_types: WorkType[];
225226
}
226227
@@ -275,7 +276,7 @@ function validateForm(): boolean {
275276
);
276277
}
277278
} else {
278-
selectedWorkTypeIds.value.forEach((wtId: string) => {
279+
selectedWorkTypeIds.value.forEach((wtId: number) => {
279280
const start = individualTimes.value[wtId]?.start;
280281
const end = individualTimes.value[wtId]?.end;
281282
@@ -314,7 +315,7 @@ function handleWorkTypeCheckbox(isChecked: boolean, workTypeId: number) {
314315
}
315316
} else {
316317
selectedWorkTypeIds.value = selectedWorkTypeIds.value.filter(
317-
(id: string) => id !== workTypeId,
318+
(id: number) => id !== workTypeId,
318319
);
319320
delete individualTimes.value[workTypeId];
320321
delete individualTeams.value[workTypeId];
@@ -336,25 +337,23 @@ async function saveSchedules() {
336337
if (!worksite || selectedWorkTypeIds.value.length === 0) return;
337338
saving.value = true;
338339
339-
const payloads = selectedWorkTypeIds.value.map((wtId: string) => {
340-
let start, end, team_id;
341-
if (sameTimeForAll.value) {
342-
start = moment(commonStartAt.value).toISOString();
343-
end = moment(commonEndAt.value).toISOString();
344-
team_id = commonTeam.value;
345-
} else {
346-
start = moment(individualTimes.value[wtId].start).toISOString();
347-
end = moment(individualTimes.value[wtId].end).toISOString();
348-
team_id = individualTeams.value[wtId];
349-
}
350-
return {
351-
worksite_work_type: wtId,
352-
start,
353-
end,
354-
team: team_id,
355-
notes: notes.value,
356-
};
357-
});
340+
const payloads: any[] = sameTimeForAll.value
341+
? [
342+
{
343+
worksite_work_types_ids: selectedWorkTypeIds.value,
344+
start: moment(commonStartAt.value).toISOString(),
345+
end: moment(commonEndAt.value).toISOString(),
346+
team: commonTeam.value,
347+
notes: notes.value,
348+
},
349+
]
350+
: selectedWorkTypeIds.value.map((wtId: number) => ({
351+
worksite_work_types_ids: [wtId],
352+
start: moment(individualTimes.value[wtId].start).toISOString(),
353+
end: moment(individualTimes.value[wtId].end).toISOString(),
354+
team: individualTeams.value[wtId],
355+
notes: notes.value,
356+
}));
358357
359358
try {
360359
await Promise.all(

src/components/scheduling/EditScheduleDialog.vue

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146

147147
<script setup lang="ts">
148148
import axios from 'axios';
149+
import { ref, computed, onMounted } from 'vue';
149150
import { useI18n } from 'vue-i18n';
150151
import { useToast } from 'vue-toastification';
151152
import moment from 'moment';
@@ -169,6 +170,9 @@ import BaseButton from '@/components/BaseButton.vue';
169170
* end: string; // ISO date
170171
* team: number|null;
171172
* notes?: string;
173+
* worksite_work_types?: WorksiteWorkType[]; // new many-to-many field
174+
* work_type_key?: string; // legacy field
175+
* worksite_id: number;
172176
* ...
173177
* }
174178
*/
@@ -178,9 +182,12 @@ interface ScheduleItem {
178182
end: string;
179183
team?: number | null;
180184
notes?: string;
185+
worksite_work_types?: { work_type_key?: string }[];
186+
work_type_key?: string;
187+
worksite_id: number;
181188
}
182189
183-
/** Props: we receive the schedule ID. */
190+
/** Props: we receive the schedule ID via event prop. */
184191
const props = defineProps<{
185192
event: Record<string, any>;
186193
}>();
@@ -208,6 +215,19 @@ const form = ref({
208215
notes: '',
209216
});
210217
218+
// Update markerIcon to use the first work type key from the nested array if available
219+
const markerIcon = computed(() => {
220+
let workTypeKey = null;
221+
if (schedule.value) {
222+
workTypeKey =
223+
schedule.value.worksite_work_types &&
224+
schedule.value.worksite_work_types.length > 0
225+
? schedule.value.worksite_work_types[0].work_type_key
226+
: schedule.value.work_type_key;
227+
}
228+
return getBasicWorktypeSVG(workTypeKey, 35);
229+
});
230+
211231
function closeDialog() {
212232
emitter.emit('modal_component:close', 'edit_schedule_dialog');
213233
}
@@ -321,10 +341,6 @@ function openInGoogleMaps() {
321341
window.open(url, '_blank');
322342
}
323343
324-
const markerIcon = computed(() => {
325-
return getBasicWorktypeSVG(schedule.value.work_type_key, 35);
326-
});
327-
328344
onMounted(async () => {
329345
await fetchTeams();
330346
await fetchSchedule(props.event.id);

src/components/scheduling/WorkTypeScheduleMap.vue

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,17 @@ async function loadMap() {
4646
const bounds = L.latLngBounds([]);
4747
4848
props.schedules.forEach((appt: WorkTypeSchedule) => {
49+
// Get coordinates or fallback to defaults
4950
const coords = appt.worksite_location?.coordinates || [-96.4, 35.7];
5051
const latLng = L.latLng(coords[1], coords[0]);
51-
const markerSvg = getBasicWorktypeSVG(appt.work_type_key, 35);
52+
53+
// Use nested work types if available, otherwise fallback to legacy work_type_key
54+
let workTypeKey = appt.work_type_key;
55+
if (appt.worksite_work_types && appt.worksite_work_types.length > 0) {
56+
workTypeKey = appt.worksite_work_types[0].work_type_key;
57+
}
58+
59+
const markerSvg = getBasicWorktypeSVG(workTypeKey, 35);
5260
const divIcon = L.divIcon({
5361
className: 'leaflet-data-marker',
5462
html: markerSvg,
@@ -57,9 +65,7 @@ async function loadMap() {
5765
popupAnchor: [0, -28],
5866
});
5967
60-
const marker = L.marker(latLng, {
61-
icon: divIcon,
62-
}).addTo(map.value);
68+
const marker = L.marker(latLng, { icon: divIcon }).addTo(map.value);
6369
6470
marker.on('click', () => {
6571
emit('workTypeScheduleClick', appt);

0 commit comments

Comments
 (0)