Cleanup
This commit is contained in:
parent
085fb25de9
commit
cbc53822f4
20 changed files with 17 additions and 644 deletions
|
|
@ -104,7 +104,7 @@ const App: React.FC = () => {
|
|||
{currentUser ? (
|
||||
<Layout
|
||||
currentUser={currentUser}
|
||||
setCurrentUser={setCurrentUser} // Make sure to pass this down
|
||||
setCurrentUser={setCurrentUser}
|
||||
isDarkMode={isDarkMode}
|
||||
toggleDarkMode={toggleDarkMode}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ const AreaModal: React.FC<AreaModalProps> = ({ isOpen, onClose, area, onSave })
|
|||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Handle Escape key to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||
});
|
||||
|
||||
if (response.ok) {
|
||||
setCurrentUser(null); // Update the application state
|
||||
navigate("/login"); // Redirect to the login page
|
||||
setCurrentUser(null);
|
||||
navigate("/login");
|
||||
} else {
|
||||
console.error("Failed to log out");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// app/frontend/components/Note/NoteModal.tsx
|
||||
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { Note } from '../../entities/Note';
|
||||
import { useDataContext } from '../../contexts/DataContext';
|
||||
|
|
@ -31,7 +29,6 @@ const NoteModal: React.FC<NoteModalProps> = ({ isOpen, onClose, note, onSave })
|
|||
|
||||
const { showSuccessToast, showErrorToast } = useToast();
|
||||
|
||||
// Fetch available tags when modal is opened
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
fetch('/api/tags')
|
||||
|
|
@ -44,7 +41,6 @@ const NoteModal: React.FC<NoteModalProps> = ({ isOpen, onClose, note, onSave })
|
|||
}
|
||||
}, [isOpen, showErrorToast]);
|
||||
|
||||
// Reset form data when modal opens or note changes
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setFormData({
|
||||
|
|
@ -58,7 +54,6 @@ const NoteModal: React.FC<NoteModalProps> = ({ isOpen, onClose, note, onSave })
|
|||
}
|
||||
}, [isOpen, note]);
|
||||
|
||||
// Handle click outside to close modal
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
|
|
@ -77,7 +72,6 @@ const NoteModal: React.FC<NoteModalProps> = ({ isOpen, onClose, note, onSave })
|
|||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Handle Escape key to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
|
|
@ -105,7 +99,6 @@ const NoteModal: React.FC<NoteModalProps> = ({ isOpen, onClose, note, onSave })
|
|||
const handleTagsChange = useCallback((newTags: string[]) => {
|
||||
setTags(newTags);
|
||||
|
||||
// Map newTags to Tag objects with 'id' if they exist in availableTags
|
||||
const updatedTags: Tag[] = newTags.map((name) => {
|
||||
const existingTag = availableTags.find((tag) => tag.name === name);
|
||||
return existingTag ? { id: existingTag.id, name } : { name };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// app/frontend/components/Project/ProjectDetails.tsx
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams, useLocation, useNavigate, Link } from "react-router-dom";
|
||||
import {
|
||||
|
|
@ -34,7 +32,6 @@ const ProjectDetails: React.FC = () => {
|
|||
const projectTitle = stateTitle || project?.name || "Project";
|
||||
const projectIcon = stateIcon;
|
||||
|
||||
// State for Collapsible Completed Tasks
|
||||
const [isCompletedOpen, setIsCompletedOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -169,7 +166,6 @@ const ProjectDetails: React.FC = () => {
|
|||
);
|
||||
}
|
||||
|
||||
// Separate tasks into active and completed
|
||||
const activeTasks = tasks.filter(task => task.status !== 'done');
|
||||
const completedTasks = tasks.filter(task => task.status === 'done');
|
||||
|
||||
|
|
@ -231,7 +227,7 @@ const ProjectDetails: React.FC = () => {
|
|||
handleTaskCreate({
|
||||
name: taskName,
|
||||
status: "not_started",
|
||||
project_id: project?.id, // Ensure project_id is correctly assigned
|
||||
project_id: project?.id,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ const ProjectModal: React.FC<ProjectModalProps> = ({
|
|||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Handle Escape key to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { ChevronDownIcon, ArrowDownIcon, ArrowUpIcon, FireIcon } from '@heroicons/react/24/outline'; // Import the icons
|
||||
import { ChevronDownIcon, ArrowDownIcon, ArrowUpIcon, FireIcon } from '@heroicons/react/24/outline';
|
||||
import { PriorityType } from '../../entities/Task';
|
||||
|
||||
interface PriorityDropdownProps {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// app/frontend/components/Tag/TagInput.tsx
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import TaskTags from '../Task/TaskTags';
|
||||
import { Tag } from '../../entities/Tag';
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ const TagModal: React.FC<TagModalProps> = ({
|
|||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Handle Escape key to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
|
|
@ -87,14 +86,9 @@ const TagModal: React.FC<TagModalProps> = ({
|
|||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
// Assuming you have createTag and updateTag functions
|
||||
if (tag) {
|
||||
// Update existing tag
|
||||
// await updateTag(formData.id, formData);
|
||||
showSuccessToast('Tag updated successfully!');
|
||||
} else {
|
||||
// Create new tag
|
||||
// await createTag(formData);
|
||||
showSuccessToast('Tag created successfully!');
|
||||
}
|
||||
onSave(formData);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// app/frontend/components/Task/TaskHeader.tsx
|
||||
|
||||
import React from "react";
|
||||
import TaskPriorityIcon from "./TaskPriorityIcon";
|
||||
import TaskTags from "./TaskTags";
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false); // State to control confirm dialog
|
||||
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
||||
|
||||
const { showSuccessToast, showErrorToast } = useToast(); // Use toast functions
|
||||
const { showSuccessToast, showErrorToast } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
setFormData(task);
|
||||
|
|
@ -118,7 +118,7 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
};
|
||||
|
||||
const handleDeleteClick = () => {
|
||||
setShowConfirmDialog(true); // Show confirmation dialog
|
||||
setShowConfirmDialog(true);
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = () => {
|
||||
|
|
@ -138,7 +138,6 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
}, 300);
|
||||
};
|
||||
|
||||
// Handler to remove tag
|
||||
const handleTagRemove = (tagId: string | number | undefined) => {
|
||||
if (tagId === undefined) return;
|
||||
const tagIndex = Number(tagId);
|
||||
|
|
@ -174,7 +173,6 @@ const TaskModal: React.FC<TaskModalProps> = ({
|
|||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Handle Escape key to close modal
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// app/frontend/components/Tasks.tsx
|
||||
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import TaskList from "./Task/TaskList";
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
// app/frontend/contexts/DataContext.tsx
|
||||
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import useFetchTags from '../hooks/useFetchTags';
|
||||
import useFetchAreas from '../hooks/useFetchAreas';
|
||||
import useFetchProjects from '../hooks/useFetchProjects'; // Use the updated hook
|
||||
import useFetchProjects from '../hooks/useFetchProjects';
|
||||
import useManageAreas from '../hooks/useManageAreas';
|
||||
import useManageNotes from '../hooks/useManageNotes';
|
||||
import useManageProjects from '../hooks/useManageProjects';
|
||||
|
|
@ -37,7 +35,7 @@ interface DataContextProps {
|
|||
mutateTags: () => void;
|
||||
mutateAreas: () => void;
|
||||
mutateNotes: () => void;
|
||||
mutateProjects: () => void; // Include mutateProjects
|
||||
mutateProjects: () => void;
|
||||
}
|
||||
|
||||
const DataContext = createContext<DataContextProps | undefined>(undefined);
|
||||
|
|
@ -58,7 +56,7 @@ export const DataProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||
isLoading: isLoadingProjects,
|
||||
isError: isErrorProjects,
|
||||
mutate: mutateProjects,
|
||||
} = useFetchProjects(); // Use the updated hook without options
|
||||
} = useFetchProjects();
|
||||
const { createArea, updateArea, deleteArea } = useManageAreas();
|
||||
const { createProject, updateProject, deleteProject } = useManageProjects();
|
||||
const { createTag, updateTag, deleteTag } = useManageTags();
|
||||
|
|
@ -111,7 +109,7 @@ export const DataProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
|||
mutateTags,
|
||||
mutateAreas,
|
||||
mutateNotes,
|
||||
mutateProjects, // Include mutateProjects
|
||||
mutateProjects,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
// app/frontend/hooks/useFetchProjects.ts
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Project } from '../entities/Project';
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ const useManageTasks = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const mutateTasks = fetchTasks; // Expose fetchTasks as mutateTasks
|
||||
const mutateTasks = fetchTasks;
|
||||
|
||||
return { tasks, isLoading, isError, fetchTasks, mutateTasks, createTask, updateTask, deleteTask };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { createRoot } from "react-dom/client"; // Import createRoot from react-dom
|
||||
import { BrowserRouter } from "react-router-dom"; // Import BrowserRouter
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App";
|
||||
import { ToastProvider } from "./components/Shared/ToastContext";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,299 +0,0 @@
|
|||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
font-size: 0.86rem;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.875rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.625rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.375rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.925rem;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.825rem;
|
||||
}
|
||||
|
||||
.dropdown-menu,
|
||||
.btn,
|
||||
.form-control,
|
||||
.form-select {
|
||||
font-size: 0.85rem;
|
||||
padding: .375rem .75rem;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
font-size: 0.95rem;
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
font-size: 0.75rem;
|
||||
padding: .25rem .5rem;
|
||||
}
|
||||
|
||||
.task-item:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
/* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: #eee;
|
||||
opacity: 1;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
/* Internet Explorer 10-11 */
|
||||
color: gray;
|
||||
}
|
||||
|
||||
::-ms-input-placeholder {
|
||||
/* Microsoft Edge */
|
||||
color: gray;
|
||||
}
|
||||
|
||||
|
||||
/* sidebar */
|
||||
.area-item .area-options {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
}
|
||||
|
||||
.area-item:hover .area-options {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.fixed-sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* main */
|
||||
|
||||
.main-content {
|
||||
margin-top: 50px;
|
||||
margin-left: 250px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.login-content {
|
||||
margin-left: 200px;
|
||||
}
|
||||
|
||||
/* sidebar */
|
||||
.sidebar {
|
||||
background: #ececec;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--bs-dark);
|
||||
}
|
||||
|
||||
.nav-link.active-link {
|
||||
background-color: #343a40;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-link:hover, .nav-link a:hover {
|
||||
color: #666 !important;
|
||||
}
|
||||
|
||||
.nav-link.active-link:hover, .nav-link.active-link a:hover {
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.dark-mode .nav-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-mode .nav-link.active-link {
|
||||
background-color: var(--bs-dark);
|
||||
}
|
||||
|
||||
/* tasks, notes */
|
||||
|
||||
.task-item,
|
||||
.note-item{
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.dark-mode .task-item,
|
||||
.dark-mode .note-item{
|
||||
border-bottom: 1px solid #222;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.dark-mode .task-list,
|
||||
.dark-mode .note-list,
|
||||
.dark-mode .project-list {
|
||||
background: var(--bs-black) !important;
|
||||
}
|
||||
|
||||
.task-item:hover,
|
||||
.note-item:hover {
|
||||
background: #eee !important;
|
||||
}
|
||||
|
||||
.dark-mode .task-item:hover,
|
||||
.dark-mode .note-item:hover {
|
||||
background: #292929 !important;
|
||||
}
|
||||
|
||||
/* task form */
|
||||
|
||||
.task-name-input::placeholder {
|
||||
color: #ccc;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.new-task-input {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
/* note form */
|
||||
|
||||
.no-focus-outline textarea:focus,
|
||||
.no-focus-outline textarea:-moz-focusring {
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
/* project card */
|
||||
a.project-card .card:hover {
|
||||
border: 1px solid #777 !important;
|
||||
}
|
||||
|
||||
/* dark mode */
|
||||
.dark-mode {
|
||||
background-color: #191919;
|
||||
color: var(--bs-light);
|
||||
}
|
||||
|
||||
.dark-mode .sidebar {
|
||||
background-color: var(--bs-black);
|
||||
}
|
||||
|
||||
.sidebar .border-top {
|
||||
border-color: #ddd !important;
|
||||
}
|
||||
|
||||
.dark-mode .sidebar .border-top {
|
||||
border-color: #333 !important;
|
||||
}
|
||||
|
||||
.dark-mode h1,
|
||||
.dark-mode h2,
|
||||
.dark-mode h3,
|
||||
.dark-mode h4,
|
||||
.dark-mode h5,
|
||||
.dark-mode h6 {
|
||||
color: var(--bs-light) !important;
|
||||
}
|
||||
|
||||
.dark-mode .modal .modal-dialog .modal-content {
|
||||
background-color: var(--bs-gray-dark);
|
||||
color: var(--bs-light);
|
||||
}
|
||||
|
||||
.dark-mode .modal-header {
|
||||
border-color: var(--bs-black);
|
||||
}
|
||||
|
||||
.dark-mode input,
|
||||
.dark-mode tags,
|
||||
.dark-mode select,
|
||||
.dark-mode textarea {
|
||||
background-color: var(--bs-dark);
|
||||
color: var(--bs-light);
|
||||
border-color: var(--bs-black);
|
||||
}
|
||||
|
||||
.dark-mode input:focus,
|
||||
.dark-mode textarea:focus {
|
||||
background-color: var(--bs-black);
|
||||
color: var(--bs-light);
|
||||
}
|
||||
|
||||
.dark-mode tag {
|
||||
background-color: var(--bs-gray-dark) !important;
|
||||
color: var(--bs-light) !important;
|
||||
me
|
||||
}
|
||||
|
||||
.dark-mode .project-card .card {
|
||||
background-color: var(--bs-gray-dark) !important;
|
||||
color: var(--bs-light) !important;
|
||||
}
|
||||
|
||||
.dark-mode .progress {
|
||||
background-color: var(--bs-dark);
|
||||
}
|
||||
|
||||
.panel-card {
|
||||
background-color: var(--bs-white) !important;
|
||||
color: var(--bs-dark) !important;
|
||||
}
|
||||
|
||||
.dark-mode .panel-card {
|
||||
background-color: var(--bs-gray-dark) !important;
|
||||
color: var(--bs-light) !important;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.dark-mode .panel {
|
||||
background-color: var(--bs-black);
|
||||
}
|
||||
|
||||
#newNoteForm .card {
|
||||
color: var(--bs-dark) !important;
|
||||
}
|
||||
|
||||
.dark-mode #newNoteForm .card {
|
||||
background-color: var(--bs-gray-dark) !important;
|
||||
color: var(--bs-light) !important;
|
||||
}
|
||||
|
||||
.blank-slate {
|
||||
background-color: #eee !important;
|
||||
color: var(--bs-dark) !important;
|
||||
}
|
||||
|
||||
.dark-mode .blank-slate {
|
||||
background-color: var(--bs-gray-dark) !important;
|
||||
color: var(--bs-light) !important;
|
||||
}
|
||||
|
||||
.note-form-toggler {
|
||||
border: 1px solid #ddd !important;
|
||||
background-color: #f4f4f4 !important;
|
||||
color: #ccc !important;
|
||||
}
|
||||
|
||||
.dark-mode .note-form-toggler {
|
||||
border: 1px solid #000 !important;
|
||||
background-color: var(--bs-dark) !important;
|
||||
color: var(--bs-light) !important;
|
||||
}
|
||||
297
public/js/app.js
297
public/js/app.js
|
|
@ -1,297 +0,0 @@
|
|||
document.addEventListener("DOMContentLoaded", function () {
|
||||
attachEventListeners();
|
||||
initializeTagifyOnNotes();
|
||||
initializeDarkModeToggle();
|
||||
setInitialDarkModeState();
|
||||
});
|
||||
|
||||
function attachEventListeners() {
|
||||
attachCollapseListeners();
|
||||
manageAreaState();
|
||||
attachTaskClickListeners();
|
||||
attachProjectModalListeners();
|
||||
attachAreaModalListeners();
|
||||
attachNoteClickListeners();
|
||||
}
|
||||
|
||||
function initializeDarkModeToggle() {
|
||||
const darkModeToggle = document.querySelector('#darkModeToggle');
|
||||
const darkModeIcon = document.querySelector('#darkModeIcon');
|
||||
const isDarkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
updateDarkMode(isDarkMode);
|
||||
|
||||
darkModeToggle.addEventListener('click', function () {
|
||||
const isDarkMode = localStorage.getItem('darkMode') === 'true';
|
||||
updateDarkMode(!isDarkMode);
|
||||
});
|
||||
}
|
||||
|
||||
function updateDarkMode(enabled) {
|
||||
document.body.classList.toggle('dark-mode', enabled);
|
||||
localStorage.setItem('darkMode', enabled);
|
||||
darkModeIcon.classList.toggle('bi-moon', !enabled);
|
||||
darkModeIcon.classList.toggle('bi-sun', enabled);
|
||||
// Update link colors and other necessary elements
|
||||
updateLinkColors(enabled);
|
||||
updateDropdownMenus(enabled);
|
||||
}
|
||||
|
||||
function updateLinkColors(darkModeEnabled) {
|
||||
const links = document.querySelectorAll('.link-dark, .link-light');
|
||||
links.forEach(link => {
|
||||
link.classList.toggle('link-dark', !darkModeEnabled);
|
||||
link.classList.toggle('link-light', darkModeEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
function setInitialDarkModeState() {
|
||||
const isDarkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const darkModeToggle = document.querySelector('#darkModeToggle');
|
||||
if (darkModeToggle) {
|
||||
darkModeToggle.checked = isDarkMode;
|
||||
document.body.classList.toggle('dark-mode', isDarkMode);
|
||||
updateLinkColors(isDarkMode);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDropdownMenus(enableDarkMode) {
|
||||
document.querySelectorAll('.dropdown-menu').forEach(menu => {
|
||||
menu.classList.toggle('dropdown-menu-dark', enableDarkMode);
|
||||
});
|
||||
}
|
||||
|
||||
function initializeTagifyOnNotes() {
|
||||
document.querySelectorAll('[id^="note_tags_new_"]').forEach(function(element) {
|
||||
new Tagify(element);
|
||||
});
|
||||
}
|
||||
|
||||
function attachCollapseListeners() {
|
||||
document.querySelectorAll('.collapse').forEach(collapseElement => {
|
||||
collapseElement.addEventListener('show.bs.collapse', () => toggleFolderIcon(collapseElement, true));
|
||||
collapseElement.addEventListener('hide.bs.collapse', () => toggleFolderIcon(collapseElement, false));
|
||||
});
|
||||
}
|
||||
|
||||
function manageAreaState() {
|
||||
document.querySelectorAll('.area-item a.nav-link').forEach(link => {
|
||||
const areaId = link.getAttribute('href').replace('#', '');
|
||||
const areaElement = document.getElementById(areaId);
|
||||
if (areaElement) {
|
||||
const isExpanded = localStorage.getItem('#' + areaId) === 'true';
|
||||
if (isExpanded) {
|
||||
link.setAttribute('aria-expanded', 'true');
|
||||
areaElement.classList.add('show');
|
||||
} else {
|
||||
link.setAttribute('aria-expanded', 'false');
|
||||
areaElement.classList.remove('show');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Save the state of areas when clicked
|
||||
document.querySelectorAll('.area-item a.nav-link').forEach(link => {
|
||||
link.addEventListener('click', function (event) {
|
||||
event.preventDefault(); // Prevent default action to manually control collapse
|
||||
const areaId = this.getAttribute('href').replace('#', '');
|
||||
const isExpanded = this.getAttribute('aria-expanded') === 'false'; // Toggle the state based on current state
|
||||
localStorage.setItem('#' + areaId, isExpanded);
|
||||
if (isExpanded) {
|
||||
this.setAttribute('aria-expanded', 'true');
|
||||
document.getElementById(areaId).classList.add('show');
|
||||
} else {
|
||||
this.setAttribute('aria-expanded', 'false');
|
||||
document.getElementById(areaId).classList.remove('show');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure this function is called when the DOM is fully loaded
|
||||
document.addEventListener("DOMContentLoaded", manageAreaState);
|
||||
|
||||
function toggleFolderIcon(collapseElement, isOpening) {
|
||||
const closedFolderIcon = collapseElement.previousElementSibling?.querySelector('.bi-folder');
|
||||
const openFolderIcon = collapseElement.previousElementSibling?.querySelector('.bi-folder2-open');
|
||||
|
||||
if (closedFolderIcon && openFolderIcon) {
|
||||
closedFolderIcon.classList.toggle('d-none', isOpening);
|
||||
openFolderIcon.classList.toggle('d-none', !isOpening);
|
||||
}
|
||||
}
|
||||
|
||||
function attachTaskClickListeners() {
|
||||
document.querySelectorAll('.task-item').forEach(taskElement => {
|
||||
taskElement.addEventListener('click', event => {
|
||||
if (!event.target.closest('.toggle-completion')) {
|
||||
openEditTaskModal(taskElement.dataset.taskId);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openEditTaskModal(taskId) {
|
||||
const formContainer = document.getElementById('edit_task_form_' + taskId);
|
||||
if (!formContainer) {
|
||||
console.error('Edit form not found for task: ' + taskId);
|
||||
return;
|
||||
}
|
||||
const formHtml = formContainer.innerHTML;
|
||||
const editTaskFormContainer = document.getElementById('editTaskFormContainer');
|
||||
editTaskFormContainer.innerHTML = formHtml;
|
||||
|
||||
new Tagify(editTaskFormContainer.querySelector('#task_tags_' + taskId));
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editTaskModal')).show();
|
||||
}
|
||||
|
||||
function attachProjectModalListeners() {
|
||||
document.querySelectorAll('[data-bs-toggle="modal"][data-project-id]').forEach(button => {
|
||||
button.addEventListener('click', () => openProjectModalForEdit(button.getAttribute('data-project-id')));
|
||||
});
|
||||
}
|
||||
|
||||
function attachNoteClickListeners() {
|
||||
document.querySelectorAll('.note-item').forEach(noteElement => {
|
||||
noteElement.addEventListener('click', event => {
|
||||
const noteId = noteElement.getAttribute('data-note-id');
|
||||
if (noteId) {
|
||||
openEditNoteModal(noteId);
|
||||
} else {
|
||||
console.error('Note ID not found for element:', noteElement);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openEditNoteModal(noteId) {
|
||||
const formContainer = document.getElementById('edit_note_form_' + noteId);
|
||||
if (!formContainer) {
|
||||
console.error('Edit form not found for note: ' + noteId);
|
||||
return;
|
||||
}
|
||||
const formHtml = formContainer.innerHTML;
|
||||
const editNoteFormContainer = document.getElementById('editNoteFormContainer');
|
||||
editNoteFormContainer.innerHTML = formHtml;
|
||||
|
||||
new Tagify(editNoteFormContainer.querySelector('#note_tags_' + noteId));
|
||||
|
||||
new bootstrap.Modal(document.getElementById('editNoteModal')).show();
|
||||
}
|
||||
|
||||
function openProjectModalForEdit(projectId) {
|
||||
fetch('/project/' + projectId)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(projectData => {
|
||||
document.getElementById('projectName').value = projectData.name;
|
||||
document.getElementById('projectDescription').value = projectData.description || '';
|
||||
var projectForm = document.getElementById('projectForm');
|
||||
if (projectForm) {
|
||||
projectForm.action = '/projects/' + projectId;
|
||||
projectForm.method = 'patch';
|
||||
}
|
||||
|
||||
var modal = new bootstrap.Modal(document.getElementById('editProjectModal'));
|
||||
modal.show();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching project data:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function attachAreaModalListeners() {
|
||||
document.querySelectorAll('.open-new-area-modal').forEach(button => {
|
||||
button.addEventListener('click', () => new bootstrap.Modal(document.getElementById('newAreaModal')).show());
|
||||
});
|
||||
|
||||
document.querySelectorAll('.open-edit-area-modal').forEach(button => {
|
||||
button.addEventListener('click', () => openEditAreaModal(button.dataset.areaId));
|
||||
});
|
||||
}
|
||||
|
||||
function deleteProject(projectId) {
|
||||
if (confirm('Are you sure you want to delete this project?')) {
|
||||
const form = document.getElementById('delete_project_' + projectId);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function deleteArea(areaId) {
|
||||
if (confirm('Are you sure you want to delete this area?')) {
|
||||
const form = document.getElementById('delete_area_' + areaId);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function openEditAreaModal(areaId) {
|
||||
fetchAreaDataAndPopulateModal(areaId);
|
||||
const modal = new bootstrap.Modal(document.getElementById('editAreaModal'));
|
||||
modal.show();
|
||||
}
|
||||
|
||||
function fetchAreaDataAndPopulateModal(areaId) {
|
||||
fetch('/areas/' + areaId + '/data')
|
||||
.then(response => response.json())
|
||||
.then(areaData => {
|
||||
populateAreaEditForm(areaData);
|
||||
})
|
||||
.catch(error => console.error('Error fetching area data:', error));
|
||||
}
|
||||
|
||||
function populateAreaEditForm(areaData) {
|
||||
document.getElementById('editAreaName').value = areaData.name;
|
||||
}
|
||||
|
||||
function toggleTaskCompletion(event, taskId) {
|
||||
event.stopPropagation();
|
||||
fetch('/task/' + taskId + '/toggle_completion', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ _method: 'patch' })
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
return response.json();
|
||||
})
|
||||
.then(data => updateTaskCompletionStatus(taskId, data))
|
||||
.catch(error => console.error('There has been a problem with your fetch operation:', error));
|
||||
}
|
||||
|
||||
function updateTaskCompletionStatus(taskId, data) {
|
||||
const iconSpan = document.querySelector('.task-item[data-task-id="' + taskId + '"] .toggle-completion');
|
||||
const taskIcon = iconSpan.querySelector('.bi');
|
||||
const taskDiv = iconSpan.closest('.task-item');
|
||||
|
||||
if (data.done) {
|
||||
taskIcon.classList.remove('bi-circle', 'text-warning', 'text-danger');
|
||||
taskIcon.classList.add('bi-check-circle-fill', 'text-success');
|
||||
taskDiv.classList.add('opacity-50');
|
||||
|
||||
} else {
|
||||
taskIcon.classList.remove('bi-check-circle-fill', 'text-success');
|
||||
taskIcon.classList.add('bi-circle');
|
||||
taskDiv.classList.remove('opacity-50');
|
||||
applyPriorityColor(taskIcon, data.priority);
|
||||
}
|
||||
setTimeout(() => taskDiv.remove(), 200);
|
||||
}
|
||||
|
||||
function applyPriorityColor(taskIcon, priority) {
|
||||
taskIcon.classList.remove('text-warning', 'text-danger', 'text-secondary');
|
||||
switch (priority) {
|
||||
case 'Medium':
|
||||
taskIcon.classList.add('text-warning');
|
||||
break;
|
||||
case 'High':
|
||||
taskIcon.classList.add('text-danger');
|
||||
break;
|
||||
default:
|
||||
taskIcon.classList.add('text-secondary');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node", // Ensures TypeScript resolves modules like 'react-router-dom'
|
||||
"moduleResolution": "node",
|
||||
"jsx": "react",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module.exports = {
|
|||
output: {
|
||||
path: path.resolve(__dirname, 'public/js'),
|
||||
filename: 'bundle.js',
|
||||
publicPath: '/js/', // Ensure HMR works properly
|
||||
publicPath: '/js/',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue