Skip to content

Commit

Permalink
Fix notes tag issue and add action button
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvel committed Nov 8, 2024
1 parent 7ad5219 commit 62325a0
Show file tree
Hide file tree
Showing 19 changed files with 589 additions and 381 deletions.
78 changes: 74 additions & 4 deletions app/frontend/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Project } from "./entities/Project";
import { Task } from "./entities/Task";
import { useDataContext } from "./contexts/DataContext";
import { User } from "./entities/User";
import { BookOpenIcon, ClipboardIcon, FolderIcon, PlusCircleIcon, Squares2X2Icon } from "@heroicons/react/24/solid";

interface LayoutProps {
currentUser: User;
Expand Down Expand Up @@ -74,6 +75,35 @@ const Layout: React.FC<LayoutProps> = ({
return () => window.removeEventListener("resize", handleResize);
}, []);

const [isFABDropdownOpen, setFABDropdownOpen] = useState(false);

const handleFABDropdownSelect = (type: string) => {
switch (type) {
case 'Task':
openTaskModal();
break;
case 'Project':
openProjectModal();
break;
case 'Note':
openNoteModal(null);
break;
case 'Area':
openAreaModal(null);
break;
default:
break;
}
setFABDropdownOpen(false);
};

const fabDropdownItems = [
{ label: 'Task', icon: <ClipboardIcon className="h-5 w-5 mr-2" /> },
{ label: 'Project', icon: <FolderIcon className="h-5 w-5 mr-2" /> },
{ label: 'Note', icon: <BookOpenIcon className="h-5 w-5 mr-2" /> },
{ label: 'Area', icon: <Squares2X2Icon className="h-5 w-5 mr-2" /> },
];

const openNoteModal = (note: Note | null = null) => {
setSelectedNote(note);
setIsNoteModalOpen(true);
Expand Down Expand Up @@ -279,7 +309,7 @@ const Layout: React.FC<LayoutProps> = ({
currentUser={currentUser}
isDarkMode={isDarkMode}
toggleDarkMode={toggleDarkMode}
openTaskModal={() => openTaskModal()}
openTaskModal={openTaskModal}
openProjectModal={openProjectModal}
openNoteModal={openNoteModal}
openAreaModal={openAreaModal}
Expand All @@ -293,23 +323,63 @@ const Layout: React.FC<LayoutProps> = ({
className={`transition-all duration-300 ease-in-out ${mainContentMarginLeft}`}
>
<div className="flex flex-col bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100 min-h-screen overflow-y-auto">
<div className="flex-grow p-6 pt-24">
<div className="flex-grow py-6 px-2 md:px-6 pt-24">
<div className="w-full max-w-5xl mx-auto">{children}</div>
</div>
</div>
</div>

{/* Floating Action Button */}
<button
onClick={() => setFABDropdownOpen(!isFABDropdownOpen)}
className="bg-blue-500 hover:bg-blue-600 text-white rounded-full p-4 shadow-lg focus:outline-none transform transition-transform duration-200 hover:scale-110"
aria-label="Open Create New Dropdown"
>
<PlusCircleIcon className="h-6 w-6" />
</button>

{isFABDropdownOpen && (
<div className="absolute right-0 mt-2 w-48 rounded-md shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5 z-20">
<ul className="py-1" role="menu" aria-orientation="vertical">
{fabDropdownItems.map(({ label, icon }) => (
<li
key={label}
className="block px-4 py-2 text-sm text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-700 cursor-pointer flex items-center"
onClick={() => handleFABDropdownSelect(label)}
role="menuitem"
>
{icon}
{label}
</li>
))}
</ul>
</div>
)}

{/* Modals */}
{isTaskModalOpen && (
<TaskModal
isOpen={isTaskModalOpen}
onClose={closeTaskModal}
task={newTask || { id: undefined, name: '', status: 'not_started', project_id: undefined, tags: [] }}
task={
newTask || {
id: undefined,
name: '',
status: 'not_started',
project_id: undefined,
tags: [],
}
}
onSave={handleSaveTask}
onDelete={() => {}}
projects={[]} // Provide project list as necessary
onCreateProject={async (name: string) => {
return { id: Math.random(), name, active: true, pin_to_sidebar: false }; // Ensure all required fields are covered
return {
id: Math.random(),
name,
active: true,
pin_to_sidebar: false,
}; // Ensure all required fields are covered
}}
/>
)}
Expand Down
148 changes: 69 additions & 79 deletions app/frontend/components/Area/AreaModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useState, useEffect, useRef } from 'react';
import { Area } from '../../entities/Area';
import { useDataContext } from '../../contexts/DataContext';
import { XMarkIcon } from '@heroicons/react/24/outline';
import { useToast } from '../Shared/ToastContext';

interface AreaModalProps {
isOpen: boolean;
Expand All @@ -9,7 +11,7 @@ interface AreaModalProps {
area?: Area | null;
}

const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area }) => {
const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area, onSave }) => {
const { createArea, updateArea } = useDataContext();
const [formData, setFormData] = useState<Area>({
id: area?.id || 0,
Expand All @@ -21,6 +23,8 @@ const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area }) => {
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const [isClosing, setIsClosing] = useState(false);

const { showSuccessToast, showErrorToast } = useToast();

useEffect(() => {
if (isOpen) {
setFormData({
Expand Down Expand Up @@ -75,9 +79,7 @@ const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area }) => {
}));
};

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();

const handleSubmit = async () => {
if (!formData.name.trim()) {
setError('Area name is required.');
return;
Expand All @@ -89,12 +91,16 @@ const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area }) => {
try {
if (formData.id && formData.id !== 0) {
await updateArea(formData.id, formData);
showSuccessToast('Area updated successfully!');
} else {
await createArea(formData);
showSuccessToast('Area created successfully!');
}
onSave(formData);
handleClose();
} catch (err) {
setError((err as Error).message);
showErrorToast('Failed to save area.');
} finally {
setIsSubmitting(false);
}
Expand All @@ -119,91 +125,75 @@ const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area }) => {
>
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
className={`bg-white dark:bg-gray-800 w-full sm:max-w-md mx-auto overflow-hidden h-screen sm:h-auto flex flex-col transform transition-transform duration-300 ${
className={`bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-800 sm:rounded-lg sm:shadow-2xl w-full sm:max-w-md overflow-hidden transform transition-transform duration-300 ${
isClosing ? 'scale-95' : 'scale-100'
} sm:rounded-lg sm:shadow-2xl`}
} h-screen sm:h-auto flex flex-col`}
style={{
maxHeight: 'calc(100vh - 4rem)',
}}
>
<form className="flex flex-col flex-1" onSubmit={handleSubmit}>
<fieldset className="flex-1 overflow-y-auto p-4 space-y-4">
<h3
id="modal-title"
className="text-lg font-medium text-gray-900 dark:text-white"
>
{formData.id && formData.id !== 0 ? 'Edit Area' : 'Create Area'}
</h3>

{/* Area Name */}
<div>
<label
htmlFor="areaName"
className="block text-sm font-medium text-gray-700 dark:text-gray-300"
>
Area Name
</label>
<input
type="text"
id="areaName"
name="name"
value={formData.name}
onChange={handleChange}
required
className="mt-1 block w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-100"
placeholder="Enter area name"
/>
<form className="flex flex-col flex-1">
<fieldset className="flex flex-col flex-1">
<div className="p-4 space-y-3 flex-1 text-sm overflow-y-auto">
{/* Area Name */}
<div className="py-4">
<input
type="text"
id="areaName"
name="name"
value={formData.name}
onChange={handleChange}
required
className="block w-full text-xl font-semibold dark:bg-gray-800 text-black dark:text-white border-b-2 border-gray-200 dark:border-gray-900 focus:outline-none shadow-sm py-2"
placeholder="Enter area name"
/>
</div>

{/* Area Description */}
<div className="pb-3">
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
Description
</label>
<textarea
id="areaDescription"
name="description"
value={formData.description}
onChange={handleChange}
rows={4}
className="block w-full rounded-md shadow-sm p-3 text-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500 transition duration-150 ease-in-out"
placeholder="Enter area description"
/>
</div>

{/* Error Message */}
{error && <div className="text-red-500">{error}</div>}
</div>

{/* Area Description */}
<div>
<label
htmlFor="areaDescription"
className="block text-sm font-medium text-gray-700 dark:text-gray-300"
{/* Action Buttons */}
<div className="p-3 flex-shrink-0 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-2">
<button
type="button"
onClick={handleClose}
className="px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-200 rounded-md hover:bg-gray-300 dark:hover:bg-gray-600 focus:outline-none transition duration-150 ease-in-out"
>
Description
</label>
<textarea
id="areaDescription"
name="description"
value={formData.description}
onChange={handleChange}
rows={3}
className="mt-1 block w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-100"
placeholder="Enter area description"
/>
Cancel
</button>
<button
type="button"
onClick={handleSubmit}
disabled={isSubmitting}
className={`px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 focus:outline-none transition duration-150 ease-in-out ${
isSubmitting ? 'opacity-50 cursor-not-allowed' : ''
}`}
>
{isSubmitting
? 'Submitting...'
: formData.id && formData.id !== 0
? 'Update Area'
: 'Create Area'}
</button>
</div>

{/* Error Message */}
{error && <div className="text-red-500">{error}</div>}
</fieldset>

{/* Modal Actions */}
<div className="flex justify-end items-center p-4 border-t border-gray-200 dark:border-gray-700 flex-shrink-0">
<button
type="button"
onClick={handleClose}
className="px-4 mr-2 py-2 bg-gray-200 text-gray-700 rounded hover:bg-gray-300 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600 focus:outline-none"
>
Cancel
</button>
<button
type="submit"
disabled={isSubmitting}
className={`px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 focus:outline-none ${
isSubmitting ? 'opacity-50 cursor-not-allowed' : ''
}`}
>
{isSubmitting
? 'Submitting...'
: formData.id && formData.id !== 0
? 'Update Area'
: 'Create Area'}
</button>
</div>
</form>
</div>
</div>
Expand Down
Loading

0 comments on commit 62325a0

Please sign in to comment.