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

担当教員と授業場所の編集欄を追加 #571

Merged
merged 11 commits into from
Oct 22, 2022
13 changes: 10 additions & 3 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
const path = require("path")
const path = require("path");
const { loadConfigFromFile, mergeConfig } = require("vite");

module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
stories: [
"../src/ui/**/*.stories.mdx",
"../src/ui/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-actions",
],
framework: "@storybook/vue3",
core: {
builder: "storybook-builder-vite",
Expand Down
2 changes: 1 addition & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import "~/styles/_index.scss";
import "~/ui/styles/_index.scss";

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.1.1",
"@storybook/addon-actions": "^6.5.12",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/vue3": "^6.4.19",
Expand Down
46 changes: 43 additions & 3 deletions src/domain/schedule.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { hasProperty } from "~/utils";
import { isNormalDay, isSpecialDay, NormalDay, SpecialDay } from "./day";
import { isModule, Module } from "./module";
import { isPeriod, Period } from "./period";
import {
isNormalDay,
isSpecialDay,
NormalDay,
normalDays,
SpecialDay,
specialDays,
} from "./day";
import { isModule, Module, modules } from "./module";
import { isPeriod, Period, periods } from "./period";
import { schedulesToTimetable } from "./timetable";

export type NormalSchedule = {
module: Module;
Expand Down Expand Up @@ -59,3 +67,35 @@ export const isEqualSchedule = (

return false;
};

export const removeDuplicateSchedules = (schedules: Schedule[]) => {
return schedules.reduce<Schedule[]>((ret, target) => {
if (ret.every((schedule) => !isEqualSchedule(schedule, target))) {
ret.push(target);
}
return ret;
}, []);
};

export const sortSchedules = (schedules: Schedule[]): Schedule[] => {
const timetable = schedulesToTimetable(schedules);
const ret: Schedule[] = [];

modules.forEach((module) => {
normalDays.forEach((day) => {
periods.forEach((period) => {
if (timetable.normal[module][day][period]) {
ret.push({ module, day, period });
}
});
});

specialDays.forEach((day) => {
if (timetable.special[module][day]) {
ret.push({ module, day });
}
});
});

return ret;
};
8 changes: 6 additions & 2 deletions src/infrastructure/api/converters/registeredCourse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ const updateApiCourseProp = <
v2: Required<ApiType.RegisteredCourse>[P]
) => boolean
): void => {
if (apiCourse.course && compareFn(apiCourse.course[prop], newValue)) return;
apiCourse[prop] = newValue;
if (
prop in apiCourse ||
(apiCourse.course && !compareFn(apiCourse.course[prop], newValue))
) {
apiCourse[prop] = newValue;
}
};

/**
Expand Down
10 changes: 6 additions & 4 deletions src/infrastructure/api/converters/schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ export const apiToSchedules = (
});
});

const rooms: Room[] = getKeysFromObj(roomNameToSchedules).map((roomName) => ({
name: roomName,
schedules: roomNameToSchedules[roomName],
}));
const rooms: Room[] = getKeysFromObj(roomNameToSchedules)
.filter((roomName) => roomName !== "")
.map((roomName) => ({
name: roomName,
schedules: roomNameToSchedules[roomName],
}));

return {
schedules,
Expand Down
5 changes: 4 additions & 1 deletion src/presentation/presenters/course.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Course, RegisteredCourse } from "~/domain/course";
import { isSpecialSchedule } from "~/domain/schedule";
import { Tag } from "~/domain/tag";
import { deepCopy } from "~/utils";
import { DisplayCourse, DisplayRegisteredCourse } from "../viewmodels/course";
Expand Down Expand Up @@ -42,7 +43,9 @@ export const registeredCourseToDisplay = (
name: course.name,
schedule: {
full: schedulesToFullString(course.schedules),
onlyModule: schedulesToModuleStrings(course.schedules),
onlyModule: schedulesToModuleStrings(
course.schedules.filter(isSpecialSchedule)
),
},
credit: creditToDisplay(course.credit),
instructor: instructorsToDisplay(course.instructors),
Expand Down
1 change: 1 addition & 0 deletions src/presentation/presenters/instructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const instructorsToDisplay = (instructors: string[]): string => {
};

export const validateInstructors = (instructors: string[]): boolean => {
if (instructors.some((instructor) => instructor === "")) return false;
if (new Set(instructors).size !== instructors.length) return false;

return true;
Expand Down
6 changes: 6 additions & 0 deletions src/presentation/presenters/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ import { Room } from "~/domain/room";
export const roomsToDisplay = (rooms: Room[]): string => {
return rooms.map(({ name }) => name).join(",");
};

export const validateRooms = (rooms: Room[]): boolean => {
return rooms.every(
({ name, schedules }) => name !== "" && schedules.length > 0
);
};
32 changes: 30 additions & 2 deletions src/presentation/presenters/timetable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,52 @@ export const getDisplayTimetable = <
);

courses.forEach((course) => {
const displayCourse = registeredCourseToDisplay(course, tags);

courseIdToSchedules[course.id]["normal"].forEach(
({ module, day, period }) => {
const displayCourse = registeredCourseToDisplay(
{
...course,
rooms: course.rooms.filter(
({ schedules }) =>
schedules
.filter(isNormalSchedule)
.filter(
(schedule) =>
schedule.module === module &&
schedule.day === day &&
schedule.period === period
).length > 0
),
},
tags
);
diaplayNormalTimetable[module][day][period].push(displayCourse);
}
);

removeDuplicate(
courseIdToSchedules[course.id]["special"].map(({ day }) => day)
).forEach((day) => {
const displayCourse = registeredCourseToDisplay(
{
...course,
rooms: course.rooms.filter(
({ schedules }) =>
schedules
.filter(isSpecialSchedule)
.filter((schedule) => schedule.day === day).length > 0
),
},
tags
);
displaySpecialTimetable[day].push(displayCourse);
});

if (
courseIdToSchedules[course.id]["normal"].length === 0 &&
courseIdToSchedules[course.id]["special"].length === 0
) {
const displayCourse = registeredCourseToDisplay(course, tags);
displaySpecialTimetable["Others"].push(displayCourse);
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/Button.stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Story } from "@storybook/vue3";
import Button from "~/ui/components/Button.vue";
import Button from "./Button.vue";

export default {
component: Button,
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/IconButton.stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Story } from "@storybook/vue3";
import IconButton from "~/ui/components/IconButton.vue";
import IconButton from "./IconButton.vue";

export default {
component: IconButton,
Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/LabeledTextField.stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Story } from "@storybook/vue3";
import LabeledTextField from "~/ui/components/LabeledTextField.vue";
import TextFieldSingleLine from "~/ui/components/TextFieldSingleLine.vue";
import LabeledTextField from "./LabeledTextField.vue";
import TextFieldSingleLine from "./TextFieldSingleLine.vue";

export default {
component: LabeledTextField,
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/Loader.stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Story } from "@storybook/vue3";
import Loader from "~/ui/components/Loader.vue";
import Loader from "./Loader.vue";

export default {
component: Loader,
Expand Down
101 changes: 101 additions & 0 deletions src/ui/components/MultiTextFieldEditor.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script lang="ts">
import { PropType, defineComponent } from "vue";
import Label from "~/ui/components/Label.vue";
import TertiaryButton from "~/ui/components/TertiaryButton.vue";
import TextFieldSingleLine from "~/ui/components/TextFieldSingleLine.vue";
import { useFocus } from "../hooks/useFocus";

export default defineComponent({
components: {
Label,
TextFieldSingleLine,
TertiaryButton,
},
props: {
elements: {
type: Array as PropType<string[]>,
required: true,
},
placeholder: {
type: String,
default: "",
},
label: {
type: String,
default: "",
},
mandatory: {
type: Boolean,
default: false,
},
size: {
type: String as PropType<"normal" | "slim">,
default: "normal",
},
},
emits: ["update:elements"],
setup(props, { emit }) {
const { targetRef, focus } = useFocus();

const addElement = () => {
const newElements = [...props.elements];
newElements.push("");
emit("update:elements", newElements);
focus([`#text-field-single-line-${newElements.length - 1}`, "input"]);
Copy link
Member

@HosokawaR HosokawaR Oct 15, 2022

Choose a reason for hiding this comment

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

フォーカスしてくれるの、とても便利ですね…!!ナイスです

};

const updateElement = (i: number, v: string) => {
const newElements = [...props.elements];
newElements.splice(i, 1, v.trim());
emit("update:elements", newElements);
};

const deleteElement = (i: number) => {
const newElements = [...props.elements];
newElements.splice(i, 1);
emit("update:elements", newElements);
};

return {
targetRef,
addElement,
updateElement,
deleteElement,
};
},
});
</script>

<template>
<div ref="targetRef" class="multi-text-field">
<Label
v-if="label !== ''"
:value="label"
:mandatory="mandatory"
:size="size"
></Label>
<TextFieldSingleLine
v-for="(el, i) in elements"
:id="`text-field-single-line-${i}`"
:key="`${i}`"
Copy link
Member

Choose a reason for hiding this comment

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

i

:model-value="el"
:placeholder="placeholder"
added
@update:model-value="(v) => updateElement(i, v)"
@close="deleteElement(i)"
></TextFieldSingleLine>
<TertiaryButton color="ghost" icon-position="left" @click="addElement">
<template #icon>add</template>
<template #text>追加する</template>
</TertiaryButton>
</div>
</template>

<style scoped lang="scss">
@import "~/ui/styles";

.multi-text-field {
display: grid;
gap: $spacing-2;
}
</style>
Loading