Skip to content

Commit

Permalink
refactor: #10 change frontend availability structure from strings to …
Browse files Browse the repository at this point in the history
…numbers
  • Loading branch information
adrianrbp committed Aug 12, 2024
1 parent d84cbda commit 5a6ebc4
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 307 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
day:"Monday"
start_time:"2024-08-07 09:00:00"
end_time:"2024-08-07 10:00:00"
available:true

#### Modelos - Instancias Factory Bot
```ruby
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/api/AvailabilityApi.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import EngineersAvailabilityServiceAWeek1 from "@/mock/eng_availability_a_w1.json";
import { AvailabilityPayload, DayAvailability } from "./types";
import { AvailabilityPayload, EngineerAvailability } from "./types";

const isMock = process.env.VUE_APP_USE_MOCK === "true";

export const requestAvailabilities = async (
serviceId: number,
weekId: string
): Promise<DayAvailability[]> => {
): Promise<EngineerAvailability[]> => {
if (isMock) {
return new Promise<DayAvailability[]>((resolve) => {
return new Promise<EngineerAvailability[]>((resolve) => {
setTimeout(() => {
if (serviceId === 1) {
resolve(EngineersAvailabilityServiceAWeek1.data);
Expand Down
37 changes: 24 additions & 13 deletions frontend/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,29 +71,40 @@ export interface EngineersResponse {
statusText: string;
}

export interface EngineerAvailability {
id: number;
available: boolean;
}
// Availability
// export interface EngineerAvailability {
// id: number;
// available: boolean;
// }

export interface TimeBlockAv {
time: string;
engineers: EngineerAvailability[];
}
// export interface TimeBlockAv {
// time: string;
// engineers: EngineerAvailability[];
// }

// export interface DayAvailability {
// day: string;
// dayLabel?: string;
// times: TimeBlockAv[];
// }

export interface DayAvailability {
day: string;
dayLabel?: string;
times: TimeBlockAv[];
day: string; // E.g., "Monday"
availableTimes: number[]; // Array of hours (e.g., [9, 10, 11])
}

export interface AvailabilityResponse {
export interface EngineerAvailability {
engineer: number; // Engineer ID
availability: DayAvailability[];
}
export interface AvailabilityResponse {
availability: EngineerAvailability[];
status: number;
statusText: string;
}

// Availability Update Payload
export interface AvailabilityPayload {
week: string;
availability: DayAvailability[];
availability: EngineerAvailability[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@
<td v-for="engineer in engineers" :key="engineer.id" class="text-center">
<input
type="checkbox"
:checked="isEngineerAvailable(day, timeBlock, engineer.id)"
@change="updateAvailability(day, timeBlock, engineer.id)"
:checked="
isEngineerAvailable(day, parseTimeToHour(timeBlock), engineer.id)
"
@change="updateAvailability(day, parseTimeToHour(timeBlock), engineer.id)"
:aria-label="`Availability ${day} ${timeBlock} Engineer ${engineer.id}`"
/>
</td>
</template>

<script setup lang="ts">
import { type Ref, inject, defineProps } from "vue";
import { Engineer, DayAvailability } from "@/api/types";
import { Engineer, EngineerAvailability } from "@/api/types";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = defineProps<{
Expand All @@ -26,7 +28,7 @@ const shiftManagement = inject("shiftManagement") as {
const { engineers } = shiftManagement;
interface AvailabilityManagement {
availabilities: Ref<DayAvailability[]>;
availabilities: Ref<EngineerAvailability[]>;
}
const availabilityManagement = inject(
Expand All @@ -38,29 +40,72 @@ if (!availabilityManagement) {
}
const { availabilities } = availabilityManagement;
// const getEngineerAvailability = (
// day: string,
// time: string,
// engineerId: number
// ) => {
// const dayData = availabilities.value.find((d) => d.day === day);
// if (!dayData) return null;
// const timeData = dayData.times.find((t) => t.time === time);
// if (!timeData) return null;
// return timeData.engineers.find((e) => e.id === engineerId) || null;
// };
// const isEngineerAvailable = (day: string, time: string, engineerId: number) => {
// const engineerData = getEngineerAvailability(day, time, engineerId);
// return engineerData ? engineerData.available : false;
// };
// const updateAvailability = (day: string, time: string, engineerId: number) => {
// const engineerData = getEngineerAvailability(day, time, engineerId);
// if (engineerData) {
// engineerData.available = !engineerData.available; // Toggle availability
// }
// };
const parseTimeToHour = (time: string): number => {
const [hour] = time.split(":");
return parseInt(hour, 10);
};
// Adjusted to work with the first JSON structure
const getEngineerAvailability = (
day: string,
time: string,
time: number, // Time as a number
engineerId: number
) => {
const dayData = availabilities.value.find((d) => d.day === day);
if (!dayData) return null;
const engineerData = availabilities.value.find(
(e) => e.engineer === engineerId
);
if (!engineerData) return null;
const timeData = dayData.times.find((t) => t.time === time);
if (!timeData) return null;
const dayData = engineerData.availability.find((d) => d.day === day);
if (!dayData) return null;
return timeData.engineers.find((e) => e.id === engineerId) || null;
return dayData.availableTimes.includes(time);
};
const isEngineerAvailable = (day: string, time: string, engineerId: number) => {
const engineerData = getEngineerAvailability(day, time, engineerId);
return engineerData ? engineerData.available : false;
const isEngineerAvailable = (day: string, time: number, engineerId: number) => {
return getEngineerAvailability(day, time, engineerId) || false;
};
const updateAvailability = (day: string, time: string, engineerId: number) => {
const engineerData = getEngineerAvailability(day, time, engineerId);
if (engineerData) {
engineerData.available = !engineerData.available; // Toggle availability
const updateAvailability = (day: string, time: number, engineerId: number) => {
const engineerData = availabilities.value.find(
(e) => e.engineer === engineerId
);
if (!engineerData) return;
const dayData = engineerData.availability.find((d) => d.day === day);
if (!dayData) return;
const timeIndex = dayData.availableTimes.indexOf(time);
if (timeIndex === -1) {
dayData.availableTimes.push(time);
} else {
dayData.availableTimes.splice(timeIndex, 1); // Toggle availability
}
};
</script>
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { mount, VueWrapper } from "@vue/test-utils";
import { nextTick, Ref, ref } from "vue";
import AvailabilityTable from "../AvailabilityTable.vue";
import { DayAvailability, Engineer } from "@/api/types";
import { EngineerAvailability, Engineer } from "@/api/types";

export class AvailabilityTablePage {
private wrapper: VueWrapper;
public shiftManagementEngineers: Ref<Engineer[]>;
public availabilities: Ref<DayAvailability[]>;
public availabilities: Ref<EngineerAvailability[]>;

constructor(engineers: Engineer[], availabilities: DayAvailability[]) {
constructor(engineers: Engineer[], availabilities: EngineerAvailability[]) {
this.shiftManagementEngineers = ref(engineers);
this.availabilities = ref(availabilities);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AvailabilityTablePage } from "./AvailabilityTable.page";
import { Engineer, DayAvailability } from "@/api/types";
import { Engineer, EngineerAvailability } from "@/api/types";

describe("AvailabilityTable.vue", () => {
let page: AvailabilityTablePage;
let providerEngineersMock: Engineer[];
let providerAvailabilitiesMock: DayAvailability[];
let providerAvailabilitiesMock: EngineerAvailability[];

beforeEach(() => {
providerEngineersMock = [
Expand All @@ -15,18 +15,19 @@ describe("AvailabilityTable.vue", () => {

providerAvailabilitiesMock = [
{
day: "Monday",
times: [
{
time: "09:00",
engineers: [
{ id: 1, available: true },
{ id: 2, available: false },
{ id: 3, available: true },
],
},
engineer: 1,
availability: [{ day: "Monday", availableTimes: [9] }],
},
{
engineer: 2,
availability: [
{ day: "Monday", availableTimes: [] }, // Engineer 2 is not available at 09:00 on Monday
],
},
{
engineer: 3,
availability: [{ day: "Monday", availableTimes: [9] }],
},
];

page = new AvailabilityTablePage(
Expand All @@ -48,18 +49,18 @@ describe("AvailabilityTable.vue", () => {
// Initially, the first engineer is available
expect(page.isCheckboxChecked(0)).toBe(true);

// Toggle availability
// Toggle availability: Engineer not Available
await page.toggleCheckbox(0);
expect(page.isCheckboxChecked(0)).toBe(false);
expect(page.availabilities.value[0].times[0].engineers[0].available).toBe(
false
expect(page.availabilities.value[0].availability[0].availableTimes).toEqual(
[]
);

// Toggle back
// Toggle back: Engineer Available
await page.toggleCheckbox(0);
expect(page.isCheckboxChecked(0)).toBe(true);
expect(page.availabilities.value[0].times[0].engineers[0].available).toBe(
true
expect(page.availabilities.value[0].availability[0].availableTimes).toEqual(
[9]
);
});
});
3 changes: 1 addition & 2 deletions frontend/src/components/ShiftManagement/ShiftTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ import AvailabilityTable from "@/components/AvailabilityManagement/AvailabilityT
const shiftManagement = inject("shiftManagement");
const { shifts, allShiftsTimeBlocks, getEngineerColor, engineers } =
shiftManagement;
const { allShiftsTimeBlocks, getEngineerColor, engineers } = shiftManagement;
const availabilityManagement = inject("availabilityManagement");
Expand Down
30 changes: 25 additions & 5 deletions frontend/src/mixins/__tests__/useAvailabilityManagement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
requestAvailabilities,
storeAvailabilities,
} from "@/api/AvailabilityApi";
import { DayAvailability } from "@/api/types";
import { EngineerAvailability } from "@/api/types";

jest.mock("@/api/AvailabilityApi");

Expand Down Expand Up @@ -32,7 +32,12 @@ describe("useAvailabilityManagement", () => {
});

it("fetches availabilities when service and week are selected", async () => {
const mockData: DayAvailability[] = [{ day: "Monday", times: [] }];
const mockData: EngineerAvailability[] = [
{
engineer: 1,
availability: [{ day: "Monday", availableTimes: [9] }],
},
];
(requestAvailabilities as jest.Mock).mockResolvedValue(mockData);

selectedService.value = 1;
Expand Down Expand Up @@ -60,7 +65,12 @@ describe("useAvailabilityManagement", () => {
selectedService.value = 1;
selectedWeek.value = "2024-W32";

const mockData: DayAvailability[] = [{ day: "Monday", times: [] }];
const mockData: EngineerAvailability[] = [
{
engineer: 1,
availability: [{ day: "Monday", availableTimes: [9] }],
},
];
mixin.availabilities.value = mockData;

await mixin.saveAvailability();
Expand All @@ -78,7 +88,12 @@ describe("useAvailabilityManagement", () => {
selectedService.value = 1;
selectedWeek.value = "2024-W32";

const mockData: DayAvailability[] = [{ day: "Monday", times: [] }];
const mockData: EngineerAvailability[] = [
{
engineer: 1,
availability: [{ day: "Monday", availableTimes: [9] }],
},
];
mixin.availabilities.value = mockData;

await mixin.saveAvailability();
Expand All @@ -94,7 +109,12 @@ describe("useAvailabilityManagement", () => {
});

it("fetches and saves availability on toggle", async () => {
const mockData: DayAvailability[] = [{ day: "Monday", times: [] }];
const mockData: EngineerAvailability[] = [
{
engineer: 1,
availability: [{ day: "Monday", availableTimes: [9] }],
},
];
(requestAvailabilities as jest.Mock).mockResolvedValue(mockData);
(storeAvailabilities as jest.Mock).mockResolvedValue(null);

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/mixins/useAvailabilityManagement.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Ref, ref, watch } from "vue";
import { DayAvailability } from "@/api/types";
import { EngineerAvailability } from "@/api/types";
import {
requestAvailabilities,
storeAvailabilities,
Expand All @@ -9,7 +9,7 @@ export function useAvailabilityManagement(
selectedService: Ref<number | null>,
selectedWeek: Ref<string | null>
) {
const availabilities = ref<DayAvailability[]>([]);
const availabilities = ref<EngineerAvailability[]>([]);

const statusMessage = ref<string | null>(null);

Expand All @@ -21,7 +21,7 @@ export function useAvailabilityManagement(
return;
}
try {
const data: DayAvailability[] = await requestAvailabilities(
const data: EngineerAvailability[] = await requestAvailabilities(
selectedService.value,
selectedWeek.value
);
Expand Down
Loading

0 comments on commit 5a6ebc4

Please sign in to comment.