From 1576a2b69a64d36fb06e180ede184f42f19de667 Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Wed, 28 May 2025 19:56:13 -0400 Subject: [PATCH 01/11] adding even more functionality --- src/App.tsx | 2 +- src/components/Header.tsx | 1 - src/components/SaveImportModal.tsx | 93 +++++++++++++++++++++++ src/components/admin/AdminDashboard.tsx | 98 ++++++++++++++++++++++--- vite.config.ts | 3 + 5 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 src/components/SaveImportModal.tsx diff --git a/src/App.tsx b/src/App.tsx index c52247f..8ee4e2f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,7 +11,7 @@ import { ConfirmationModal } from './components/ConfirmationModal'; import { SettingsModal } from './components/SettingsModal'; import { HelpModal } from './components/HelpModal'; import { ErrorNotification } from './components/ErrorNotification'; -import { IntroModal } from './components/IntroModal'; +// import { IntroModal } from './components/IntroModal'; import { Tour } from './components/tour/Tour'; import { AuthModal } from './components/auth/AuthModal'; import { AdminDashboard } from './components/admin/AdminDashboard'; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 99872bf..ac5d3e0 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -19,7 +19,6 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm const [showExportModal, setShowExportModal] = useState(false); const [showSaveModal, setShowSaveModal] = useState(false); const [saving, setSaving] = useState(false); - const [name, setName] = useState(''); const handleSave = async (name: string) => { if (!name.trim()) { diff --git a/src/components/SaveImportModal.tsx b/src/components/SaveImportModal.tsx new file mode 100644 index 0000000..cc5c498 --- /dev/null +++ b/src/components/SaveImportModal.tsx @@ -0,0 +1,93 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { X } from 'lucide-react'; + +interface SaveImportModalProps { + onClose: () => void; + onSave: (name: string, isExample: boolean) => void; +} + +export function SaveImportModal({ onClose, onSave }: SaveImportModalProps) { + const [name, setName] = useState(''); + const modalRef = useRef(null); + const inputRef = useRef(null); + const [isExample, setIsExample] = useState(false); + + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (modalRef.current && !modalRef.current.contains(event.target as Node)) { + onClose(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + inputRef.current?.focus(); + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [onClose]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (name.trim()) { + onSave(name.trim(), isExample); + onClose(); + } + }; + + return ( +
+
+
+

Save Task List As...

+ +
+
+
+ + setName(e.target.value)} + className="w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-1 focus:ring-blue-500" + placeholder="Enter a name for your task list" + required + /> +
+
+ + + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/admin/AdminDashboard.tsx b/src/components/admin/AdminDashboard.tsx index aaf5e0f..fdcf3a6 100644 --- a/src/components/admin/AdminDashboard.tsx +++ b/src/components/admin/AdminDashboard.tsx @@ -1,7 +1,9 @@ import React, { useState, useEffect } from 'react'; -import { getTaskLists, deleteTaskList, TaskList } from '../../services/taskListService'; -import { Edit2, Trash2, Plus, ArrowLeft } from 'lucide-react'; +import { getTaskLists, deleteTaskList, TaskList, saveTaskList } from '../../services/taskListService'; +import { Edit2, Trash2, Plus, ArrowLeft, Upload } from 'lucide-react'; import { ListEditor } from './ListEditor'; +import { SaveImportModal } from '../SaveImportModal'; +import { Task } from '../../types/task'; interface AdminDashboardProps { onClose: () => void; @@ -12,6 +14,9 @@ export function AdminDashboard({ onClose, onError }: AdminDashboardProps) { const [lists, setLists] = useState([]); const [loading, setLoading] = useState(true); const [editingList, setEditingList] = useState(null); + const [showSaveImportModal, setShowSaveImportModal] = useState(false); + const [tasks, setTasks] = useState([]); + const [saving, setSaving] = useState(false); const [isCreating, setIsCreating] = useState(false); useEffect(() => { @@ -30,6 +35,62 @@ export function AdminDashboard({ onClose, onError }: AdminDashboardProps) { } }; + const onSave = () => { + setShowSaveImportModal(false); + fetchLists(); + } + + const handleImport = () => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + + input.onchange = (e) => { + const file = (e.target as HTMLInputElement).files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + try { + const content = e.target?.result as string; + const parsed = JSON.parse(content); + if (parsed.data) { + setTasks(parsed.data); + setShowSaveImportModal(true); + } + } catch (error) { + console.error('Error parsing imported file:', error); + } + }; + reader.readAsText(file); + } + }; + + input.click(); + }; + + const handleSaveImport = async (name: string, isExample: boolean) => { + if (!name.trim()) { + onError('Please enter a name for the list'); + return; + } + + if (tasks.length === 0) { + onError('Please add at least one task to the list'); + return; + } + + setSaving(true); + try { + await saveTaskList(name, tasks, isExample); + onSave(); + } catch (error) { + console.error('Error saving list:', error); + onError('Failed to save task list'); + } finally { + setSaving(false); + } + }; + const handleDelete = async (id: string) => { if (!confirm('Are you sure you want to delete this list?')) return; @@ -85,13 +146,25 @@ export function AdminDashboard({ onClose, onError }: AdminDashboardProps) {

Task Lists

- + +
+ + +
@@ -159,6 +232,13 @@ export function AdminDashboard({ onClose, onError }: AdminDashboardProps) {
+ {showSaveImportModal && ( + setShowSaveImportModal(false)} + onSave={handleSaveImport} + /> + )} + ); } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 332b43d..9857627 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,6 +8,9 @@ export default defineConfig(({ mode }) => { plugins: [react()], define: { 'import.meta.env.VITE_DEV_MODE': env.VITE_DEV_MODE + }, + server: { + port: 3174 } }; }); From 07dca5215d722fe4690e261e17f2dc86863605dd Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Thu, 29 May 2025 14:04:46 -0400 Subject: [PATCH 02/11] adding variable token support --- src/App.tsx | 78 ++++++++++++++++------- src/components/Header.tsx | 6 +- src/components/MergeVariableEditForm.tsx | 38 +++++++++++ src/components/MergeVariablesModal.tsx | 81 ++++++++++++++++++++++++ src/components/TaskInput.tsx | 2 - src/components/VariableDisplay.tsx | 26 ++++++++ src/components/VariableEditForm.tsx | 46 ++++++++++++++ src/components/VariableItem.tsx | 62 ++++++++++++++++++ src/components/VariableList.tsx | 27 ++++++++ src/components/VariableListSection.tsx | 54 ++++++++++++++++ src/components/admin/AdminDashboard.tsx | 4 +- src/components/admin/ListEditor.tsx | 5 +- src/hooks/useAuth.ts | 4 +- src/hooks/useTasks.ts | 28 ++++++++ src/services/taskListService.ts | 8 ++- src/types/variable.ts | 5 ++ 16 files changed, 442 insertions(+), 32 deletions(-) create mode 100644 src/components/MergeVariableEditForm.tsx create mode 100644 src/components/MergeVariablesModal.tsx create mode 100644 src/components/VariableDisplay.tsx create mode 100644 src/components/VariableEditForm.tsx create mode 100644 src/components/VariableItem.tsx create mode 100644 src/components/VariableList.tsx create mode 100644 src/components/VariableListSection.tsx create mode 100644 src/types/variable.ts diff --git a/src/App.tsx b/src/App.tsx index 8ee4e2f..3ba5ecd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,12 @@ import React, { useState, useEffect } from 'react'; -import { HelpCircle } from 'lucide-react'; +import { HelpCircle, Merge } from 'lucide-react'; import { useSettings } from './hooks/useSettings'; import { useTasks } from './hooks/useTasks'; import { useAuth } from './hooks/useAuth'; import { Header } from './components/Header'; import { TaskInput } from './components/TaskInput'; import { TaskListSection } from './components/TaskListSection'; +import { VariableListSection } from './components/VariableListSection'; import { Footer } from './components/Footer'; import { ConfirmationModal } from './components/ConfirmationModal'; import { SettingsModal } from './components/SettingsModal'; @@ -16,6 +17,9 @@ import { Tour } from './components/tour/Tour'; import { AuthModal } from './components/auth/AuthModal'; import { AdminDashboard } from './components/admin/AdminDashboard'; import { supabase } from './lib/supabase'; +import { Task } from './types/task'; +import { MergeVariablesModal } from './components/MergeVariablesModal'; +import { Variable } from './types/variable'; export default function App() { const [settings, setSettings] = useSettings(); @@ -28,16 +32,22 @@ export default function App() { toggleTask, deleteTask, editTask, + variables, + setVariables, + addVariable, + deleteVariable, + editVariable, reorderTasks } = useTasks(); - const [ipaddress, setIpaddress] = useState(''); - const [username, setUsername] = useState(''); const [showConfirmationModal, setShowConfirmationModal] = useState(false); const [showSettingsModal, setShowSettingsModal] = useState(false); const [showHelpModal, setShowHelpModal] = useState(false); const [showAuthModal, setShowAuthModal] = useState(false); const [showAdminDashboard, setShowAdminDashboard] = useState(false); + const [showMergedVariables, setShowMergedVariables] = useState(false); + const [showMergeVariablesModal, setShowMergeVariablesModal] = useState(false); + const [hasTokens, setHasTokens] = useState(false); const [isFirstUser, setIsFirstUser] = useState(false); const [error, setError] = useState(null); const [showTour, setShowTour] = useState(() => { @@ -72,6 +82,15 @@ export default function App() { } }, [authLoading]); + useEffect(() => { + const checker = variables.filter(variable => variable.token.length > 0); + setHasTokens(checker.length > 0); + }, [variables]); + + useEffect(() => { + + }, [showMergedVariables]); + const handleLogoClick = () => { if (tasks.length > 0) { setShowConfirmationModal(true); @@ -120,9 +139,11 @@ export default function App() { return false; }; - const mergeData = (e) => { - e.preventDefault(); - console.log(ipaddress, username); + const handleMergeVariables = () => { + setVariables(variables) + setShowMergeVariablesModal(false); + setShowMergedVariables(!showMergedVariables); + console.log("editVariable", variables) } if (showAdminDashboard && isAdmin) { @@ -153,6 +174,7 @@ export default function App() { onSettingsClick={() => setShowSettingsModal(true)} onAdminClick={() => setShowAdminDashboard(true)} tasks={tasks} + variables={variables} onImport={setTasks} isAdmin={isAdmin} onError={setError} @@ -171,24 +193,28 @@ export default function App() { googleApiKey={settings.googleApiKey} onError={setError} isAdmin={isAdmin} - /> + />
-
- -
-
- -
-
- -
+ + { variables.length > 0 ? ( +
+ +
+ ): ''}
@@ -230,6 +256,14 @@ export default function App() { isFirstUser={isFirstUser} /> )} + {showMergeVariablesModal && ( + setShowMergeVariablesModal(false)} + onMerge={handleMergeVariables} + editVariable={editVariable} + /> + )}
); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index ac5d3e0..471eaca 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -4,18 +4,20 @@ import { Task } from '../types/task'; import { ExportModal } from './ExportModal'; import { SaveModal } from './SaveModal'; import { saveTaskList } from '../services/taskListService'; +import { Variable } from '../types/variable'; interface HeaderProps { onLogoClick: () => void; onSettingsClick: () => void; onAdminClick: () => void; tasks: Task[]; + variables: Variable[]; onImport: (tasks: Task[]) => void; onError: (error: string) => void; isAdmin?: boolean; } -export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onImport, onError, isAdmin }: HeaderProps) { +export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, variables, onImport, onError, isAdmin }: HeaderProps) { const [showExportModal, setShowExportModal] = useState(false); const [showSaveModal, setShowSaveModal] = useState(false); const [saving, setSaving] = useState(false); @@ -33,7 +35,7 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm setSaving(true); try { - await saveTaskList(name, tasks, false); + await saveTaskList(name, tasks, variables, false); // onSave(); } catch (error) { console.error('Error saving list:', error); diff --git a/src/components/MergeVariableEditForm.tsx b/src/components/MergeVariableEditForm.tsx new file mode 100644 index 0000000..677dac1 --- /dev/null +++ b/src/components/MergeVariableEditForm.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState } from 'react'; +import { Variable } from '../types/variable'; + +interface MergeVariableEditFormProps { + variable: Variable; + editVariable: (id: string, token: string, value: string) => void; +} + +export function MergeVariableEditForm({ variable, editVariable }: MergeVariableEditFormProps) { + const [value, setValue] = useState(variable.value); + + useEffect(() => { + editVariable(variable.id, variable.token, value); + }, [value]); + + return ( +
+
+
+ Token: %%{variable.token}%% +
+
+ +
+
+
+ ); +} diff --git a/src/components/MergeVariablesModal.tsx b/src/components/MergeVariablesModal.tsx new file mode 100644 index 0000000..7e4f644 --- /dev/null +++ b/src/components/MergeVariablesModal.tsx @@ -0,0 +1,81 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { X } from 'lucide-react'; +import { Variable } from '../types/variable'; +import { MergeVariableEditForm } from './MergeVariableEditForm'; + +interface MergeVariablesModalProps { + variables: Variable[]; + editVariable: (id: string, token: string, value: string) => void; + onClose: () => void; + onMerge: () => void; +} + +export function MergeVariablesModal({ variables, editVariable, onClose, onMerge }: MergeVariablesModalProps) { + const modalRef = useRef(null); + const inputRef = useRef(null); + const [hasValues, setHasValues] = useState(false); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (modalRef.current && !modalRef.current.contains(event.target as Node)) { + onClose(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + inputRef.current?.focus(); + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [onClose]); + + useEffect(() => { + const checker = variables.filter(variable => variable.value.length > 0); + setHasValues(checker.length > 0); + }, [variables]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (hasValues) { + onClose(); + onMerge(); + } + }; + + return ( +
+
+
+

Set Variable Values

+ +
+
+
+ {variables.filter(variable => variable.token.length > 0).map(variable => ( + + ))} +
+
+ + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/TaskInput.tsx b/src/components/TaskInput.tsx index b467136..80cab61 100644 --- a/src/components/TaskInput.tsx +++ b/src/components/TaskInput.tsx @@ -24,7 +24,6 @@ export function TaskInput({ onAddTask }: TaskInputProps) { const [optional, setOptional] = useState(false); const handleSubmit = (e: React.FormEvent) => { - console.log('juan', code) e.preventDefault(); if (text.trim() || richText.trim()) { onAddTask( @@ -117,7 +116,6 @@ export function TaskInput({ onAddTask }: TaskInputProps) { language="javascript" code={code} onChange={(code) => { - console.log('new code', code) return setCode(code); }} /> diff --git a/src/components/VariableDisplay.tsx b/src/components/VariableDisplay.tsx new file mode 100644 index 0000000..dcaf9a9 --- /dev/null +++ b/src/components/VariableDisplay.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react'; +import { Variable } from '../types/variable'; + +interface VariableDisplayProps { + variable: Variable; + handleCopy: () => void; +} + +export function VariableDisplay({ variable, handleCopy }: VariableDisplayProps) { + + const handleDisplayCopy = () => { + if (variable.token.length) handleCopy(); + } + + return ( +
+
+
+ Token: {variable.token.length ? ( + + ): ''} +
+
+
+ ); +} diff --git a/src/components/VariableEditForm.tsx b/src/components/VariableEditForm.tsx new file mode 100644 index 0000000..df8bf41 --- /dev/null +++ b/src/components/VariableEditForm.tsx @@ -0,0 +1,46 @@ +import React, { useState } from 'react'; +import { Variable } from '../types/variable'; +import { Save } from 'lucide-react'; + +interface VariableEditFormProps { + variable: Variable; + editVariable: (id: string, token: string, value: string) => void; + setIsEditing: (isEditing: boolean) => void; +} + +export function VariableEditForm({ variable, editVariable, setIsEditing }: VariableEditFormProps) { + const [token, setToken] = useState(variable.token); + + return ( +
+
+
+ +
+
+ +
+
+
+ ); +} diff --git a/src/components/VariableItem.tsx b/src/components/VariableItem.tsx new file mode 100644 index 0000000..6dc0b34 --- /dev/null +++ b/src/components/VariableItem.tsx @@ -0,0 +1,62 @@ +import React, { useState } from 'react'; +import { Copy, Edit2, Trash2 } from 'lucide-react'; +import { Variable } from '../types/variable'; +import { VariableEditForm } from './VariableEditForm'; +import { VariableDisplay } from './VariableDisplay'; + +interface VariableItemProps { + variable: Variable; + onDeleteVariable: (id: string) => void; + editVariable: (id: string, token: string, value: string) => void; +} + +export function VariableItem({ variable, onDeleteVariable, editVariable }: VariableItemProps) { + const [isEditing, setIsEditing] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(variable.token.length ? `%%${variable.token}%%` : ''); + }; + + return ( +
+
+
+
+ {isEditing ? ( + + ): ( + + )} + +
+
+
+ {variable.token.length && !isEditing ? ( + + ): ''} + {isEditing ? '': ( + + )} + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/VariableList.tsx b/src/components/VariableList.tsx new file mode 100644 index 0000000..797159f --- /dev/null +++ b/src/components/VariableList.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Variable } from '../types/variable'; +import { VariableItem } from './VariableItem'; + +interface VariableListProps { + variables: Variable[]; + onDeleteVariable: (id: string) => void; + editVariable: (id: string, token: string, value: string) => void; +} + +export function VariableList({ variables, onDeleteVariable, editVariable }: VariableListProps) { + + return ( +
+
+ {variables.map((variable) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/VariableListSection.tsx b/src/components/VariableListSection.tsx new file mode 100644 index 0000000..2b2ef7d --- /dev/null +++ b/src/components/VariableListSection.tsx @@ -0,0 +1,54 @@ +import React, { useEffect } from 'react'; +import { Variable } from '../types/variable'; +import { VariableList } from './VariableList'; +import { PlusCircle } from 'lucide-react'; + +interface VariableListSectionProps { + variables: Variable[]; + onDeleteVariable: (id: string) => void; + addVariable: (token: string, value: string) => void; + editVariable: (id: string, token: string, value: string) => void; +} + +export function VariableListSection({ + variables, + onDeleteVariable, + addVariable, + editVariable, +}: VariableListSectionProps) { + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + addVariable('', ''); + } + + return ( + <> +
+
+

Token Variables

+

Manage your variables here.

+
+ +
+
+ +
+
+
+ {variables.length > 0 ? ( + + ): ''} + + ); +} \ No newline at end of file diff --git a/src/components/admin/AdminDashboard.tsx b/src/components/admin/AdminDashboard.tsx index fdcf3a6..e0a1033 100644 --- a/src/components/admin/AdminDashboard.tsx +++ b/src/components/admin/AdminDashboard.tsx @@ -4,6 +4,7 @@ import { Edit2, Trash2, Plus, ArrowLeft, Upload } from 'lucide-react'; import { ListEditor } from './ListEditor'; import { SaveImportModal } from '../SaveImportModal'; import { Task } from '../../types/task'; +import { Variable } from '../../types/variable'; interface AdminDashboardProps { onClose: () => void; @@ -16,6 +17,7 @@ export function AdminDashboard({ onClose, onError }: AdminDashboardProps) { const [editingList, setEditingList] = useState(null); const [showSaveImportModal, setShowSaveImportModal] = useState(false); const [tasks, setTasks] = useState([]); + const [variables, setVariables] = useState([]); const [saving, setSaving] = useState(false); const [isCreating, setIsCreating] = useState(false); @@ -81,7 +83,7 @@ export function AdminDashboard({ onClose, onError }: AdminDashboardProps) { setSaving(true); try { - await saveTaskList(name, tasks, isExample); + await saveTaskList(name, tasks, variables, isExample); onSave(); } catch (error) { console.error('Error saving list:', error); diff --git a/src/components/admin/ListEditor.tsx b/src/components/admin/ListEditor.tsx index c5116af..5094eae 100644 --- a/src/components/admin/ListEditor.tsx +++ b/src/components/admin/ListEditor.tsx @@ -4,12 +4,14 @@ import { TaskInput } from '../TaskInput'; import { TaskList } from '../TaskList'; import { Task } from '../../types/task'; import { saveTaskList } from '../../services/taskListService'; +import { Variable } from '../../types/variable'; interface ListEditorProps { list?: { id?: string; name: string; data: Task[]; + variables: Variable[]; is_example?: boolean; }; onSave: () => void; @@ -20,6 +22,7 @@ interface ListEditorProps { export function ListEditor({ list, onSave, onCancel, onError }: ListEditorProps) { const [name, setName] = useState(list?.name || ''); const [tasks, setTasks] = useState(list?.data || []); + const [variables, setVariables] = useState(list?.variables || []); const [isExample, setIsExample] = useState(list?.is_example || false); const [saving, setSaving] = useState(false); @@ -36,7 +39,7 @@ export function ListEditor({ list, onSave, onCancel, onError }: ListEditorProps) setSaving(true); try { - await saveTaskList(name, tasks, isExample); + await saveTaskList(name, tasks, variables, isExample); onSave(); } catch (error) { console.error('Error saving list:', error); diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 5cf47ad..1b8a9d4 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -11,7 +11,7 @@ export function useAuth() { // Get initial session supabase.auth.getSession().then(({ data: { session } }) => { setUser(session?.user ?? null); - setIsAdmin(session?.user?.user_metadata?.role === 'admin' ?? false); + setIsAdmin(session?.user?.user_metadata?.role === 'admin' ? true: false); setLoading(false); }); @@ -19,7 +19,7 @@ export function useAuth() { const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { const currentUser = session?.user ?? null; setUser(currentUser); - setIsAdmin(currentUser?.user_metadata?.role === 'admin' ?? false); + setIsAdmin(currentUser?.user_metadata?.role === 'admin' ? true: false); setLoading(false); }); diff --git a/src/hooks/useTasks.ts b/src/hooks/useTasks.ts index 20dccc0..a35fa70 100644 --- a/src/hooks/useTasks.ts +++ b/src/hooks/useTasks.ts @@ -1,8 +1,10 @@ import { useState } from 'react'; import { Task } from '../types/task'; +import { Variable } from '../types/variable'; export function useTasks() { const [tasks, setTasks] = useState([]); + const [variables, setVariables] = useState([]); const addTask = ( text: string, @@ -71,6 +73,27 @@ export function useTasks() { setTasks(newTasks); }; + const addVariable = (token: string, value: string) => { + const newVariable: Variable = { + id: crypto.randomUUID(), + token, + value, + }; + setVariables(prev => [...prev, newVariable]); + }; + + const deleteVariable = (id: string) => { + setVariables(prev => prev.filter((variable) => variable.id !== id)); + }; + + const editVariable = (id: string, token: string, value: string) => { + setVariables(prev => + prev.map((variable) => + variable.id === id ? { ...variable, token, value } : variable + ) + ); + }; + return { tasks, setTasks, @@ -79,6 +102,11 @@ export function useTasks() { toggleTask, deleteTask, editTask, + variables, + setVariables, + addVariable, + deleteVariable, + editVariable, reorderTasks }; } \ No newline at end of file diff --git a/src/services/taskListService.ts b/src/services/taskListService.ts index dd43ce9..ae728e5 100644 --- a/src/services/taskListService.ts +++ b/src/services/taskListService.ts @@ -1,5 +1,6 @@ import { supabase } from '../lib/supabase'; import { Task } from '../types/task'; +import { Variable } from '../types/variable'; export interface TaskList { id: string; @@ -8,9 +9,10 @@ export interface TaskList { created_at: string; user_id: string | null; is_example?: boolean; + variables: Variable[]; } -export async function saveTaskList(name: string, tasks: Task[], isExample = false) { +export async function saveTaskList(name: string, tasks: Task[], variables: Variable[], isExample = false) { // Ensure all tasks are unchecked before saving const uncheckedTasks = tasks.map(task => ({ ...task, @@ -30,6 +32,7 @@ export async function saveTaskList(name: string, tasks: Task[], isExample = fals .from('task_lists') .update({ data: uncheckedTasks, + variables: variables, is_example: isExample }) .eq('id', existingList.id) @@ -46,6 +49,7 @@ export async function saveTaskList(name: string, tasks: Task[], isExample = fals { name, data: uncheckedTasks, + variables: variables, is_example: isExample, user_id: isExample ? null : (await supabase.auth.getUser()).data.user?.id } @@ -152,7 +156,7 @@ export async function importExampleList(url: string) { throw new Error(`Failed to fetch task list: ${response.statusText}`); } const data = await response.json(); - return saveTaskList(data.name, data.data, true); + return saveTaskList(data.name, data.data, [], true); } catch (error) { console.error(`Error importing example list ${url}:`, error); throw error; diff --git a/src/types/variable.ts b/src/types/variable.ts new file mode 100644 index 0000000..6f8bf42 --- /dev/null +++ b/src/types/variable.ts @@ -0,0 +1,5 @@ +export interface Variable { + id: string; + token: string; + value: string; +} From 9553634e1803afb54310c9a9185c686de193d95b Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Thu, 29 May 2025 18:56:21 -0400 Subject: [PATCH 03/11] add variables support for user screens --- README.md | 1 + src/App.tsx | 46 +++++++++--------- src/components/Header.tsx | 9 ++-- src/components/MergeVariableEditForm.tsx | 8 ++-- src/components/MergeVariablesModal.tsx | 11 +++-- src/components/VariableDisplay.tsx | 2 +- src/components/VariableEditForm.tsx | 9 ++-- src/components/VariableItem.tsx | 13 ++--- src/components/VariableList.tsx | 14 ++---- src/components/VariableListSection.tsx | 28 ++++------- src/components/admin/AdminDashboard.tsx | 7 ++- src/components/admin/ListEditor.tsx | 9 +++- src/components/code/CodeBlock.tsx | 43 +++++++++++++++-- src/context/variableContext copy.tsx | 44 +++++++++++++++++ src/context/variableContext.tsx | 60 ++++++++++++++++++++++++ src/hooks/useTasks.ts | 28 ----------- src/main.tsx | 8 +++- src/types/variable.ts | 8 ++++ 18 files changed, 237 insertions(+), 111 deletions(-) create mode 100644 src/context/variableContext copy.tsx create mode 100644 src/context/variableContext.tsx diff --git a/README.md b/README.md index 70fa2e0..4d1641e 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ CREATE TABLE IF NOT EXISTS task_lists ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), name text NOT NULL, data jsonb NOT NULL, + variables jsonb NOT NULL, created_at timestamptz DEFAULT now(), user_id uuid REFERENCES auth.users(id), is_example boolean DEFAULT false diff --git a/src/App.tsx b/src/App.tsx index 3ba5ecd..165a860 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,7 +19,9 @@ import { AdminDashboard } from './components/admin/AdminDashboard'; import { supabase } from './lib/supabase'; import { Task } from './types/task'; import { MergeVariablesModal } from './components/MergeVariablesModal'; -import { Variable } from './types/variable'; +import { useVariables, VariableProvider } from './context/variableContext'; +// import VariableProvider, { VariableContext} from './context/variableContext'; +// import { VariablesContextType } from './types/variable'; export default function App() { const [settings, setSettings] = useSettings(); @@ -32,11 +34,6 @@ export default function App() { toggleTask, deleteTask, editTask, - variables, - setVariables, - addVariable, - deleteVariable, - editVariable, reorderTasks } = useTasks(); @@ -45,7 +42,6 @@ export default function App() { const [showHelpModal, setShowHelpModal] = useState(false); const [showAuthModal, setShowAuthModal] = useState(false); const [showAdminDashboard, setShowAdminDashboard] = useState(false); - const [showMergedVariables, setShowMergedVariables] = useState(false); const [showMergeVariablesModal, setShowMergeVariablesModal] = useState(false); const [hasTokens, setHasTokens] = useState(false); const [isFirstUser, setIsFirstUser] = useState(false); @@ -55,6 +51,8 @@ export default function App() { return !hasSeenTour && !settings.googleApiKey; }); + const { variables, setVariables, mergingVariables, setMergingVariables } = useVariables(); + useEffect(() => { // Check if this is the first user const checkFirstUser = async () => { @@ -89,7 +87,7 @@ export default function App() { useEffect(() => { - }, [showMergedVariables]); + }, [mergingVariables]); const handleLogoClick = () => { if (tasks.length > 0) { @@ -142,8 +140,9 @@ export default function App() { const handleMergeVariables = () => { setVariables(variables) setShowMergeVariablesModal(false); - setShowMergedVariables(!showMergedVariables); - console.log("editVariable", variables) + setMergingVariables(!mergingVariables); + console.log("editVariable", variables); + // TODO show merged token variables in codeblocks } if (showAdminDashboard && isAdmin) { @@ -159,6 +158,15 @@ export default function App() { ); } + const handleMergeVariableTrigger = () => { + if (mergingVariables) { + setMergingVariables(false); + // TODO remove showing the merged token variables in codeblocks + } else { + setShowMergeVariablesModal(true); + } + } + return ( <>
@@ -174,7 +182,6 @@ export default function App() { onSettingsClick={() => setShowSettingsModal(true)} onAdminClick={() => setShowAdminDashboard(true)} tasks={tasks} - variables={variables} onImport={setTasks} isAdmin={isAdmin} onError={setError} @@ -193,25 +200,20 @@ export default function App() { googleApiKey={settings.googleApiKey} onError={setError} isAdmin={isAdmin} - /> + />
- - { variables.length > 0 ? ( + + { variables?.length > 0 ? (
): ''} @@ -258,10 +260,8 @@ export default function App() { )} {showMergeVariablesModal && ( setShowMergeVariablesModal(false)} onMerge={handleMergeVariables} - editVariable={editVariable} /> )}
diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 471eaca..afc76aa 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -4,24 +4,27 @@ import { Task } from '../types/task'; import { ExportModal } from './ExportModal'; import { SaveModal } from './SaveModal'; import { saveTaskList } from '../services/taskListService'; -import { Variable } from '../types/variable'; +import { useVariables } from '../context/variableContext'; +// import { VariableContext } from '../context/variableContext'; +// import { VariablesContextType } from '../types/variable'; interface HeaderProps { onLogoClick: () => void; onSettingsClick: () => void; onAdminClick: () => void; tasks: Task[]; - variables: Variable[]; onImport: (tasks: Task[]) => void; onError: (error: string) => void; isAdmin?: boolean; } -export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, variables, onImport, onError, isAdmin }: HeaderProps) { +export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onImport, onError, isAdmin }: HeaderProps) { const [showExportModal, setShowExportModal] = useState(false); const [showSaveModal, setShowSaveModal] = useState(false); const [saving, setSaving] = useState(false); + const { variables } = useVariables(); + const handleSave = async (name: string) => { if (!name.trim()) { onError('Please enter a name for the list'); diff --git a/src/components/MergeVariableEditForm.tsx b/src/components/MergeVariableEditForm.tsx index 677dac1..d61ebf6 100644 --- a/src/components/MergeVariableEditForm.tsx +++ b/src/components/MergeVariableEditForm.tsx @@ -1,14 +1,16 @@ import React, { useEffect, useState } from 'react'; -import { Variable } from '../types/variable'; +// import { Variable } from '../types/variable'; +import { useVariables, Variable } from '../context/variableContext'; interface MergeVariableEditFormProps { variable: Variable; - editVariable: (id: string, token: string, value: string) => void; } -export function MergeVariableEditForm({ variable, editVariable }: MergeVariableEditFormProps) { +export function MergeVariableEditForm({ variable }: MergeVariableEditFormProps) { const [value, setValue] = useState(variable.value); + const { editVariable } = useVariables(); + useEffect(() => { editVariable(variable.id, variable.token, value); }, [value]); diff --git a/src/components/MergeVariablesModal.tsx b/src/components/MergeVariablesModal.tsx index 7e4f644..7cb4d8a 100644 --- a/src/components/MergeVariablesModal.tsx +++ b/src/components/MergeVariablesModal.tsx @@ -1,20 +1,21 @@ import React, { useState, useEffect, useRef } from 'react'; import { X } from 'lucide-react'; -import { Variable } from '../types/variable'; +import { useVariables } from '../context/variableContext'; import { MergeVariableEditForm } from './MergeVariableEditForm'; +// import VariableContext from '../context/variableContext'; interface MergeVariablesModalProps { - variables: Variable[]; - editVariable: (id: string, token: string, value: string) => void; onClose: () => void; onMerge: () => void; } -export function MergeVariablesModal({ variables, editVariable, onClose, onMerge }: MergeVariablesModalProps) { +export function MergeVariablesModal({ onClose, onMerge }: MergeVariablesModalProps) { const modalRef = useRef(null); const inputRef = useRef(null); const [hasValues, setHasValues] = useState(false); + const { variables } = useVariables(); + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (modalRef.current && !modalRef.current.contains(event.target as Node)) { @@ -55,7 +56,7 @@ export function MergeVariablesModal({ variables, editVariable, onClose, onMerge
{variables.filter(variable => variable.token.length > 0).map(variable => ( - + ))}
diff --git a/src/components/VariableDisplay.tsx b/src/components/VariableDisplay.tsx index dcaf9a9..4fbd81f 100644 --- a/src/components/VariableDisplay.tsx +++ b/src/components/VariableDisplay.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { Variable } from '../types/variable'; interface VariableDisplayProps { diff --git a/src/components/VariableEditForm.tsx b/src/components/VariableEditForm.tsx index df8bf41..1acf487 100644 --- a/src/components/VariableEditForm.tsx +++ b/src/components/VariableEditForm.tsx @@ -1,16 +1,19 @@ import React, { useState } from 'react'; -import { Variable } from '../types/variable'; +// import { Variable, VariablesContextType } from '../types/variable'; import { Save } from 'lucide-react'; +import { useVariables, Variable } from '../context/variableContext'; +// import { VariableContext } from '../context/variableContext'; interface VariableEditFormProps { variable: Variable; - editVariable: (id: string, token: string, value: string) => void; setIsEditing: (isEditing: boolean) => void; } -export function VariableEditForm({ variable, editVariable, setIsEditing }: VariableEditFormProps) { +export function VariableEditForm({ variable, setIsEditing }: VariableEditFormProps) { const [token, setToken] = useState(variable.token); + const { editVariable } = useVariables(); + return (
diff --git a/src/components/VariableItem.tsx b/src/components/VariableItem.tsx index 6dc0b34..da30fb6 100644 --- a/src/components/VariableItem.tsx +++ b/src/components/VariableItem.tsx @@ -1,18 +1,19 @@ import React, { useState } from 'react'; import { Copy, Edit2, Trash2 } from 'lucide-react'; -import { Variable } from '../types/variable'; +// import { Variable, VariablesContextType } from '../types/variable'; import { VariableEditForm } from './VariableEditForm'; import { VariableDisplay } from './VariableDisplay'; +import { useVariables, Variable } from '../context/variableContext'; interface VariableItemProps { variable: Variable; - onDeleteVariable: (id: string) => void; - editVariable: (id: string, token: string, value: string) => void; } -export function VariableItem({ variable, onDeleteVariable, editVariable }: VariableItemProps) { +export function VariableItem({ variable }: VariableItemProps) { const [isEditing, setIsEditing] = useState(false); + const { deleteVariable } = useVariables(); + const handleCopy = async () => { await navigator.clipboard.writeText(variable.token.length ? `%%${variable.token}%%` : ''); }; @@ -23,7 +24,7 @@ export function VariableItem({ variable, onDeleteVariable, editVariable }: Varia
{isEditing ? ( - + ): ( )} @@ -50,7 +51,7 @@ export function VariableItem({ variable, onDeleteVariable, editVariable }: Varia )}
); -} +} diff --git a/src/context/variableContext copy.tsx b/src/context/variableContext copy.tsx new file mode 100644 index 0000000..273e36d --- /dev/null +++ b/src/context/variableContext copy.tsx @@ -0,0 +1,44 @@ +import React, { createContext, useState } from "react"; +import { Variable } from "../types/variable"; + +const defaultVariables: Variable[] = []; + +export const VariableContext = createContext({ + variables: defaultVariables, +}); + +interface VariableProviderProps { + children: React.ReactNode; +} + +const VariableProvider = ({ children }: VariableProviderProps) => { + const [variables, setVariables] = useState(defaultVariables); // Initialize variables as an empty array + + const addVariable = (token: string, value: string) => { + const newVariable: Variable = { + id: crypto.randomUUID(), + token, + value, + }; + setVariables([...variables, newVariable]); + }; + + const deleteVariable = (id: string) => { + setVariables(variables.filter((variable) => variable.id !== id)); + }; + + const editVariable = (id: string, token: string, value: string) => { + setVariables(variables.map((variable) => + variable.id === id ? { ...variable, token, value } : variable + ) + ); + }; + + return ( + + {children} + + ); +}; + +export default VariableProvider; diff --git a/src/context/variableContext.tsx b/src/context/variableContext.tsx new file mode 100644 index 0000000..fd315fd --- /dev/null +++ b/src/context/variableContext.tsx @@ -0,0 +1,60 @@ +import React, { createContext, useState, useContext } from "react"; + + +export interface Variable { + id: string; + token: string; + value: string; +} + +interface VariablesContextType { + variables: Variable[]; + mergingVariables: boolean; + setMergingVariables: (mergingVariables: boolean) => void; + setVariables: (variables: Variable[]) => void; + addVariable: (token: string, value: string) => void; + deleteVariable: (id: string) => void; + editVariable: (id: string, token: string, value: string) => void; +} + +const VariableContext = createContext(undefined); + +export const VariableProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const [variables, setVariables] = useState([]); + const [mergingVariables, setMergingVariables] = useState(false); + + const addVariable = (token: string, value: string) => { + const newVariable: Variable = { + id: crypto.randomUUID(), + token, + value, + }; + setVariables([...variables, newVariable]); + }; + + const deleteVariable = (id: string) => { + setVariables(variables.filter((variable) => variable.id !== id)); + }; + + const editVariable = (id: string, token: string, value: string) => { + setVariables(variables.map((variable) => + variable.id === id ? { ...variable, token, value } : variable + ) + ); + }; + + return ( + + {children} + + ); +}; + +export const useVariables = (): VariablesContextType => { + const context = useContext(VariableContext); + if (!context) throw new Error("useVariables must be used within a VariableProvider"); + + return context; +} diff --git a/src/hooks/useTasks.ts b/src/hooks/useTasks.ts index a35fa70..20dccc0 100644 --- a/src/hooks/useTasks.ts +++ b/src/hooks/useTasks.ts @@ -1,10 +1,8 @@ import { useState } from 'react'; import { Task } from '../types/task'; -import { Variable } from '../types/variable'; export function useTasks() { const [tasks, setTasks] = useState([]); - const [variables, setVariables] = useState([]); const addTask = ( text: string, @@ -73,27 +71,6 @@ export function useTasks() { setTasks(newTasks); }; - const addVariable = (token: string, value: string) => { - const newVariable: Variable = { - id: crypto.randomUUID(), - token, - value, - }; - setVariables(prev => [...prev, newVariable]); - }; - - const deleteVariable = (id: string) => { - setVariables(prev => prev.filter((variable) => variable.id !== id)); - }; - - const editVariable = (id: string, token: string, value: string) => { - setVariables(prev => - prev.map((variable) => - variable.id === id ? { ...variable, token, value } : variable - ) - ); - }; - return { tasks, setTasks, @@ -102,11 +79,6 @@ export function useTasks() { toggleTask, deleteTask, editTask, - variables, - setVariables, - addVariable, - deleteVariable, - editVariable, reorderTasks }; } \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index e87bc0f..4749b44 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,9 +2,13 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App.tsx'; import './index.css'; +import { VariableProvider } from './context/variableContext.tsx'; + createRoot(document.getElementById('root')!).render( - + + + -); +); diff --git a/src/types/variable.ts b/src/types/variable.ts index 6f8bf42..30e0b62 100644 --- a/src/types/variable.ts +++ b/src/types/variable.ts @@ -3,3 +3,11 @@ export interface Variable { token: string; value: string; } + +export interface VariablesContextType { + variables: Variable[]; + setVariables: (variables: Variable[]) => void; + addVariable: (token: string, value: string) => void; + deleteVariable: (id: string) => void; + editVariable: (id: string, token: string, value: string) => void; +} From 27421b8618cf84d54c7ca6339b81f35ce42c6de7 Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Thu, 29 May 2025 19:04:41 -0400 Subject: [PATCH 04/11] add subtle styling to merge mode --- src/App.tsx | 4 ++-- src/components/code/CodeBlock.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 165a860..ccb6c8d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -208,12 +208,12 @@ export default function App() {
): ''} diff --git a/src/components/code/CodeBlock.tsx b/src/components/code/CodeBlock.tsx index 6dee557..d97de17 100644 --- a/src/components/code/CodeBlock.tsx +++ b/src/components/code/CodeBlock.tsx @@ -31,7 +31,7 @@ export function CodeBlock({ code, language }: CodeBlockProps) { } else { setRenderedCode(code); } - }, [code, variables]); + }, [code, variables, mergingVariables]); const renderCodeBlockCode = (code: string): string => { function replaceTokenIgnoreCase(input: string, tokenDefinition: {token: string, value: string}): string { From 00df68283111dc43fac28edf55579fa7ab811962 Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Fri, 30 May 2025 13:53:51 -0400 Subject: [PATCH 05/11] completed adding the token variables section. I also updated support for importing tasklists that don't have variables defined in the json file. --- src/App.tsx | 46 +++-- src/components/AITaskGenerator.tsx | 5 +- src/components/Header.tsx | 48 +++--- src/components/SaveModal.tsx | 14 +- src/components/TaskListSection.tsx | 8 +- src/components/TaskListSelector.tsx | 9 +- src/components/admin/AdminDashboard.tsx | 15 +- src/components/admin/ListEditor.tsx | 216 ++++++++++++++++-------- src/context/variableContext copy.tsx | 44 ----- src/types/importData.ts | 7 + 10 files changed, 255 insertions(+), 157 deletions(-) delete mode 100644 src/context/variableContext copy.tsx create mode 100644 src/types/importData.ts diff --git a/src/App.tsx b/src/App.tsx index ccb6c8d..3d8e0e4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,9 +19,8 @@ import { AdminDashboard } from './components/admin/AdminDashboard'; import { supabase } from './lib/supabase'; import { Task } from './types/task'; import { MergeVariablesModal } from './components/MergeVariablesModal'; -import { useVariables, VariableProvider } from './context/variableContext'; -// import VariableProvider, { VariableContext} from './context/variableContext'; -// import { VariablesContextType } from './types/variable'; +import { useVariables, Variable } from './context/variableContext'; +import { ImportDataType } from './types/importData'; export default function App() { const [settings, setSettings] = useSettings(); @@ -141,19 +140,18 @@ export default function App() { setVariables(variables) setShowMergeVariablesModal(false); setMergingVariables(!mergingVariables); - console.log("editVariable", variables); - // TODO show merged token variables in codeblocks } + const handleClearAll = () => { + setTasks([]); + setVariables([]); + } + if (showAdminDashboard && isAdmin) { return ( setShowAdminDashboard(false)} onError={setError} - onEditList={(list) => { - setTasks(list.data); - setShowAdminDashboard(false); - }} /> ); } @@ -161,12 +159,31 @@ export default function App() { const handleMergeVariableTrigger = () => { if (mergingVariables) { setMergingVariables(false); - // TODO remove showing the merged token variables in codeblocks } else { setShowMergeVariablesModal(true); } } + const isImportDataType = (data: any): data is ImportDataType => { + return Array.isArray(data.tasks) && Array.isArray(data.variables); + }; + + const handleOnImport = (data: ImportDataType | Task[]) => { + if (isImportDataType(data)) { + setTasks(data.tasks); + setVariables(data.variables); + } else { + setTasks(data); + setVariables([]); + } + setMergingVariables(false); + }; + + const handleImportTaskList = (tasks: Task[], variables: Variable[]) => { + setTasks(tasks); + setVariables(variables); + } + return ( <>
@@ -182,9 +199,10 @@ export default function App() { onSettingsClick={() => setShowSettingsModal(true)} onAdminClick={() => setShowAdminDashboard(true)} tasks={tasks} - onImport={setTasks} - isAdmin={isAdmin} + onImport={handleOnImport} onError={setError} + onClear={handleClearAll} + isAdmin={isAdmin} />
@@ -196,13 +214,13 @@ export default function App() { onDuplicate={duplicateTask} onReorder={reorderTasks} onCheckAllSubTasks={checkAllSubTasks} - onImportTaskList={setTasks} + onImportTaskList={handleImportTaskList} googleApiKey={settings.googleApiKey} onError={setError} isAdmin={isAdmin} />
-
+
{ variables?.length > 0 ? (
diff --git a/src/components/AITaskGenerator.tsx b/src/components/AITaskGenerator.tsx index fb0acba..127de6c 100644 --- a/src/components/AITaskGenerator.tsx +++ b/src/components/AITaskGenerator.tsx @@ -2,10 +2,11 @@ import React, { useState } from 'react'; import { Send, Paperclip } from 'lucide-react'; import { generateTasks } from '../services/aiService'; import { ChatMessage } from '../types/chat'; +import { Variable } from '../types/variable'; interface AITaskGeneratorProps { apiKey: string; - onTasksGenerated: (tasks: any[]) => void; + onTasksGenerated: (tasks: any[], variables: Variable[]) => void; onError: (error: string) => void; } @@ -78,7 +79,7 @@ export function AITaskGenerator({ apiKey, onTasksGenerated, onError }: AITaskGen createdAt: new Date(task.createdAt || new Date()), id: task.id || crypto.randomUUID() })); - onTasksGenerated(newTasks); + onTasksGenerated(newTasks, parsedData.data.variables); setChatInput(''); setSelectedFile(null); setSelectedFileName(null); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index afc76aa..fa1a6e8 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,31 +1,31 @@ import React, { useState } from 'react'; -import { CheckSquare, Settings, Shield, Download, Save, Upload } from 'lucide-react'; +import { CheckSquare, Settings, Shield, Download, Save, Upload, XSquare } from 'lucide-react'; import { Task } from '../types/task'; import { ExportModal } from './ExportModal'; import { SaveModal } from './SaveModal'; import { saveTaskList } from '../services/taskListService'; import { useVariables } from '../context/variableContext'; -// import { VariableContext } from '../context/variableContext'; -// import { VariablesContextType } from '../types/variable'; +import { ImportDataType } from '../types/importData'; interface HeaderProps { onLogoClick: () => void; onSettingsClick: () => void; onAdminClick: () => void; tasks: Task[]; - onImport: (tasks: Task[]) => void; + onImport: (data: ImportDataType) => void; onError: (error: string) => void; + onClear: () => void; isAdmin?: boolean; } -export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onImport, onError, isAdmin }: HeaderProps) { +export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onImport, onError, onClear, isAdmin }: HeaderProps) { const [showExportModal, setShowExportModal] = useState(false); const [showSaveModal, setShowSaveModal] = useState(false); const [saving, setSaving] = useState(false); const { variables } = useVariables(); - const handleSave = async (name: string) => { + const handleSave = async (name: string, isExample: boolean) => { if (!name.trim()) { onError('Please enter a name for the list'); return; @@ -38,7 +38,7 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm setSaving(true); try { - await saveTaskList(name, tasks, variables, false); + await saveTaskList(name, tasks, variables, isExample); // onSave(); } catch (error) { console.error('Error saving list:', error); @@ -49,7 +49,7 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm }; const handleExport = (name: string) => { - const dataStr = JSON.stringify({ name, data: tasks }, null, 2); + const dataStr = JSON.stringify({ name, data: { tasks, variables } }, null, 2); const dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr); const sanitizedName = name.replace(/[^a-z0-9]/gi, '_').toLowerCase(); @@ -94,17 +94,7 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm
- {isAdmin && ( - - )} + + {isAdmin && ( + + )} +
{isAdmin && (
+ - setName(e.target.value)} - placeholder="Enter list name" - className="text-2xl font-semibold text-gray-900 border-none focus:outline-none focus:ring-0 bg-transparent" - /> -
-
- -
+ {showMergeVariablesModal && ( + setShowMergeVariablesModal(false)} + onMerge={handleMergeVariables} + /> + )} + ); } \ No newline at end of file diff --git a/src/context/variableContext copy.tsx b/src/context/variableContext copy.tsx deleted file mode 100644 index 273e36d..0000000 --- a/src/context/variableContext copy.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { createContext, useState } from "react"; -import { Variable } from "../types/variable"; - -const defaultVariables: Variable[] = []; - -export const VariableContext = createContext({ - variables: defaultVariables, -}); - -interface VariableProviderProps { - children: React.ReactNode; -} - -const VariableProvider = ({ children }: VariableProviderProps) => { - const [variables, setVariables] = useState(defaultVariables); // Initialize variables as an empty array - - const addVariable = (token: string, value: string) => { - const newVariable: Variable = { - id: crypto.randomUUID(), - token, - value, - }; - setVariables([...variables, newVariable]); - }; - - const deleteVariable = (id: string) => { - setVariables(variables.filter((variable) => variable.id !== id)); - }; - - const editVariable = (id: string, token: string, value: string) => { - setVariables(variables.map((variable) => - variable.id === id ? { ...variable, token, value } : variable - ) - ); - }; - - return ( - - {children} - - ); -}; - -export default VariableProvider; diff --git a/src/types/importData.ts b/src/types/importData.ts new file mode 100644 index 0000000..3c79e27 --- /dev/null +++ b/src/types/importData.ts @@ -0,0 +1,7 @@ +import { Task } from "./task"; +import { Variable } from "./variable"; + +export interface ImportDataType { + tasks: Task[], + variables: Variable[]; +} From 213483fa35d57c117f96835ea351d590ed239c0d Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Fri, 30 May 2025 18:46:20 -0400 Subject: [PATCH 06/11] making small modifications to ensure a good UX --- src/App.tsx | 1 + src/components/Header.tsx | 3 ++- src/components/admin/ListEditor.tsx | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 3d8e0e4..fb190e4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -145,6 +145,7 @@ export default function App() { const handleClearAll = () => { setTasks([]); setVariables([]); + setMergingVariables(false); } if (showAdminDashboard && isAdmin) { diff --git a/src/components/Header.tsx b/src/components/Header.tsx index fa1a6e8..b3be0a4 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -23,7 +23,7 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm const [showSaveModal, setShowSaveModal] = useState(false); const [saving, setSaving] = useState(false); - const { variables } = useVariables(); + const { variables, setMergingVariables } = useVariables(); const handleSave = async (name: string, isExample: boolean) => { if (!name.trim()) { @@ -75,6 +75,7 @@ export function Header({ onLogoClick, onSettingsClick, onAdminClick, tasks, onIm if (parsed.data) { onImport(parsed.data); } + setMergingVariables(false); } catch (error) { console.error('Error parsing imported file:', error); } diff --git a/src/components/admin/ListEditor.tsx b/src/components/admin/ListEditor.tsx index fbeea16..fd4a982 100644 --- a/src/components/admin/ListEditor.tsx +++ b/src/components/admin/ListEditor.tsx @@ -36,6 +36,7 @@ export function ListEditor({ list, onSave, onCancel, onError }: ListEditorProps) useEffect(() => { setVariables(list?.variables || []); + setMergingVariables(false); }, []); useEffect(() => { From c8799fba4dc578d45a1cda3ef1bd597fbfd8bb73 Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Fri, 30 May 2025 19:45:59 -0400 Subject: [PATCH 07/11] making the title grow dynamically --- src/components/admin/ListEditor.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/admin/ListEditor.tsx b/src/components/admin/ListEditor.tsx index fd4a982..24bb83a 100644 --- a/src/components/admin/ListEditor.tsx +++ b/src/components/admin/ListEditor.tsx @@ -61,6 +61,9 @@ export function ListEditor({ list, onSave, onCancel, onError }: ListEditorProps) setSaving(true); try { + // TODO + console.log('name', name); + await saveTaskList(name, tasks, variables, isExample); onSave(); } catch (error) { @@ -203,7 +206,7 @@ export function ListEditor({ list, onSave, onCancel, onError }: ListEditorProps)
-
+
From cf1d7c5bcca597ac51e55bd908c9285497d63189 Mon Sep 17 00:00:00 2001 From: Juan Pena <7140890+jerrypena1@users.noreply.github.com> Date: Sat, 31 May 2025 08:29:12 -0400 Subject: [PATCH 08/11] Improving the UI and cleaning up the code --- src/components/MergeVariableEditForm.tsx | 9 +++++++-- src/components/MergeVariablesModal.tsx | 4 ++-- src/components/VariableDisplay.tsx | 9 +++++++-- src/components/VariableEditForm.tsx | 19 ++++++++++++++++--- src/components/VariableItem.tsx | 5 ++--- src/components/VariableList.tsx | 2 -- src/components/VariableListSection.tsx | 6 ++---- src/components/admin/AdminDashboard.tsx | 2 -- src/components/admin/ListEditor.tsx | 2 -- src/context/variableContext.tsx | 12 +++++++----- src/types/variable.ts | 13 ------------- 11 files changed, 43 insertions(+), 40 deletions(-) delete mode 100644 src/types/variable.ts diff --git a/src/components/MergeVariableEditForm.tsx b/src/components/MergeVariableEditForm.tsx index d61ebf6..947eb46 100644 --- a/src/components/MergeVariableEditForm.tsx +++ b/src/components/MergeVariableEditForm.tsx @@ -12,15 +12,20 @@ export function MergeVariableEditForm({ variable }: MergeVariableEditFormProps) const { editVariable } = useVariables(); useEffect(() => { - editVariable(variable.id, variable.token, value); + editVariable(variable.id, variable.token, value, variable.description); }, [value]); return (
- Token: %%{variable.token}%% + Token: %%{variable.token}%%
+ { variable.description && ( +
+ Description: {variable.description} +
+ )}