diff --git a/src/views/components/common/ScheduleListItem.tsx b/src/views/components/common/ScheduleListItem.tsx
index 8d9ca413..d79038f9 100644
--- a/src/views/components/common/ScheduleListItem.tsx
+++ b/src/views/components/common/ScheduleListItem.tsx
@@ -11,16 +11,19 @@ import {
RadioButton,
Trash,
} from '@phosphor-icons/react';
+import { background } from '@shared/messages';
import type { UserSchedule } from '@shared/types/UserSchedule';
import Text from '@views/components/common/Text/Text';
import { useEnforceScheduleLimit } from '@views/hooks/useEnforceScheduleLimit';
import useSchedules from '@views/hooks/useSchedules';
+import { LONGHORN_DEVELOPERS_ADMINS, LONGHORN_DEVELOPERS_SWE } from '@views/lib/getGitHubStats';
import clsx from 'clsx';
import React, { useEffect, useMemo, useState } from 'react';
import { Button } from './Button';
import DialogProvider, { usePrompt } from './DialogProvider/DialogProvider';
import { ExtensionRootWrapper, styleResetClass } from './ExtensionRoot/ExtensionRoot';
+import Link from './Link';
import { SortableListDragHandle } from './SortableListDragHandle';
/**
@@ -32,6 +35,7 @@ interface ScheduleListItemProps {
}
const IS_STORYBOOK = import.meta.env.STORYBOOK;
+const teamMembers = [...LONGHORN_DEVELOPERS_ADMINS, ...LONGHORN_DEVELOPERS_SWE];
/**
* This is a reusable dropdown component that can be used to toggle the visiblity of information
@@ -40,6 +44,7 @@ export default function ScheduleListItem({ schedule, onClick }: ScheduleListItem
const [activeSchedule] = useSchedules();
const [isEditing, setIsEditing] = useState(false);
const [editorValue, setEditorValue] = useState(schedule.name);
+ const teamMember = teamMembers[Math.floor(Math.random() * teamMembers.length)];
const showDialog = usePrompt();
const enforceScheduleLimit = useEnforceScheduleLimit();
@@ -65,13 +70,46 @@ export default function ScheduleListItem({ schedule, onClick }: ScheduleListItem
const handleBlur = async () => {
if (editorValue.trim() !== '' && editorValue.trim() !== schedule.name) {
schedule.name = (await renameSchedule(schedule.id, editorValue.trim())) as string;
+
+ if (schedule.name === '404') {
+ const url = chrome.runtime.getURL('/404.html');
+ background.openNewTab({ url });
+ }
+
+ if (Math.random() < 0.002) {
+ showDialog({
+ title: 'Schedule name already taken',
+ description: (
+ <>
+ Schedule name
+ {schedule.name}
+
+ is already taken.
+
+
+ Join the
+
+
+ Discord
+
+ to contact {teamMember?.name as string}.
+ >
+ ),
+ // eslint-disable-next-line react/no-unstable-nested-components
+ buttons: close => (
+
+ ),
+ });
+ }
}
setIsEditing(false);
};
const handleDelete = () => {
showDialog({
- title: `Are you sure?`,
+ title: 'Are you sure?',
description: (
<>
Deleting
diff --git a/utils/plugins/vite-build-logger.ts b/utils/plugins/vite-build-logger.ts
new file mode 100644
index 00000000..3e5c11c2
--- /dev/null
+++ b/utils/plugins/vite-build-logger.ts
@@ -0,0 +1,81 @@
+import chalk from 'chalk';
+import type { Plugin } from 'vite';
+
+/**
+ * Options to configure the build logger.
+ */
+interface BuildLoggerOptions {
+ includeEnvVars?: string[]; // Specific env vars to display
+ includeTimestamp?: boolean;
+ includeBuildTime?: boolean;
+ customMetadata?: Record string | Promise>;
+}
+
+/**
+ * Vite plugin to log build information.
+ *
+ * @param Options - to configure the build logger.
+ *
+ * @returns Vite plugin object.
+ */
+export function buildLogger(options: BuildLoggerOptions = {}): Plugin {
+ const startTime = Date.now();
+
+ return {
+ name: 'vite-build-logger',
+ enforce: 'post',
+
+ async closeBundle() {
+ console.log(`\n${chalk.bold.cyan('=== Build Information ===')}`);
+
+ // Environment
+ console.log(chalk.yellow('\nBuild Environment:'));
+ console.log(`🔧 Node Build Mode: ${process.env.NODE_ENV}`);
+ console.log(`🎯 Browser Target: ${process.env.BROWSER_TARGET}`);
+
+ // Timestamp
+ if (options.includeTimestamp) {
+ console.log(chalk.yellow('\nBuild Timestamp:'));
+ console.log(`📅 ${new Date().toISOString()}`);
+ }
+
+ // Build Time
+ if (options.includeBuildTime) {
+ const buildTime = Date.now() - startTime;
+ console.log(chalk.yellow('\nBuild Time:'));
+ console.log(`⏱️ ${buildTime}ms (${(buildTime / 1000).toFixed(2)}s)`);
+ }
+
+ // Selected Environment Variables
+ if (options.includeEnvVars?.length) {
+ console.log(chalk.yellow('\nEnvironment Variables:'));
+ for (const envVar of options.includeEnvVars) {
+ if (process.env[envVar]) {
+ // Mask sensitive variables that might contain tokens or keys
+ const isSensitive =
+ envVar.toLowerCase().includes('key') ||
+ envVar.toLowerCase().includes('token') ||
+ envVar.toLowerCase().includes('secret');
+
+ const value = isSensitive ? '****' : process.env[envVar];
+ console.log(`${envVar}: ${value}`);
+ } else {
+ console.log(`${envVar}: ${chalk.red('Not defined')}`);
+ }
+ }
+ }
+
+ // Custom Metadata
+ if (options.customMetadata) {
+ console.log(chalk.yellow('\nCustom Metadata:'));
+ for (const [key, getter] of Object.entries(options.customMetadata)) {
+ // eslint-disable-next-line no-await-in-loop
+ const value = await getter();
+ console.log(`${key}: ${value}`);
+ }
+ }
+
+ console.log(`\n${chalk.bold.cyan('=====================')}\n`);
+ },
+ };
+}
diff --git a/vite.config.ts b/vite.config.ts
index db93c2b3..71cf663d 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,6 +1,7 @@
///
import { crx } from '@crxjs/vite-plugin';
import react from '@vitejs/plugin-react-swc';
+import { execSync } from 'child_process';
import { resolve } from 'path';
import UnoCSS from 'unocss/vite';
import Icons from 'unplugin-icons/vite';
@@ -11,17 +12,27 @@ import inspect from 'vite-plugin-inspect';
import packageJson from './package.json';
import manifest from './src/manifest';
import vitePluginRunCommandOnDemand from './utils/plugins/run-command-on-demand';
+import { buildLogger } from './utils/plugins/vite-build-logger';
+
+const BROWSER_TARGET = process.env.BROWSER_TARGET || 'chrome';
+
+// Set browser target environment variable default
+process.env.BROWSER_TARGET = BROWSER_TARGET;
const root = resolve(__dirname, 'src');
const pagesDir = resolve(root, 'pages');
const assetsDir = resolve(root, 'assets');
const publicDir = resolve(__dirname, 'public');
+// Set default environment variables
+process.env.PROD = process.env.NODE_ENV === 'production' ? 'true' : 'false';
+
const isBeta = !!process.env.BETA;
if (isBeta) {
process.env.VITE_BETA_BUILD = 'true';
}
process.env.VITE_PACKAGE_VERSION = packageJson.version;
+// TODO: Debug this. If PROD is false, VITE_SENTRY_ENVIRONMENT is in production mode
if (process.env.PROD) {
process.env.VITE_SENTRY_ENVIRONMENT = 'production';
} else if (isBeta) {
@@ -162,6 +173,23 @@ export default defineConfig({
// afterServerStart: 'pnpm gulp forceDisableUseDynamicUrl',
closeBundle: 'pnpm gulp forceDisableUseDynamicUrl',
}),
+ buildLogger({
+ includeEnvVars: [
+ 'VITE_PACKAGE_VERSION',
+ 'NODE_ENV',
+ 'BROWSER_TARGET',
+ 'PROD',
+ 'VITE_SENTRY_ENVIRONMENT',
+ 'VITE_BETA_BUILD',
+ ],
+ includeTimestamp: true,
+ includeBuildTime: true,
+ customMetadata: {
+ gitBranch: () => execSync('git rev-parse --abbrev-ref HEAD').toString().trim(),
+ gitCommit: () => execSync('git rev-parse --short HEAD').toString().trim(),
+ nodeVersion: () => process.version,
+ },
+ }),
],
resolve: {
alias: {
@@ -205,6 +233,8 @@ export default defineConfig({
},
build: {
target: ['chrome120', 'edge120', 'firefox120'],
+ // NOTE: Eventually we will add this back once we support multiple browsers
+ // outDir: `dist/${process.env.BROWSER_TARGET || 'chrome'}`,
emptyOutDir: true,
reportCompressedSize: false,
sourcemap: true,