feat: schedule list item action menu (#230)

* feat: action menu for schedule list item

* feat: schedule action menu functionality

* feat: dialog provider popups for delete

* feat: duplicate schedule satiesfies type

* refactor: change non-null assertion to early return for rename schedule

* refactor: move schedule list item dialog providers to util file

* style: run prettier

* chore: inline object with satisfies operator

* fix: border issues

* style: change popups to match figma

* fix: update import for schedule list item dialog providers

* style: change dropdown text style to match figma

* fix: add back dialog context

* style: rounded edges when hovering over action + soften border color

* chore: cleanup and improve styling

* fix: dialog in popupmain

---------

Co-authored-by: doprz <52579214+doprz@users.noreply.github.com>
Co-authored-by: Razboy20 <razboy20@gmail.com>
This commit is contained in:
Casey Charleston
2024-10-04 23:15:51 -05:00
committed by GitHub
parent 9ec05ef764
commit 15fc3699cf
10 changed files with 325 additions and 145 deletions

View File

@@ -1,6 +1,8 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { generateRandomId } from '@shared/util/random';
import handleDuplicate from './handleDuplicate';
/**
* Creates a new schedule with the given name
* @param scheduleName the name of the schedule to create
@@ -8,13 +10,13 @@ import { generateRandomId } from '@shared/util/random';
*/
export default async function createSchedule(scheduleName: string): Promise<string | undefined> {
const schedules = await UserScheduleStore.get('schedules');
// if (schedules.find(schedule => schedule.name === scheduleName)) {
// return `Schedule ${scheduleName} already exists`;
// }
// Duplicate schedule found, we need to append a number to the end of the schedule name
const updatedName = await handleDuplicate(scheduleName);
schedules.push({
id: generateRandomId(),
name: scheduleName,
name: updatedName,
courses: [],
hours: 0,
updatedAt: Date.now(),

View File

@@ -17,7 +17,11 @@ export default async function deleteSchedule(scheduleId: string): Promise<string
throw new Error(`Schedule ${scheduleId} does not exist`);
}
if (scheduleIndex === activeIndex) {
throw new Error('You cannot delete your active schedule! Please switch to another schedule before deleting.');
throw new Error(`Cannot delete active schedule`);
}
if (scheduleIndex < activeIndex) {
await UserScheduleStore.set('activeIndex', activeIndex - 1);
}
schedules.splice(scheduleIndex, 1);

View File

@@ -0,0 +1,31 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import { generateRandomId } from '@shared/util/random';
import handleDuplicate from './handleDuplicate';
/**
* Creates a new schedule with the given name
* @param scheduleName the name of the schedule to create
* @returns undefined if successful, otherwise an error message
*/
export default async function duplicateSchedule(scheduleId: string): Promise<string | undefined> {
const schedules = await UserScheduleStore.get('schedules');
const schedule = schedules.find(schedule => schedule.id === scheduleId);
if (schedule === undefined) {
throw new Error(`Schedule ${scheduleId} does not exist`);
}
const updatedName = await handleDuplicate(schedule.name);
schedules.push({
id: generateRandomId(),
name: updatedName,
courses: JSON.parse(JSON.stringify(schedule.courses)),
hours: schedule.hours,
updatedAt: Date.now(),
} satisfies typeof schedule);
await UserScheduleStore.set('schedules', schedules);
return undefined;
}

View File

@@ -0,0 +1,37 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
/**
* Duplicates a new schedule with the given name.
* Assumes that each schedule has a unique name.
* @param scheduleName the name of the schedule to handle duplication for
* @param schedules the list of UserSchedules to find existing names
* @returns the new name for the schedule, of the form `{baseName}({index})`
*/
export default async function handleDuplicate(scheduleName: string): Promise<string> {
const schedules = await UserScheduleStore.get('schedules');
// No point in checking for duplicates if the name is unique
if (schedules.find(schedule => schedule.name === scheduleName) === undefined) {
return scheduleName;
}
// Regex for matching `{baseName}({index})`, where match[1] = baseName, match[2] = (index)
const regex = /^(.+?)(\(\d+\))?$/;
// Extract base name and existing index
const match = scheduleName.match(regex);
const baseName = match && match[1] ? match[1] : scheduleName;
// Extract number from parentheses and increment
let index = match && match[2] ? parseInt(match[2].slice(1, -1), 10) + 1 : 1;
let newName: string;
// Increment until an unused index is found
do {
newName = `${baseName} (${index++})`;
// eslint-disable-next-line @typescript-eslint/no-loop-func
} while (schedules.find(schedule => schedule.name === newName));
return newName;
}

View File

@@ -1,21 +1,41 @@
import { UserScheduleStore } from '@shared/storage/UserScheduleStore';
import handleDuplicate from './handleDuplicate';
/**
* Renames a schedule with the specified name to a new name.
* @param scheduleId - The id of the schedule to be renamed.
* @param newName - The new name for the schedule.
* @returns A promise that resolves to a string if there is an error, or undefined if the schedule is renamed successfully.
* @returns A promise that resolves to the new name if successful, otherwise undefined.
*/
export default async function renameSchedule(scheduleId: string, newName: string): Promise<string | undefined> {
const schedules = await UserScheduleStore.get('schedules');
const scheduleIndex = schedules.findIndex(schedule => schedule.id === scheduleId);
if (scheduleIndex === -1) {
return `Schedule ${scheduleId} does not exist`;
return undefined;
}
const schedule = schedules[scheduleIndex];
if (schedule === undefined) {
return undefined;
}
schedules[scheduleIndex]!.name = newName;
// schedules[scheduleIndex].updatedAt = Date.now();
// if old name is of the form `{baseName}{index}` and newName === baseName, do nothing.
const oldName = schedule.name;
const regex = /^(.+?)(\(\d+\))?$/;
const match = oldName?.match(regex);
const baseName = match?.[1] ?? '';
const baseNameOfNewName = newName.match(regex)?.[1];
if (baseName === baseNameOfNewName) {
return oldName;
}
const updatedName = await handleDuplicate(newName);
schedule.name = updatedName;
schedule.updatedAt = Date.now();
await UserScheduleStore.set('schedules', schedules);
return undefined;
return newName;
}