Compare commits
4 Commits
derek/wall
...
copilot/su
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cf599486e | ||
|
|
02350188b5 | ||
|
|
3e1eb4ee6b | ||
|
|
1dfafa9db2 |
@@ -142,7 +142,7 @@
|
|||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"react-dev-utils": "^12.0.1",
|
"react-dev-utils": "^12.0.1",
|
||||||
"semantic-release": "^24.2.3",
|
"semantic-release": "^24.2.3",
|
||||||
"storybook": "^8.6.0",
|
"storybook": "^8.6.15",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"unocss": "^0.63.6",
|
"unocss": "^0.63.6",
|
||||||
"unocss-preset-primitives": "0.0.2-beta.1",
|
"unocss-preset-primitives": "0.0.2-beta.1",
|
||||||
|
|||||||
697
pnpm-lock.yaml
generated
697
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -2,11 +2,6 @@ import type { Serialized } from '@chrome-extension-toolkit';
|
|||||||
|
|
||||||
import { generateRandomId } from '../util/random';
|
import { generateRandomId } from '../util/random';
|
||||||
import { Course } from './Course';
|
import { Course } from './Course';
|
||||||
export interface Wallpaper {
|
|
||||||
type: string;
|
|
||||||
/** Image url will be here */
|
|
||||||
data?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a user's schedule that is stored in the extension
|
* Represents a user's schedule that is stored in the extension
|
||||||
@@ -18,8 +13,6 @@ export class UserSchedule {
|
|||||||
hours: number;
|
hours: number;
|
||||||
/** Unix timestamp of when the schedule was last updated */
|
/** Unix timestamp of when the schedule was last updated */
|
||||||
updatedAt: number;
|
updatedAt: number;
|
||||||
/** Wallpaper for this schedule */
|
|
||||||
wallpaper?: Wallpaper;
|
|
||||||
|
|
||||||
constructor(schedule: Serialized<UserSchedule>) {
|
constructor(schedule: Serialized<UserSchedule>) {
|
||||||
this.courses = schedule.courses.map(c => new Course(c));
|
this.courses = schedule.courses.map(c => new Course(c));
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ type CourseWithId = { course: Course } & BaseItem;
|
|||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Components/Common/SortableList',
|
title: 'Components/Common/SortableList',
|
||||||
component: SortableList,
|
component: SortableList<CourseWithId>,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: 'centered',
|
layout: 'centered',
|
||||||
},
|
},
|
||||||
@@ -85,7 +85,7 @@ const meta = {
|
|||||||
} satisfies Meta<typeof SortableList<CourseWithId>>;
|
} satisfies Meta<typeof SortableList<CourseWithId>>;
|
||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
type Story = StoryObj<Meta<typeof SortableList<CourseWithId>>>;
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
args: {
|
args: {
|
||||||
@@ -94,7 +94,9 @@ export const Default: Story = {
|
|||||||
course,
|
course,
|
||||||
})),
|
})),
|
||||||
onChange: () => {},
|
onChange: () => {},
|
||||||
renderItem: ({ id, course }) => <PopupCourseBlock key={id} course={course} colors={course.colors} />,
|
renderItem: (item: CourseWithId) => (
|
||||||
|
<PopupCourseBlock key={item.id} course={item.course} colors={item.course.colors} />
|
||||||
|
),
|
||||||
},
|
},
|
||||||
render: args => (
|
render: args => (
|
||||||
<div className='h-3xl w-3xl transform-none'>
|
<div className='h-3xl w-3xl transform-none'>
|
||||||
|
|||||||
@@ -28,31 +28,6 @@ import { LargeLogo } from '../common/LogoIcon';
|
|||||||
import Text from '../common/Text/Text';
|
import Text from '../common/Text/Text';
|
||||||
import CalendarFooter from './CalendarFooter';
|
import CalendarFooter from './CalendarFooter';
|
||||||
import DiningAppPromo from './DiningAppPromo';
|
import DiningAppPromo from './DiningAppPromo';
|
||||||
import { Wallpaper } from 'src/shared/types/UserSchedule';
|
|
||||||
|
|
||||||
function getWallpaper(wallpaper?: Wallpaper): React.CSSProperties {
|
|
||||||
if (!wallpaper || !wallpaper.data || wallpaper.type === 'none') {
|
|
||||||
return { backgroundColor: 'white' };
|
|
||||||
}
|
|
||||||
|
|
||||||
let imageUrl: string;
|
|
||||||
// get wallpaper image from storage, or just default to white
|
|
||||||
|
|
||||||
if (wallpaper.type === 'custom' && wallpaper.data) {
|
|
||||||
imageUrl = wallpaper.data;
|
|
||||||
} else {
|
|
||||||
// Fallback to white background
|
|
||||||
return { backgroundColor: 'white' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
backgroundImage: `url(${imageUrl})`,
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundPosition: 'center',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
backgroundOrigin: 'border-box',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calendar page component
|
* Calendar page component
|
||||||
@@ -121,7 +96,7 @@ export default function Calendar(): ReactNode {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<CalendarContext.Provider value>
|
<CalendarContext.Provider value>
|
||||||
<div className='h-full w-full flex flex-col' style={getWallpaper(activeSchedule.wallpaper)}>
|
<div className='h-full w-full flex flex-col'>
|
||||||
<div className='screenshot:calendar-target h-screen flex overflow-auto'>
|
<div className='screenshot:calendar-target h-screen flex overflow-auto'>
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
|
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react';
|
||||||
import { CalendarDots, Export, FileCode, FilePng, FileText, Sidebar } from '@phosphor-icons/react';
|
import { CalendarDots, Export, FileCode, FilePng, Sidebar } from '@phosphor-icons/react';
|
||||||
import styles from '@views/components/calendar/CalendarHeader/CalendarHeader.module.scss';
|
import styles from '@views/components/calendar/CalendarHeader/CalendarHeader.module.scss';
|
||||||
import { Button } from '@views/components/common/Button';
|
import { Button } from '@views/components/common/Button';
|
||||||
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
|
import DialogProvider from '@views/components/common/DialogProvider/DialogProvider';
|
||||||
@@ -11,7 +11,7 @@ import useSchedules from '@views/hooks/useSchedules';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { handleExportJson, saveAsCal, saveAsText, saveCalAsPng } from '../utils';
|
import { handleExportJson, saveAsCal, saveCalAsPng } from '../utils';
|
||||||
|
|
||||||
interface CalendarHeaderProps {
|
interface CalendarHeaderProps {
|
||||||
sidebarOpen?: boolean;
|
sidebarOpen?: boolean;
|
||||||
@@ -111,18 +111,6 @@ export default function CalendarHeader({ sidebarOpen, onSidebarToggle }: Calenda
|
|||||||
Save as .json
|
Save as .json
|
||||||
</Button>
|
</Button>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem>
|
|
||||||
<Button
|
|
||||||
className='w-full flex justify-start'
|
|
||||||
onClick={saveAsText}
|
|
||||||
color='ut-black'
|
|
||||||
size='small'
|
|
||||||
variant='minimal'
|
|
||||||
icon={FileText}
|
|
||||||
>
|
|
||||||
Save as .txt
|
|
||||||
</Button>
|
|
||||||
</MenuItem>
|
|
||||||
{/* <MenuItem>
|
{/* <MenuItem>
|
||||||
<Button color='ut-black' size='small' variant='minimal' icon={FileTxt}>
|
<Button color='ut-black' size='small' variant='minimal' icon={FileTxt}>
|
||||||
Export Unique IDs
|
Export Unique IDs
|
||||||
|
|||||||
@@ -245,27 +245,6 @@ export const scheduleToIcsString = (schedule: Serialized<UserSchedule>) => {
|
|||||||
return icsString;
|
return icsString;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the provided schedule in a human readable/copyable text format
|
|
||||||
* @param schedule - The schedule object
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const scheduleToText = (schedule: Serialized<UserSchedule>) => {
|
|
||||||
const lines: string[] = [];
|
|
||||||
|
|
||||||
lines.push(`Schedule: ${schedule.name}`);
|
|
||||||
lines.push('');
|
|
||||||
|
|
||||||
for (const c of schedule.courses) {
|
|
||||||
lines.push(c.fullName);
|
|
||||||
lines.push(`${c.creditHours} Credit Hours`);
|
|
||||||
lines.push(`${c.uniqueId}`);
|
|
||||||
lines.push('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines.join('\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the current schedule as a calendar file in the iCalendar format (ICS).
|
* Saves the current schedule as a calendar file in the iCalendar format (ICS).
|
||||||
* Fetches the current active schedule and converts it into an ICS string.
|
* Fetches the current active schedule and converts it into an ICS string.
|
||||||
@@ -283,25 +262,6 @@ export const saveAsCal = async () => {
|
|||||||
downloadBlob(icsString, 'CALENDAR', 'schedule.ics');
|
downloadBlob(icsString, 'CALENDAR', 'schedule.ics');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Save current schedule as a plain text file consisting of
|
|
||||||
* Course Name - Course ID
|
|
||||||
* Course Time
|
|
||||||
* Unique Number
|
|
||||||
* Line Break
|
|
||||||
* Repeat
|
|
||||||
*/
|
|
||||||
export const saveAsText = async () => {
|
|
||||||
const schedule = await getSchedule();
|
|
||||||
|
|
||||||
if (!schedule) {
|
|
||||||
throw new Error('No schedule found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const scheduleText = scheduleToText(schedule);
|
|
||||||
downloadBlob(scheduleText, 'TEXT', 'schedule.txt');
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves current schedule to JSON that can be imported on other devices.
|
* Saves current schedule to JSON that can be imported on other devices.
|
||||||
* @param id - Provided schedule ID to download
|
* @param id - Provided schedule ID to download
|
||||||
|
|||||||
Reference in New Issue
Block a user