Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scheduling report courses view #75

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
81233e5
add instructor role type
jxjj Oct 27, 2023
0806103
create report for TA's or Primary instructor
jxjj Oct 27, 2023
681573a
Merge branch 'develop' into feature/71-Scheduling-report-add-view-of-…
jxjj Oct 30, 2023
e4077cc
add tab components
jxjj Oct 30, 2023
e0496ee
remove unused
jxjj Oct 30, 2023
87ce141
extract useTerms to its own composable
jxjj Oct 30, 2023
0730a34
include instructor role with instructor data
jxjj Oct 30, 2023
a958893
normalize course records
jxjj Oct 30, 2023
893e0ba
add `useGroup` composable
jxjj Oct 30, 2023
58b8c47
add `useGroupCourseHistory` composable
jxjj Oct 30, 2023
9a506a9
wip
jxjj Oct 31, 2023
32c04cc
wip
jxjj Nov 1, 2023
0ff0d41
bringing back filters
jxjj Nov 1, 2023
b6298c8
fix search
jxjj Nov 1, 2023
1f2afea
initial courses tab
jxjj Nov 1, 2023
c5dc15b
rename `lib` to `utils`
jxjj Nov 1, 2023
f3c6edf
breaking apart composables
jxjj Nov 1, 2023
bc38d98
trying with pinia store
jxjj Nov 1, 2023
b4229e5
put getters inside to keep from retriggering compute
jxjj Nov 1, 2023
be71ca6
some table reworking
jxjj Nov 1, 2023
60035d9
start formatting course table
jxjj Nov 1, 2023
e4515ab
add collaspable details to instructor
jxjj Nov 2, 2023
303330b
fix sorting
jxjj Nov 2, 2023
3c57fa8
add enrollment info to each instructor
jxjj Nov 2, 2023
34fc527
add additional details to courses
jxjj Nov 2, 2023
9f9e227
fix alignment
jxjj Nov 2, 2023
63041fd
update tabs
jxjj Nov 2, 2023
7b1218b
tab tweaks
jxjj Nov 2, 2023
fa6bbf8
integrate date range
jxjj Nov 2, 2023
274f93b
improve course tab switch
jxjj Nov 2, 2023
b3466ad
show leaves per term in course view
jxjj Nov 2, 2023
a56a89e
fix report not loading when groups change
jxjj Nov 2, 2023
1b038cb
limit leaves row height when sticky
jxjj Nov 2, 2023
0725689
remove console.log
jxjj Nov 2, 2023
a99258d
fix isLoadingComplete and scroll
jxjj Nov 2, 2023
c313925
un-sticky leave row
jxjj Nov 2, 2023
388a017
Refactor course table row to improve search
jxjj Nov 3, 2023
201623f
Add TimelessCourse to doesCourseMatchSearchTerm
jxjj Nov 3, 2023
4ff569e
Add search functionality and cancelation indicator
jxjj Nov 3, 2023
e5a315b
only show courses where instructors matches filter
jxjj Nov 3, 2023
bde2748
fix search
jxjj Nov 3, 2023
361e6ff
search highlights instructor leaves on course page
jxjj Nov 3, 2023
a0bd537
put section number with instructor details
jxjj Nov 3, 2023
16fb355
fixed with table columns
jxjj Nov 3, 2023
cf64b4f
bump sail
jxjj Nov 3, 2023
f6525bb
bump cypress
jxjj Nov 3, 2023
d8ecab8
fix tests: change to useTermsStore to init on use
jxjj Nov 3, 2023
42d1163
cleanup
jxjj Nov 3, 2023
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
1 change: 1 addition & 0 deletions app/Http/Resources/InstructorResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function toArray($request) {
'email' => $this->email,
'title' => $this->title,
'jobCode' => $this->jobCode,
'instructorRole' => $this->instructorRole,
'leaves' => $this->when($this->leaves->isNotEmpty(), $this->leaves),
'academicAppointment' => Utilities::trimWithFallback($this->jobCategory),
'emplid' => $this->emplid,
Expand Down
1 change: 1 addition & 0 deletions app/Library/UserService.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public function attachInstructorsToCourses(Collection $courses, Collection $empl

$user->jobCategory = $employeeInfo?->CATEGORY;
$user->jobCode = $employeeInfo?->JOBCODE;
$user->instructorRole = $course->INSTRUCTOR_ROLE;

$course->instructor = $user;
});
Expand Down
20 changes: 10 additions & 10 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"p-debounce": "^4.0.0",
"p-map": "^6.0.0",
"papaparse": "^5.4.1",
"pinia": "^2.1.7",
"popper.js": "^1.16.1",
"v-tooltip": "^2.0.3",
"vue": "^3.1.0",
Expand All @@ -42,7 +43,7 @@
"@vue/compiler-sfc": "^3.1.0",
"autoprefixer": "^10.4.14",
"cross-env": "^5.2.1",
"cypress": "12.17.1",
"cypress": "13.4.0",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-cypress": "^2.14.0",
Expand Down
11 changes: 8 additions & 3 deletions resources/js/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@

<script>
import { AppHeader, NavbarItem, AppFooter } from "@umn-latis/cla-vue-template";
import UserLookup from "./components/UserLookup.vue";
import CreateGroup from "./components/CreateGroup.vue";
import { $can } from "./lib";
import UserLookup from "@/components/UserLookup.vue";
import CreateGroup from "@/components/CreateGroup.vue";
import { $can } from "@/utils";
import { mapStores } from "pinia";
import { useTermsStore } from "./stores/useTermsStore";

export default {
components: {
Expand All @@ -86,6 +88,9 @@ export default {
createGroup: false,
};
},
computed: {
...mapStores(useTermsStore),
},
mounted() {
this.$store.dispatch("fetchUser");
console.log("Component mounted.");
Expand Down
90 changes: 56 additions & 34 deletions resources/js/api.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,32 @@
import { axios } from "@/lib";
import {
ApiUserLookupResponse,
ApiUserResponse,
UserLookupItem,
User,
Leave,
NewLeave,
Course,
Term,
Group,
InstructorRole,
} from "@/types";

export async function lookupUsers(query: string): Promise<UserLookupItem[]> {
const res = await axios.get<ApiUserLookupResponse>(
import { axios } from "@/utils";
import type * as Types from "@/types";

export async function lookupUsers(
query: string,
): Promise<Types.UserLookupItem[]> {
const res = await axios.get<Types.ApiUserLookupResponse>(
`/api/autocompleter/user?searchType=nameAndInternetId&q=${query}`,
);
return res.data.items;
}

export async function getUser(userId: number): Promise<User> {
const res = await axios.get<ApiUserResponse>(`/api/user/${userId}`);
export async function getUser(userId: number): Promise<Types.User> {
const res = await axios.get<Types.ApiUserResponse>(`/api/user/${userId}`);
return res.data;
}

export async function updateUser(user: User) {
const res = await axios.put<User>(`/api/user/${user.id}`, user);
export async function updateUser(user: Types.User) {
const res = await axios.put<Types.User>(`/api/user/${user.id}`, user);
return res.data;
}

export async function createLeave(leave: NewLeave) {
const res = await axios.post<Leave>(`/api/leaves`, leave);
export async function createLeave(leave: Types.NewLeave) {
const res = await axios.post<Types.Leave>(`/api/leaves`, leave);
return res.data;
}

export async function updateLeave(leave: Leave) {
const res = await axios.put<Leave>(`/api/leaves/${leave.id}`, leave);
export async function updateLeave(leave: Types.Leave) {
const res = await axios.put<Types.Leave>(`/api/leaves/${leave.id}`, leave);
return res.data;
}

Expand All @@ -44,38 +35,56 @@ export async function deleteLeave(leaveId: number) {
return res.data;
}

export async function getUserLeaves(userId: number): Promise<Leave[]> {
const res = await axios.get<Leave[]>(`/api/users/${userId}/leaves`);
export async function getUserLeaves(userId: number): Promise<Types.Leave[]> {
const res = await axios.get<Types.Leave[]>(`/api/users/${userId}/leaves`);
return res.data;
}

export async function updateUserLeaves(userId: number, leaves: Leave[]) {
const res = await axios.put<Leave[]>(`/api/users/${userId}/leaves`, {
export async function updateUserLeaves(userId: number, leaves: Types.Leave[]) {
const res = await axios.put<Types.Leave[]>(`/api/users/${userId}/leaves`, {
leaves,
});
return res.data;
}

let getTermsCache = [] as Term[];
let getTermsCache = [] as Types.Term[];
export async function getTerms() {
if (getTermsCache.length) {
return getTermsCache;
}
const res = await axios.get<Term[]>(`/api/terms`);
const res = await axios.get<Types.Term[]>(`/api/terms`);
getTermsCache = res.data;
return res.data;
}

const toCourse = (
rawCourse: Types.ApiCourseInstructorRecord,
): Types.Course => ({
shortCode: `${rawCourse.subject}-${rawCourse.catalogNumber}`,
classNumber: rawCourse.classNumber,
term: rawCourse.term,
subject: rawCourse.subject,
catalogNumber: rawCourse.catalogNumber,
classSection: rawCourse.classSection,
title: rawCourse.title,
enrollmentCap: rawCourse.enrollmentCap,
enrollmentTotal: rawCourse.enrollmentTotal,
cancelled: rawCourse.cancelled,
courseType: rawCourse.courseType,
courseLevel: rawCourse.courseLevel,
instructors: [],
});

export async function getGroupCoursesByTerm({
groupId,
termId,
roles,
}: {
groupId: number;
termId: number;
roles: InstructorRole[];
roles: Types.InstructorRole[];
}) {
const res = await axios.get<Course[]>(
const res = await axios.get<Types.ApiCourseInstructorRecord[]>(
`/api/terms/${termId}/groups/${groupId}/courses`,
{
params: {
Expand All @@ -84,10 +93,23 @@ export async function getGroupCoursesByTerm({
},
);

return res.data;
const coursesByClassNumber = new Map<
Types.Course["classNumber"],
Types.Course
>();

res.data.forEach((rawCourse) => {
const course =
coursesByClassNumber.get(rawCourse.classNumber) ?? toCourse(rawCourse);
course.instructors.push(rawCourse.instructor);

coursesByClassNumber.set(rawCourse.classNumber, course);
});

return [...coursesByClassNumber.values()];
}

export async function getGroup(groupId: number) {
const res = await axios.get<Group>(`/api/group/${groupId}`);
const res = await axios.get<Types.Group>(`/api/group/${groupId}`);
return res.data;
}
7 changes: 6 additions & 1 deletion resources/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import "./bootstrap";

import { createApp } from "vue";
import { createPinia } from "pinia";
import VTooltip from "v-tooltip";
import { store } from "./store";
import { router } from "./router";
Expand All @@ -17,4 +18,8 @@ import "@umn-latis/cla-vue-template/dist/style.css";
import "../sass/app.scss";
import "../sass/utils.css";

createApp(App).use(store).use(router).use(VTooltip).mount("#app");
const pinia = createPinia();

const app = createApp(App).use(store).use(pinia).use(router).use(VTooltip);

app.mount("#app");
2 changes: 1 addition & 1 deletion resources/js/components/EditGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ import Members from "./Members.vue";
import Modal from "./Modal.vue";
import FolderWidget from "./FolderWidget.vue";
import PersonSearch from "./PersonSearch.vue";
import { dayjs, axios } from "@/lib";
import { dayjs, axios } from "@/utils";

export default {
components: {
Expand Down
10 changes: 8 additions & 2 deletions resources/js/components/Favorites.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,21 @@
>{{ favorite[titleItem] }}
</router-link>
</td>
<td>{{ favorite.updated_at ? dayjs(favorite.updated_at).format("YYYY, MMM Do") : '' }}</td>
<td>
{{
favorite.updated_at
? dayjs(favorite.updated_at).format("YYYY, MMM Do")
: ""
}}
</td>
</tr>
</tbody>
</table>
</template>

<script>
import SortableLink from "./SortableLink.vue";
import { dayjs } from "@/lib";
import { dayjs } from "@/utils";

export default {
components: {
Expand Down
4 changes: 2 additions & 2 deletions resources/js/components/Gantt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

<script lang="ts">
import GanttRow from "./GanttRow.vue";
import { dayjs } from "@/lib";
import { dayjs } from "@/utils";

export default {
components: {
Expand All @@ -57,7 +57,7 @@ export default {
},
methods: {
dayjs,
}
},
};
</script>

Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/GanttRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<script lang="ts">
import $ from "jquery";
import dayjs from "../lib/dayjs";
import { dayjs } from "@/utils";

$(function () {
$("[data-toggle='tooltip']").tooltip();
Expand Down
14 changes: 11 additions & 3 deletions resources/js/components/LeaveChip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,25 @@
>✧</span
>
<NoIcon v-if="leave.status === 'cancelled'" title="cancelled" />
<span
<div
:class="{
'tw-line-through': leave.status === 'cancelled',
}"
>
{{ prettyLeaveType }} Leave
</span>
<span v-if="variant === 'instructor' && instructor">
{{ instructor.surName }}, {{ instructor.givenName }}
</span>
<span v-else> {{ prettyLeaveType }} Leave </span>
</div>
</header>
<div
v-if="isOpen"
class="leave-details tw-flex tw-flex-col tw-gap-2 tw-items-center tw-text-xs tw-normal-case"
>
<ul class="tw-text-xs tw-pl-6 tw-list-none tw-m-0">
<li v-if="variant === 'instructor'" class="tw-capitalize">
{{ leave.type }} Leave
</li>
<li class="tw-capitalize">
{{ isOnlyPartiallyEligible ? "Eligible when Tenured" : leave.status }}
</li>
Expand All @@ -65,9 +71,11 @@ const props = withDefaults(
defineProps<{
leave: Leave;
instructor?: Instructor;
variant?: "leaveType" | "instructor";
}>(),
{
instructor: undefined,
variant: "leaveType",
},
);

Expand Down
Loading
Loading