Skip to content

Commit

Permalink
卒業要件を切り替えるとそれまでの履修設定をクリアする
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroto7 committed Oct 20, 2019
1 parent 4a367ac commit c8e35a2
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 99 deletions.
106 changes: 8 additions & 98 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,97 +1,16 @@
import 'bootstrap/dist/css/bootstrap.min.css';
import React, { useState } from 'react';
import { Alert, Container, Form, Navbar } from 'react-bootstrap';
import { Alert, Container, Navbar } from 'react-bootstrap';
import './App.css';
import Course from './Course';
import CourseMovementConfirmationModal from './CourseMovementConfirmationModal';
import getValueFromModal from './getValueFromModal';
import RegistrationStatus from './RegistrationStatus';
import Requirements, { RegisteredCreditsCounts, RequirementWithChildren, RequirementWithCourses, SelectionRequirement } from './Requirements';
import RequirementSelector, { defaultRequirement } from './RequirementSelector';
import RequirementView from './RequirementView';
import RequirementsRootView, { emptyPlan } from './RequirementsRootView';

const App = () => {
const [requirement, setRequirement] = useState(defaultRequirement);
const [courseToStatus, setCourseToStatus] = useState(new Map<Course, RegistrationStatus>());
const [courseToRequirement, setCourseToRequirement] = useState(new Map<Course, RequirementWithCourses>());
const [requirementToOthersCount, setRequirementToOthersCount] = useState(
new Map<RequirementWithCourses, { acquired: number, registered: number }>()
);
const [selectionNameToOptionName, setSelectionNameToOptionName] = useState(new Map<string, string>());
const [showsOnlyRegistered, setShowsOnlyRegistered] = useState(false);
const [modals, setModals] = useState(new Array<JSX.Element>());

const handleCourseClick = async (course: Course, requirement: RequirementWithCourses) => {
const currentStatus: RegistrationStatus = courseToStatus.get(course) || RegistrationStatus.Unregistered;
const currentRequirement = courseToRequirement.get(course);
if (currentStatus === RegistrationStatus.Unregistered || currentRequirement === requirement) {
setCourseToStatus(new Map(
[...courseToStatus, [
course,
showsOnlyRegistered ?
currentStatus === RegistrationStatus.Acquired ? RegistrationStatus.Registered : RegistrationStatus.Acquired :
(currentStatus + 1) % 3
]]
));
} else if (
currentRequirement !== undefined &&
!await getValueFromModal(
CourseMovementConfirmationModal,
{ currentRequirement, courseToStatus, courseToRequirement, selectionNameToOptionName, requirementToOthersCount },
modals, setModals
)
) {
return;
}
setCourseToRequirement(new Map([...courseToRequirement, [course, requirement]]));
}

const clearCourseToRequirement = (requirement: Requirements, newCourseToRequirement: Map<Course, RequirementWithCourses>) => {
if (requirement instanceof RequirementWithChildren) {
for (const child of requirement.children) {
clearCourseToRequirement(child, newCourseToRequirement);
}
} else if (requirement instanceof RequirementWithCourses) {
for (const course of requirement.courses) {
if (newCourseToRequirement.get(course) === requirement) {
newCourseToRequirement.delete(course);
}
}
} else {
const selectedRequirement = requirement.getSelectedRequirement(selectionNameToOptionName);
if (selectedRequirement !== undefined) {
clearCourseToRequirement(selectedRequirement, newCourseToRequirement);
}
}
}

const clearCourseToRequirementInSelection = (selectionName: string, requirement: Requirements, newCourseToRequirement: Map<Course, RequirementWithCourses>) => {
if (requirement instanceof RequirementWithChildren) {
for (const child of requirement.children) {
clearCourseToRequirementInSelection(selectionName, child, newCourseToRequirement);
}
} else if (requirement instanceof SelectionRequirement && requirement.selectionName === selectionName) {
clearCourseToRequirement(requirement, newCourseToRequirement);
}
}

const handleOthersCountsChange = (requirement: RequirementWithCourses, newOthersCount: RegisteredCreditsCounts) => {
setRequirementToOthersCount(new Map([
...requirementToOthersCount,
[requirement, newOthersCount]
]));
}

const handleSelectionChange = (selectionName: string, newOptionName: string) => {
const newCourseToRequirement = new Map(courseToRequirement);
clearCourseToRequirementInSelection(selectionName, requirement, newCourseToRequirement);
setCourseToRequirement(newCourseToRequirement);
setSelectionNameToOptionName(new Map([...selectionNameToOptionName, [selectionName, newOptionName]]));
}
const [plan, setPlan] = useState(emptyPlan);

return (
<>
{modals}
<Navbar variant="dark" bg="dark">
<Navbar.Brand>卒業要件を満たしたい</Navbar.Brand>
</Navbar>
Expand All @@ -100,20 +19,11 @@ const App = () => {
このツールの結果を利用する場合、必ず履修要覧や支援室などでその結果が正しいことを確認するようにしてください。
<strong>科目や要件の定義が誤っていたり、実際には認められない履修の組み合わせがある可能性があります。</strong>
</Alert>
<RequirementSelector onChange={requirement => setRequirement(requirement)} />
<Form.Check custom className="mb-3" id="showsOnlyRegisteredCheck"
label="履修する科目のみ表示する"
checked={showsOnlyRegistered}
onChange={() => setShowsOnlyRegistered(!showsOnlyRegistered)}
/>
<div className="my-3">
<RequirementView
requirement={requirement} showsOnlyRegistered={showsOnlyRegistered}
courseToStatus={courseToStatus} courseToRequirement={courseToRequirement} selectionNameToOptionName={selectionNameToOptionName}
onCourseClick={handleCourseClick} onOthersCountsChange={handleOthersCountsChange}
onSelectionChange={handleSelectionChange} requirementToOthersCount={requirementToOthersCount}
/>
</div>
<RequirementSelector onChange={requirement => {
setRequirement(requirement);
setPlan(emptyPlan);
}} />
<RequirementsRootView requirement={requirement} plan={plan} onChange={newPlan => setPlan(newPlan)} />
</Container>
</>
);
Expand Down
2 changes: 1 addition & 1 deletion src/CourseMovementConfirmationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const CourseMovementConfirmationModal = ({ currentRequirement, courseToStatus, c
currentRequirement: RequirementWithCourses,
courseToStatus: Map<Course, RegistrationStatus>,
courseToRequirement: Map<Course, Requirements>,
selectionNameToOptionName: Map<string, string>,
selectionNameToOptionName: ReadonlyMap<string, string>,
requirementToOthersCount: Map<RequirementWithCourses, RegisteredCreditsCounts>,
onReturn: (value: boolean) => void,
onExited: () => void,
Expand Down
133 changes: 133 additions & 0 deletions src/RequirementsRootView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import React, { useState } from 'react';
import { Form } from 'react-bootstrap';
import Course from './Course';
import CourseMovementConfirmationModal from './CourseMovementConfirmationModal';
import getValueFromModal from './getValueFromModal';
import RegistrationStatus from './RegistrationStatus';
import Requirements, { RegisteredCreditsCounts, RequirementWithChildren, RequirementWithCourses, SelectionRequirement } from './Requirements';
import RequirementView from './RequirementView';

export interface Plan {
readonly courseToStatus: Map<Course, RegistrationStatus>;
readonly courseToRequirement: Map<Course, RequirementWithCourses>;
readonly requirementToOthersCount: Map<RequirementWithCourses, RegisteredCreditsCounts>;
readonly selectionNameToOptionName: ReadonlyMap<string, string>;
}
export const emptyPlan: Plan = {
courseToStatus: new Map,
courseToRequirement: new Map,
requirementToOthersCount: new Map,
selectionNameToOptionName: new Map,
};

const RequirementsRootView = ({ requirement, plan, onChange }: {
requirement: Requirements,
plan: Plan,
onChange: (newPlan: Plan) => void,
}) => {
const { courseToStatus, courseToRequirement, requirementToOthersCount, selectionNameToOptionName } = plan;

const [showsOnlyRegistered, setShowsOnlyRegistered] = useState(false);
const [modals, setModals] = useState(new Array<JSX.Element>());

const handleCourseClick = async (course: Course, requirement: RequirementWithCourses) => {
const currentStatus: RegistrationStatus = courseToStatus.get(course) || RegistrationStatus.Unregistered;
const currentRequirement = courseToRequirement.get(course);
let newCourseToStatus = courseToStatus;
if (currentStatus === RegistrationStatus.Unregistered || currentRequirement === requirement) {
newCourseToStatus = new Map([
...courseToStatus,
[
course,
showsOnlyRegistered ?
currentStatus === RegistrationStatus.Acquired ? RegistrationStatus.Registered : RegistrationStatus.Acquired :
(currentStatus + 1) % 3
]
]);
} else if (
currentRequirement !== undefined &&
!await getValueFromModal(
CourseMovementConfirmationModal,
{ currentRequirement, courseToStatus, courseToRequirement, selectionNameToOptionName, requirementToOthersCount },
modals, setModals
)
) {
return;
}
onChange({
...plan,
courseToStatus: newCourseToStatus,
courseToRequirement: new Map([...courseToRequirement, [course, requirement]]),
});
}

const clearCourseToRequirement = (requirement: Requirements, newCourseToRequirement: Map<Course, RequirementWithCourses>) => {
if (requirement instanceof RequirementWithChildren) {
for (const child of requirement.children) {
clearCourseToRequirement(child, newCourseToRequirement);
}
} else if (requirement instanceof RequirementWithCourses) {
for (const course of requirement.courses) {
if (newCourseToRequirement.get(course) === requirement) {
newCourseToRequirement.delete(course);
}
}
} else {
const selectedRequirement = requirement.getSelectedRequirement(selectionNameToOptionName);
if (selectedRequirement !== undefined) {
clearCourseToRequirement(selectedRequirement, newCourseToRequirement);
}
}
}

const clearCourseToRequirementInSelection = (selectionName: string, requirement: Requirements, newCourseToRequirement: Map<Course, RequirementWithCourses>) => {
if (requirement instanceof RequirementWithChildren) {
for (const child of requirement.children) {
clearCourseToRequirementInSelection(selectionName, child, newCourseToRequirement);
}
} else if (requirement instanceof SelectionRequirement && requirement.selectionName === selectionName) {
clearCourseToRequirement(requirement, newCourseToRequirement);
}
}

const handleOthersCountsChange = (requirement: RequirementWithCourses, newOthersCount: RegisteredCreditsCounts) => {
onChange({
...plan,
requirementToOthersCount: new Map([
...requirementToOthersCount,
[requirement, newOthersCount]
]),
});
}

const handleSelectionChange = (selectionName: string, newOptionName: string) => {
const newCourseToRequirement = new Map(courseToRequirement);
clearCourseToRequirementInSelection(selectionName, requirement, newCourseToRequirement);
onChange({
...plan,
courseToRequirement: newCourseToRequirement,
selectionNameToOptionName: new Map([...selectionNameToOptionName, [selectionName, newOptionName]]),
});
}

return (
<>
{modals}
<Form.Check custom className="mb-3" id="showsOnlyRegisteredCheck"
label="履修する科目のみ表示する"
checked={showsOnlyRegistered}
onChange={() => setShowsOnlyRegistered(!showsOnlyRegistered)}
/>
<div className="my-3">
<RequirementView
requirement={requirement} showsOnlyRegistered={showsOnlyRegistered}
courseToStatus={courseToStatus} courseToRequirement={courseToRequirement} selectionNameToOptionName={selectionNameToOptionName}
onCourseClick={handleCourseClick} onOthersCountsChange={handleOthersCountsChange}
onSelectionChange={handleSelectionChange} requirementToOthersCount={requirementToOthersCount}
/>
</div>
</>
);
}

export default RequirementsRootView;

0 comments on commit c8e35a2

Please sign in to comment.