Skip to content

Commit

Permalink
Duplicate calendar (#1048)
Browse files Browse the repository at this point in the history
  • Loading branch information
jotalis authored Nov 26, 2024
1 parent 0bba37d commit a1684de
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 87 deletions.
4 changes: 2 additions & 2 deletions apps/antalmanac/src/actions/ActionTypesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export interface ClearScheduleAction {

export interface CopyScheduleAction {
type: 'copySchedule';
to: number;
newScheduleName: string;
}

export interface ChangeCourseColorAction {
Expand Down Expand Up @@ -159,7 +159,7 @@ class ActionTypesStore extends EventEmitter {
AppStore.schedule.clearCurrentSchedule();
break;
case 'copySchedule':
AppStore.schedule.copySchedule(action.to);
AppStore.schedule.copySchedule(action.newScheduleName);
break;
default:
break;
Expand Down
12 changes: 6 additions & 6 deletions apps/antalmanac/src/actions/AppStoreActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { removeLocalStorageUserId, setLocalStorageUserId } from '$lib/localStora
import AppStore from '$stores/AppStore';

export interface CopyScheduleOptions {
onSuccess: (index: number) => unknown;
onError: (index: number) => unknown;
onSuccess: (scheduleName: string) => unknown;
onError: (scheduleName: string) => unknown;
}

export const addCourse = (
Expand Down Expand Up @@ -250,17 +250,17 @@ export const changeCourseColor = (sectionCode: string, term: string, newColor: s
AppStore.changeCourseColor(sectionCode, term, newColor);
};

export const copySchedule = (to: number, options?: CopyScheduleOptions) => {
export const copySchedule = (newScheduleName: string, options?: CopyScheduleOptions) => {
logAnalytics({
category: analyticsEnum.addedClasses.title,
action: analyticsEnum.addedClasses.actions.COPY_SCHEDULE,
});

try {
AppStore.copySchedule(to);
options?.onSuccess(to);
AppStore.copySchedule(newScheduleName);
options?.onSuccess(newScheduleName);
} catch (error) {
options?.onError(to);
options?.onError(newScheduleName);
}
};

Expand Down
20 changes: 16 additions & 4 deletions apps/antalmanac/src/components/dialogs/AddSchedule.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField } from '@mui/material';
import type { DialogProps } from '@mui/material';
import { useState } from 'react';
import { useState, useEffect, useCallback } from 'react';

import { addSchedule } from '$actions/AppStoreActions';
import AppStore from '$stores/AppStore';
Expand All @@ -12,7 +12,9 @@ import { useThemeStore } from '$stores/SettingsStore';
function AddScheduleDialog({ onClose, onKeyDown, ...props }: DialogProps) {
const isDark = useThemeStore((store) => store.isDark);

const [name, setName] = useState(AppStore.getDefaultScheduleName());
const [name, setName] = useState(
AppStore.getNextScheduleName(AppStore.getDefaultScheduleName(), AppStore.getScheduleNames().length)
);

const handleCancel = () => {
onClose?.({}, 'escapeKeyDown');
Expand All @@ -24,7 +26,6 @@ function AddScheduleDialog({ onClose, onKeyDown, ...props }: DialogProps) {

const submitName = () => {
addSchedule(name);
setName(AppStore.schedule.getDefaultScheduleName());
onClose?.({}, 'escapeKeyDown');
};

Expand All @@ -46,6 +47,17 @@ function AddScheduleDialog({ onClose, onKeyDown, ...props }: DialogProps) {
}
};

const handleScheduleNamesChange = useCallback(() => {
setName(AppStore.getNextScheduleName(AppStore.getDefaultScheduleName(), AppStore.getScheduleNames().length));
}, []);

useEffect(() => {
AppStore.on('scheduleNamesChange', handleScheduleNamesChange);
return () => {
AppStore.off('scheduleNamesChange', handleScheduleNamesChange);
};
}, [handleScheduleNamesChange]);

return (
<Dialog onKeyDown={handleKeyDown} {...props}>
<DialogTitle>Add Schedule</DialogTitle>
Expand All @@ -60,7 +72,7 @@ function AddScheduleDialog({ onClose, onKeyDown, ...props }: DialogProps) {
<Button onClick={handleCancel} color={isDark ? 'secondary' : 'primary'}>
Cancel
</Button>
<Button onClick={submitName} variant="contained" color="primary" disabled={name.trim() === ''}>
<Button onClick={submitName} variant="contained" color="primary" disabled={name?.trim() === ''}>
Add Schedule
</Button>
</DialogActions>
Expand Down
45 changes: 12 additions & 33 deletions apps/antalmanac/src/components/dialogs/CopySchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import {
DialogActions,
DialogContent,
DialogTitle,
MenuItem,
Select,
TextField,
type DialogProps,
} from '@mui/material';
import { SelectChangeEvent } from '@mui/material';
import { useState, useEffect, useCallback } from 'react';

import { copySchedule } from '$actions/AppStoreActions';
Expand All @@ -22,65 +20,46 @@ interface CopyScheduleDialogProps extends DialogProps {
function CopyScheduleDialog(props: CopyScheduleDialogProps) {
const { index } = props;
const { onClose } = props; // destructured separately for memoization.
const [scheduleNames, setScheduleNames] = useState(AppStore.getScheduleNames());
const [selectedSchedule, setSelectedSchedule] = useState<number>(0);
const [name, setName] = useState<string>(`Copy of ${AppStore.getScheduleNames()[index]}`);

const handleScheduleChange = useCallback((event: SelectChangeEvent<number>) => {
setSelectedSchedule(event.target.value as number);
const handleNameChange = useCallback((event: React.ChangeEvent<HTMLTextAreaElement>) => {
setName(event.target.value);
}, []);

const handleCancel = useCallback(() => {
onClose?.({}, 'escapeKeyDown');
}, [onClose]);

const handleCopy = useCallback(() => {
if (selectedSchedule !== scheduleNames.length) {
copySchedule(selectedSchedule);
} else {
scheduleNames.forEach((_, scheduleIndex) => {
if (scheduleIndex !== index) {
copySchedule(scheduleIndex);
}
});
}
copySchedule(name);
onClose?.({}, 'escapeKeyDown');
}, [index, onClose, selectedSchedule, scheduleNames]);
}, [onClose, name]);

const handleScheduleNamesChange = useCallback(() => {
setScheduleNames([...AppStore.getScheduleNames()]);
}, []);
setName(`Copy of ${AppStore.getScheduleNames()[index]}`);
}, [index]);

useEffect(() => {
AppStore.on('scheduleNamesChange', handleScheduleNamesChange);

return () => {
AppStore.off('scheduleNamesChange', handleScheduleNamesChange);
};
}, [handleScheduleNamesChange]);

return (
<Dialog onClose={onClose} {...props}>
<DialogTitle>Copy To Schedule</DialogTitle>

<DialogTitle>Copy Schedule</DialogTitle>
<DialogContent>
<Box padding={1}>
<Select fullWidth value={selectedSchedule} onChange={handleScheduleChange}>
{scheduleNames.map((name, idx) => (
<MenuItem key={idx} value={idx} disabled={index === idx}>
{name}
</MenuItem>
))}
<MenuItem value={scheduleNames.length}>Copy to All Schedules</MenuItem>
</Select>
<TextField fullWidth label="Name" onChange={handleNameChange} value={name} />
</Box>
</DialogContent>

<DialogActions>
<Button onClick={handleCancel} color="inherit">
Cancel
</Button>
<Button onClick={handleCopy} variant="contained" color="primary">
Copy Schedule
<Button onClick={handleCopy} variant="contained" color="primary" disabled={name?.trim() === ''}>
Make a Copy
</Button>
</DialogActions>
</Dialog>
Expand Down
21 changes: 15 additions & 6 deletions apps/antalmanac/src/components/dialogs/DeleteSchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
DialogTitle,
type DialogProps,
} from '@mui/material';
import { useCallback, useMemo } from 'react';
import { useCallback, useEffect, useState } from 'react';

import { deleteSchedule } from '$actions/AppStoreActions';
import AppStore from '$stores/AppStore';
Expand Down Expand Up @@ -35,10 +35,7 @@ function DeleteScheduleDialog(props: ScheduleNameDialogProps) {
* This is destructured separately for memoization.
*/
const { onClose } = props;

const scheduleName = useMemo(() => {
return AppStore.schedule.getScheduleName(index);
}, [index]);
const [name, setName] = useState<string>(AppStore.getScheduleNames()[index]);

const handleCancel = useCallback(() => {
onClose?.({}, 'escapeKeyDown');
Expand All @@ -49,12 +46,24 @@ function DeleteScheduleDialog(props: ScheduleNameDialogProps) {
onClose?.({}, 'escapeKeyDown');
}, [index, onClose]);

const handleScheduleNamesChange = useCallback(() => {
setName(AppStore.getScheduleNames()[index]);
}, [index]);

useEffect(() => {
AppStore.on('scheduleNamesChange', handleScheduleNamesChange);

return () => {
AppStore.off('scheduleNamesChange', handleScheduleNamesChange);
};
}, [handleScheduleNamesChange]);

return (
<Dialog {...dialogProps}>
<DialogTitle>Delete Schedule</DialogTitle>

<DialogContent>
<DialogContentText>Are you sure you want to delete &#34;{scheduleName}&#34;?</DialogContentText>
<DialogContentText>Are you sure you want to delete &#34;{name}&#34;?</DialogContentText>
</DialogContent>

<DialogActions>
Expand Down
20 changes: 6 additions & 14 deletions apps/antalmanac/src/components/dialogs/RenameSchedule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
TextField,
type DialogProps,
} from '@mui/material';
import { useCallback, useState, useEffect, useMemo } from 'react';
import { useCallback, useState, useEffect } from 'react';

import { renameSchedule } from '$actions/AppStoreActions';
import AppStore from '$stores/AppStore';
Expand All @@ -34,19 +34,11 @@ function RenameScheduleDialog(props: ScheduleNameDialogProps) {
* This is destructured separately for memoization.
*/
const { onClose } = props;

const [scheduleNames, setScheduleNames] = useState(AppStore.getScheduleNames());

const [name, setName] = useState(scheduleNames[index]);

const disabled = useMemo(() => {
return name?.trim() === '';
}, [name]);
const [name, setName] = useState(AppStore.getScheduleNames()[index]);

const handleCancel = useCallback(() => {
onClose?.({}, 'escapeKeyDown');
setName(scheduleNames[index]);
}, [onClose, scheduleNames, index]);
}, [onClose, index]);

const handleNameChange = useCallback((event: React.ChangeEvent<HTMLTextAreaElement>) => {
setName(event.target.value);
Expand Down Expand Up @@ -75,8 +67,8 @@ function RenameScheduleDialog(props: ScheduleNameDialogProps) {
);

const handleScheduleNamesChange = useCallback(() => {
setScheduleNames(AppStore.getScheduleNames());
}, []);
setName(AppStore.getScheduleNames()[index]);
}, [index]);

useEffect(() => {
AppStore.on('scheduleNamesChange', handleScheduleNamesChange);
Expand All @@ -100,7 +92,7 @@ function RenameScheduleDialog(props: ScheduleNameDialogProps) {
<Button onClick={handleCancel} color={'inherit'}>
Cancel
</Button>
<Button onClick={submitName} variant="contained" color="primary" disabled={disabled}>
<Button onClick={submitName} variant="contained" color="primary" disabled={name?.trim() === ''}>
Rename Schedule
</Button>
</DialogActions>
Expand Down
13 changes: 10 additions & 3 deletions apps/antalmanac/src/stores/AppStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class AppStore extends EventEmitter {
}
}

getNextScheduleName(newScheduleName: string, scheduleIndex: number) {
return this.schedule.getNextScheduleName(newScheduleName, scheduleIndex);
}

getDefaultScheduleName() {
return this.schedule.getDefaultScheduleName();
}
Expand Down Expand Up @@ -291,14 +295,17 @@ class AppStore extends EventEmitter {
window.localStorage.removeItem('unsavedActions');
}

copySchedule(to: number) {
this.schedule.copySchedule(to);
copySchedule(newScheduleName: string) {
this.schedule.copySchedule(newScheduleName);
this.unsavedChanges = true;
const action: CopyScheduleAction = {
type: 'copySchedule',
to: to,
newScheduleName: newScheduleName,
};
actionTypesStore.autoSaveSchedule(action);
this.emit('scheduleNamesChange');
this.emit('currentScheduleIndexChange');
this.emit('scheduleNotesChange');
this.emit('addedCoursesChange');
this.emit('customEventsChange');
}
Expand Down
Loading

0 comments on commit a1684de

Please sign in to comment.