Skip to content

Commit

Permalink
Fix issue with tags persistence
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisvel committed Nov 6, 2024
1 parent 03016e9 commit 3b13ce6
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 41 deletions.
3 changes: 0 additions & 3 deletions app/frontend/components/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ const Login: React.FC = () => {
tududi
</h1>
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-sm">
<h2 className="text-2xl font-bold mb-6 text-center text-gray-700">
Login
</h2>
{error && (
<div className="mb-4 text-center text-red-500">
{error}
Expand Down
6 changes: 3 additions & 3 deletions app/frontend/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface NavbarProps {
email: string;
avatarUrl?: string;
};
setCurrentUser: React.Dispatch<React.SetStateAction<any>>; // Add this line
setCurrentUser: React.Dispatch<React.SetStateAction<any>>;
isSidebarOpen: boolean;
setIsSidebarOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
Expand All @@ -18,13 +18,13 @@ const Navbar: React.FC<NavbarProps> = ({
isDarkMode,
toggleDarkMode,
currentUser,
setCurrentUser, // Add this line
setCurrentUser,
isSidebarOpen,
setIsSidebarOpen,
}) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const navigate = useNavigate(); // Add this line
const navigate = useNavigate();

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
Expand Down
65 changes: 40 additions & 25 deletions app/frontend/components/Projects.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useState, useEffect } from "react";
import { Project } from "../entities/Project";
import { Link, useNavigate, useSearchParams } from "react-router-dom";
import { EllipsisVerticalIcon } from "@heroicons/react/24/solid";
import { Link, useSearchParams } from "react-router-dom";
import { EllipsisVerticalIcon, MagnifyingGlassIcon } from "@heroicons/react/24/solid";
import ConfirmDialog from "./Shared/ConfirmDialog";
import ProjectModal from "./Project/ProjectModal";
import { useDataContext } from "../contexts/DataContext";
import useFetchProjects from "../hooks/useFetchProjects";

// Utility function to generate initials
const getProjectInitials = (name: string) => {
const words = name
.trim()
Expand All @@ -20,21 +19,24 @@ const getProjectInitials = (name: string) => {
};

const Projects: React.FC = () => {
const { areas, createProject, updateProject, deleteProject } =
useDataContext();

const [taskStatusCounts, setTaskStatusCounts] = useState<Record<number, any>>(
{}
);
const { areas, createProject, updateProject, deleteProject } = useDataContext();
const [taskStatusCounts, setTaskStatusCounts] = useState<Record<number, any>>({});
const [isProjectModalOpen, setIsProjectModalOpen] = useState<boolean>(false);
const [projectToEdit, setProjectToEdit] = useState<Project | null>(null);
const [projectToDelete, setProjectToDelete] = useState<Project | null>(null);
const [isConfirmDialogOpen, setIsConfirmDialogOpen] =
useState<boolean>(false);
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState<boolean>(false);
const [activeDropdown, setActiveDropdown] = useState<number | null>(null);
const [searchQuery, setSearchQuery] = useState<string>('');

const navigate = useNavigate();
const [searchParams, setSearchParams] = useSearchParams();

// Set default URL parameter ?active=true if not provided
useEffect(() => {
if (!searchParams.has("active")) {
searchParams.set("active", "true");
setSearchParams(searchParams);
}
}, [searchParams, setSearchParams]);

const activeFilter = searchParams.get("active") ?? "active";
const areaFilter = searchParams.get("area_id") ?? "";
Expand All @@ -54,10 +56,7 @@ const Projects: React.FC = () => {
const getCompletionPercentage = (projectId: number | undefined) => {
if (!projectId) return 0;
const taskStatus = taskStatusCounts[projectId] || {};
const totalTasks =
(taskStatus.done || 0) +
(taskStatus.not_started || 0) +
(taskStatus.in_progress || 0);
const totalTasks = (taskStatus.done || 0) + (taskStatus.not_started || 0) + (taskStatus.in_progress || 0);

if (totalTasks === 0) return 0;

Expand All @@ -81,16 +80,13 @@ const Projects: React.FC = () => {

const handleDeleteProject = async () => {
if (!projectToDelete) return;

await deleteProject(projectToDelete.id!);
setIsConfirmDialogOpen(false);
setProjectToDelete(null);
mutate();
};

const handleActiveFilterChange = (
e: React.ChangeEvent<HTMLSelectElement>
) => {
const handleActiveFilterChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const newActiveFilter = e.target.value;
const params = new URLSearchParams(searchParams);

Expand All @@ -114,6 +110,10 @@ const Projects: React.FC = () => {
setSearchParams(params);
};

const filteredProjects = projects.filter((project) =>
project.name.toLowerCase().includes(searchQuery.toLowerCase())
);

if (isLoading) {
return (
<div className="flex items-center justify-center h-screen bg-gray-100 dark:bg-gray-900">
Expand All @@ -132,7 +132,7 @@ const Projects: React.FC = () => {
);
}

const groupedProjects = projects.reduce<Record<string, Project[]>>(
const groupedProjects = filteredProjects.reduce<Record<string, Project[]>>(
(acc, project) => {
const areaName = project.area ? project.area.name : "Uncategorized";
if (!acc[areaName]) acc[areaName] = [];
Expand All @@ -143,7 +143,7 @@ const Projects: React.FC = () => {
);

return (
<div className="flex justify-center px-4 lg:px-2 ">
<div className="flex justify-center px-4 lg:px-2">
<div className="w-full max-w-6xl">
<div className="flex items-center mb-8">
<i className="bi bi-folder-fill text-xl mr-2"></i>
Expand All @@ -163,7 +163,7 @@ const Projects: React.FC = () => {
</label>
<select
id="activeFilter"
value={activeFilter || "all"} // Use "all" when no active filter is selected
value={activeFilter || "all"}
onChange={handleActiveFilterChange}
className="block w-full p-2 border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
>
Expand Down Expand Up @@ -196,6 +196,20 @@ const Projects: React.FC = () => {
</div>
</div>

{/* Search Bar with Icon */}
<div className="mb-4">
<div className="flex items-center bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm p-2">
<MagnifyingGlassIcon className="h-5 w-5 text-gray-500 dark:text-gray-400 mr-2" />
<input
type="text"
placeholder="Search projects..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full bg-transparent border-none focus:ring-0 focus:outline-none dark:text-white"
/>
</div>
</div>

{/* Project Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{Object.keys(groupedProjects).length === 0 ? (
Expand All @@ -205,15 +219,15 @@ const Projects: React.FC = () => {
) : (
Object.keys(groupedProjects).map((areaName) => (
<React.Fragment key={areaName}>
<h3 className="col-span-full text-md uppercase font-light text-gray-800 dark:text-gray-200 mb-4">
<h3 className="col-span-full text-md uppercase font-light text-gray-800 dark:text-gray-200 mb-2 mt-6">
{areaName}
</h3>

{groupedProjects[areaName].map((project) => (
<div
key={project.id}
className="bg-gray-50 dark:bg-gray-900 rounded-lg shadow-md relative"
style={{ minHeight: "280px", maxHeight: "280px" }} // Increased card height for image space
style={{ minHeight: "280px", maxHeight: "280px" }} // Consistent card height
>
<div
className="bg-gray-200 dark:bg-gray-700 flex items-center justify-center overflow-hidden rounded-t-lg"
Expand Down Expand Up @@ -319,3 +333,4 @@ const Projects: React.FC = () => {
};

export default Projects;

4 changes: 2 additions & 2 deletions app/frontend/components/Task/TaskTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const TaskTags: React.FC<TaskTagsProps> = ({ tags = [], onTagRemove, className }
<button
type="button"
onClick={() => handleTagClick(tag.name)}
className="flex items-center space-x-1"
className="flex items-center"
>
<TagIcon className="hidden md:block h-4 w-4 text-gray-500 dark:text-gray-300" />
<TagIcon className="hidden md:block h-4 w-4 text-gray-500 dark:text-gray-300 mr-2" />
<span className="text-xs text-gray-700 dark:text-gray-300">{tag.name}</span>
</button>
{onTagRemove && (
Expand Down
4 changes: 2 additions & 2 deletions app/routes/tasks_routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ def update_task_tags(task, tags_data)

tag_names = tags_data.map { |tag| tag['name'] }.compact.reject(&:empty?).uniq

existing_tags = Tag.where(name: tag_names)
existing_tags = Tag.where(user: current_user, name: tag_names)
new_tags = tag_names - existing_tags.pluck(:name)
created_tags = new_tags.map { |name| Tag.create(name: name) }
created_tags = new_tags.map { |name| Tag.create(name: name, user: current_user) }

task.tags = (existing_tags + created_tags).uniq
end
Expand Down
Loading

0 comments on commit 3b13ce6

Please sign in to comment.