feat: can open tabs, updated injected popup heading. basically done
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot';
|
||||||
|
import { Calendar } from 'src/views/components/calendar/Calendar/Calendar';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calendar page
|
* Calendar page
|
||||||
@@ -8,7 +9,7 @@ import ExtensionRoot from '@views/components/common/ExtensionRoot/ExtensionRoot'
|
|||||||
export default function CalendarMain() {
|
export default function CalendarMain() {
|
||||||
return (
|
return (
|
||||||
<ExtensionRoot>
|
<ExtensionRoot>
|
||||||
<div>Calendar Placeholder</div>
|
<Calendar />
|
||||||
</ExtensionRoot>
|
</ExtensionRoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const colors = {
|
|||||||
gray: '#9cadb7',
|
gray: '#9cadb7',
|
||||||
offwhite: '#d6d2c4',
|
offwhite: '#d6d2c4',
|
||||||
concrete: '#95a5a6',
|
concrete: '#95a5a6',
|
||||||
|
red: '#B91C1C' // Not sure if this should be here, but it's used for remove course, and add course is ut-green
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
red: '#af2e2d',
|
red: '#af2e2d',
|
||||||
|
|||||||
@@ -1,100 +1,100 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
|
import { FaCalendarAlt, FaCog, FaRedo } from 'react-icons/fa'; // Added FaRedo for the refresh icon
|
||||||
import { StatusIcon } from '@shared/util/icons';
|
import { StatusIcon } from '@shared/util/icons';
|
||||||
import { generateCourses } from 'src/stories/components/List.stories';
|
|
||||||
import { Status } from 'src/shared/types/Course';
|
import { Status } from 'src/shared/types/Course';
|
||||||
|
import { test_colors } from 'src/stories/components/PopupCourseBlock.stories';
|
||||||
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
import ExtensionRoot from './common/ExtensionRoot/ExtensionRoot';
|
||||||
import PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock';
|
import PopupCourseBlock from './common/PopupCourseBlock/PopupCourseBlock';
|
||||||
import Text from './common/Text/Text';
|
import Text from './common/Text/Text';
|
||||||
import Divider from './common/Divider/Divider';
|
import Divider from './common/Divider/Divider';
|
||||||
import logoImage from '../../assets/logo.png'; // Adjust the path as necessary
|
import logoImage from '../../assets/logo.png'; // Adjust the path as necessary
|
||||||
import List from './common/List/List'; // Ensure this path is correctly pointing to your List component
|
import List from './common/List/List'; // Ensure this path is correctly pointing to your List component
|
||||||
|
import useSchedules from '../hooks/useSchedules';
|
||||||
|
import { handleOpenCalendar } from './injected/CourseCatalogInjectedPopup/HeadingAndActions';
|
||||||
|
import { openTabFromContentScript } from '../lib/openNewTabFromContentScript';
|
||||||
|
|
||||||
|
|
||||||
export default function PopupMain() {
|
export default function PopupMain() {
|
||||||
const courses = generateCourses(5);
|
const [activeSchedule] = useSchedules();
|
||||||
|
|
||||||
|
|
||||||
// Manually applying colors for the demonstration
|
const draggableElements = activeSchedule?.courses.map((course, i) => (
|
||||||
const colors = {
|
<PopupCourseBlock
|
||||||
OPEN: { primaryColor: '#34D399', secondaryColor: '#059669' },
|
key={course.uniqueId}
|
||||||
CLOSED: { primaryColor: '#818cf8', secondaryColor: '#4f46e5' },
|
course={course}
|
||||||
WAITLISTED: { primaryColor: '#F59E00', secondaryColor: '#B45309' },
|
colors={test_colors[i]}
|
||||||
CANCELLED: { primaryColor: '#EF4444', secondaryColor: '#b91c1c' },
|
/>
|
||||||
TEMP: { primaryColor: '#fde047', secondaryColor: '#eab308' },
|
));
|
||||||
};
|
|
||||||
|
|
||||||
const draggableElements = courses.map((course) => (
|
const handleOpenOptions = async () => { // Not sure if it's bad practice to export this
|
||||||
<PopupCourseBlock
|
const url = chrome.runtime.getURL('/src/pages/options/index.html');
|
||||||
key={course.uniqueId}
|
await openTabFromContentScript(url);
|
||||||
course={course}
|
};
|
||||||
colors={colors[course.status]}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
|
return (
|
||||||
return (
|
<ExtensionRoot>
|
||||||
<ExtensionRoot>
|
<div className="mx-auto max-w-sm rounded-lg bg-white p-4 shadow-md">
|
||||||
<div className="mx-auto max-w-sm rounded-lg bg-white p-4 shadow-md">
|
<div className="mb-2 flex items-center justify-between bg-white">
|
||||||
<div className="mb-2 flex items-center justify-between bg-white">
|
<div className="flex items-center">
|
||||||
<div className="flex items-center">
|
<img src={logoImage} alt="Logo" style={{ width: '40px', height: '40px', marginRight: '8px' }} />
|
||||||
<img src={logoImage} alt="Logo" style={{ width: '40px', height: '40px', marginRight: '8px' }} />
|
<div>
|
||||||
<div>
|
<Text as="div" variant="h1-course" style={{ color: '#bf5700', fontSize: '1.3rem' }}>UT Registration</Text>
|
||||||
<Text as="div" variant="h1-course" style={{ color: '#bf5700', fontSize: '1.3rem' }}>UT Registration</Text>
|
<Text as="div" variant="h1-course" style={{ color: '#f8971f', fontSize: '1.3rem' }}>Plus</Text>
|
||||||
<Text as="div" variant="h1-course" style={{ color: '#f8971f', fontSize: '1.3rem' }}>Plus</Text>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<button style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }} onClick={handleOpenCalendar}>
|
||||||
|
<FaCalendarAlt color="white" />
|
||||||
|
</button>
|
||||||
|
<button style={{ backgroundColor: 'white', marginLeft: '10px', borderRadius: '8px', padding: '8px', boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }}
|
||||||
|
onClick = {handleOpenOptions}>
|
||||||
|
<FaCog color="#C05621" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<Divider color="#E2E8F0" type="solid" style={{ margin: '1rem 0' }} />
|
||||||
<button style={{ backgroundColor: '#bf5700', borderRadius: '8px', padding: '8px' }}>
|
<div className="mb-4 rounded-lg bg-white p-2 text-left shadow-inner" style={{ backgroundColor: 'white', border: '1px solid #FBD38D', borderRadius: '0.5rem' }}>
|
||||||
<FaCalendarAlt color="white" />
|
<Text as="div" variant="h2-course" style={{ color: '#DD6B20', fontSize: '1.2rem' }}>MAIN SCHEDULE:</Text>
|
||||||
</button>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'start', color: '#333f48' }}>
|
||||||
<button style={{ backgroundColor: 'white', marginLeft: '10px', borderRadius: '8px', padding: '8px', boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }}>
|
<Text as="div" variant="h1" style={{ fontSize: '1.2rem', fontWeight: 'bold', marginRight: '0.5rem' }}>22 HOURS</Text>
|
||||||
<FaCog color="#C05621" />
|
<Text as="div" variant="h2-course" style={{ fontSize: '1.2rem' }}>8 Courses</Text>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Divider color="#E2E8F0" type="solid" style={{ margin: '1rem 0' }} />
|
|
||||||
<div className="mb-4 rounded-lg bg-white p-2 text-left shadow-inner" style={{ backgroundColor: 'white', border: '1px solid #FBD38D', borderRadius: '0.5rem' }}>
|
|
||||||
<Text as="div" variant="h2-course" style={{ color: '#DD6B20', fontSize: '1.2rem' }}>MAIN SCHEDULE:</Text>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'start', color: '#333f48' }}>
|
|
||||||
<Text as="div" variant="h1" style={{ fontSize: '1.2rem', fontWeight: 'bold', marginRight: '0.5rem' }}>22 HOURS</Text>
|
|
||||||
<Text as="div" variant="h2-course" style={{ fontSize: '1.2rem' }}>8 Courses</Text>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* Integrate the List component here */}
|
|
||||||
<List
|
|
||||||
draggableElements={draggableElements}
|
|
||||||
itemHeight={100} // Adjust based on your content size
|
|
||||||
listHeight={500} // Adjust based on total height you want for the list
|
|
||||||
listWidth={350} // Adjust based on your layout/design
|
|
||||||
gap={12} // Spacing between items
|
|
||||||
/>
|
|
||||||
<div className="mt-4 flex justify-between border-t border-gray-200 p-4 text-xs">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
|
||||||
<StatusIcon status={Status.WAITLISTED} className="text-white h-5 w-5" />
|
|
||||||
</div>
|
</div>
|
||||||
<Text as="span" variant="mini">WAITLISTED</Text>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
{/* Integrate the List component here */}
|
||||||
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
{activeSchedule ? <List
|
||||||
<StatusIcon status={Status.CLOSED} className="text-white h-5 w-5" />
|
draggableElements={draggableElements}
|
||||||
|
itemHeight={100} // Adjust based on your content size
|
||||||
|
listHeight={500} // Adjust based on total height you want for the list
|
||||||
|
listWidth={350} // Adjust based on your layout/design
|
||||||
|
gap={12} // Spacing between items
|
||||||
|
/> : null}
|
||||||
|
<div className="mt-4 flex justify-between border-t border-gray-200 p-4 text-xs">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
||||||
|
<StatusIcon status={Status.WAITLISTED} className="h-5 w-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<Text as="span" variant="mini">WAITLISTED</Text>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
||||||
|
<StatusIcon status={Status.CLOSED} className="h-5 w-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<Text as="span" variant="mini">CLOSED</Text>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
||||||
|
<StatusIcon status={Status.CANCELLED} className="h-5 w-5 text-white" />
|
||||||
|
</div>
|
||||||
|
<Text as="span" variant="mini">CANCELLED</Text>
|
||||||
</div>
|
</div>
|
||||||
<Text as="span" variant="mini">CLOSED</Text>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center">
|
<div className="mt-2 text-center text-xs">
|
||||||
<div style={{ backgroundColor: '#6B7280', padding: '1px', borderRadius: '4px', marginRight: '3px', marginLeft: '8px' }}>
|
<div className="inline-flex items-center justify-center">
|
||||||
<StatusIcon status={Status.CANCELLED} className="h-5 w-5 text-white" />
|
<Text as="div" variant="mini">DATA UPDATED ON: 12:00 AM 02/01/2024</Text>
|
||||||
|
<FaRedo className="ml-2 h-4 w-4 text-gray-600" />
|
||||||
</div>
|
</div>
|
||||||
<Text as="span" variant="mini">CANCELLED</Text>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 text-center text-xs">
|
</ExtensionRoot>
|
||||||
<div className="inline-flex items-center justify-center">
|
);
|
||||||
<Text as="div" variant="mini">DATA UPDATED ON: 12:00 AM 02/01/2024</Text>
|
|
||||||
<FaRedo className="ml-2 h-4 w-4 text-gray-600" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ExtensionRoot>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
import { Button } from '@views/components/common/Button/Button';
|
import { Button } from '@views/components/common/Button/Button';
|
||||||
import { Chip, flagMap } from '@views/components/common/Chip/Chip';
|
import { Chip, flagMap } from '@views/components/common/Chip/Chip';
|
||||||
import Divider from '@views/components/common/Divider/Divider';
|
import Divider from '@views/components/common/Divider/Divider';
|
||||||
import Text from '@views/components/common/Text/Text';
|
import Text from '@views/components/common/Text/Text';
|
||||||
import React from 'react';
|
|
||||||
import addCourse from 'src/pages/background/lib/addCourse';
|
import addCourse from 'src/pages/background/lib/addCourse';
|
||||||
import openNewTab from 'src/pages/background/util/openNewTab';
|
import removeCourse from 'src/pages/background/lib/removeCourse';
|
||||||
|
import { openTabFromContentScript } from 'src/views/lib/openNewTabFromContentScript';
|
||||||
import { Course } from 'src/shared/types/Course';
|
import { Course } from 'src/shared/types/Course';
|
||||||
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
import { UserSchedule } from 'src/shared/types/UserSchedule';
|
||||||
import Add from '~icons/material-symbols/add';
|
import Add from '~icons/material-symbols/add';
|
||||||
|
import Minus from '~icons/material-symbols/minus';
|
||||||
import CalendarMonth from '~icons/material-symbols/calendar-month';
|
import CalendarMonth from '~icons/material-symbols/calendar-month';
|
||||||
import CloseIcon from '~icons/material-symbols/close';
|
import CloseIcon from '~icons/material-symbols/close';
|
||||||
import Copy from '~icons/material-symbols/content-copy';
|
import Copy from '~icons/material-symbols/content-copy';
|
||||||
@@ -24,6 +26,11 @@ interface HeadingAndActionProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const handleOpenCalendar = async () => { // Not sure if it's bad practice to export this
|
||||||
|
const url = chrome.runtime.getURL('calendar.html');
|
||||||
|
await openTabFromContentScript(url);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the heading component for the CoursePopup component.
|
* Renders the heading component for the CoursePopup component.
|
||||||
*
|
*
|
||||||
@@ -32,6 +39,10 @@ interface HeadingAndActionProps {
|
|||||||
*/
|
*/
|
||||||
const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, activeSchedule }) => {
|
const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, activeSchedule }) => {
|
||||||
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
|
const { courseName, department, number: courseNumber, uniqueId, instructors, flags, schedule } = course;
|
||||||
|
const [courseAdded, setCourseAdded] = useState<boolean>(
|
||||||
|
activeSchedule.courses.some(course => course.uniqueId === uniqueId)
|
||||||
|
);
|
||||||
|
|
||||||
const instructorString = instructors
|
const instructorString = instructors
|
||||||
.map(instructor => {
|
.map(instructor => {
|
||||||
const { firstName, lastName } = instructor;
|
const { firstName, lastName } = instructor;
|
||||||
@@ -42,30 +53,32 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
|
|||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
navigator.clipboard.writeText(uniqueId.toString());
|
navigator.clipboard.writeText(uniqueId.toString());
|
||||||
};
|
};
|
||||||
const handleOpenCalendar = async () => {
|
|
||||||
const url = chrome.runtime.getURL('calendar.html');
|
|
||||||
await openNewTab(url);
|
|
||||||
};
|
|
||||||
const handleOpenRateMyProf = async () => {
|
const handleOpenRateMyProf = async () => {
|
||||||
const openTabs = instructors.map(instructor => {
|
const openTabs = instructors.map(instructor => {
|
||||||
const { fullName } = instructor;
|
const { fullName } = instructor;
|
||||||
const url = `https://www.ratemyprofessors.com/search/professors/1255?q=${fullName}`;
|
const url = `https://www.ratemyprofessors.com/search/professors/1255?q=${fullName}`;
|
||||||
return openNewTab(url);
|
return openTabFromContentScript(url);
|
||||||
});
|
});
|
||||||
await Promise.all(openTabs);
|
await Promise.all(openTabs);
|
||||||
};
|
};
|
||||||
const handleOpenCES = async () => {
|
const handleOpenCES = async () => {
|
||||||
// TODO: does not look up the professor just takes you to the page
|
// TODO: does not look up the professor just takes you to the page
|
||||||
const cisUrl = 'https://utexas.bluera.com/utexas/rpvl.aspx?rid=d3db767b-049f-46c5-9a67-29c21c29c580®l=en-US';
|
const cisUrl = 'https://utexas.bluera.com/utexas/rpvl.aspx?rid=d3db767b-049f-46c5-9a67-29c21c29c580®l=en-US';
|
||||||
await openNewTab(cisUrl);
|
await openTabFromContentScript(cisUrl);
|
||||||
};
|
};
|
||||||
const handleOpenPastSyllabi = async () => {
|
const handleOpenPastSyllabi = async () => {
|
||||||
// not specific to professor
|
// not specific to professor
|
||||||
const url = `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${courseNumber}&course_title=${courseName}&unique=&instructor_first=&instructor_last=&course_type=In+Residence&search=Search`;
|
const url = `https://utdirect.utexas.edu/apps/student/coursedocs/nlogon/?year=&semester=&department=${department}&course_number=${courseNumber}&course_title=${courseName}&unique=&instructor_first=&instructor_last=&course_type=In+Residence&search=Search`;
|
||||||
await openNewTab(url);
|
await openTabFromContentScript(url);
|
||||||
};
|
};
|
||||||
const handleAddCourse = async () => {
|
const handleAddOrRemoveCourse = async () => {
|
||||||
await addCourse(activeSchedule.name, course);
|
if (!courseAdded) {
|
||||||
|
await addCourse(activeSchedule.name, course);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await removeCourse(activeSchedule.name, course);
|
||||||
|
}
|
||||||
|
setCourseAdded(!courseAdded);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className='w-full pb-3 pt-6'>
|
<div className='w-full pb-3 pt-6'>
|
||||||
@@ -124,8 +137,8 @@ const HeadingAndActions: React.FC<HeadingAndActionProps> = ({ course, onClose, a
|
|||||||
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>
|
<Button variant='outline' color='ut-orange' icon={Description} onClick={handleOpenPastSyllabi}>
|
||||||
Past Syllabi
|
Past Syllabi
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant='filled' color='ut-green' icon={Add} onClick={handleAddCourse}>
|
<Button variant='filled' color={!courseAdded ? 'ut-green' : 'ut-red'} icon={!courseAdded ? Add : Minus} onClick={handleAddOrRemoveCourse}>
|
||||||
Add Course
|
{!courseAdded ? 'Add Course' : 'Remove Course'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|||||||
25
src/views/lib/openNewTabFromContentScript.ts
Normal file
25
src/views/lib/openNewTabFromContentScript.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { createMessenger } from "chrome-extension-toolkit";
|
||||||
|
|
||||||
|
type MyMessages = {
|
||||||
|
openNewTab: {
|
||||||
|
data: { url: string };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const messenger = createMessenger<MyMessages>('background');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content scripts and background scripts are isolated environments.
|
||||||
|
* Content scripts are where our code interacting with the webpage lives,
|
||||||
|
* whereas the background script is where we can open a tab from.
|
||||||
|
* This function allows us to open a new tab from the content script by communicating
|
||||||
|
* with the background script.
|
||||||
|
*/
|
||||||
|
export async function openTabFromContentScript(url: string) {
|
||||||
|
// @ts-ignore
|
||||||
|
messenger.openNewTab({ url }).then(() => {
|
||||||
|
console.log('New tab opened with URL:', url);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('Error opening new tab:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user