diff --git a/.github/workflows/compile.yaml b/.github/workflows/compile.yaml index 7c32042..e4c6b0f 100644 --- a/.github/workflows/compile.yaml +++ b/.github/workflows/compile.yaml @@ -11,6 +11,7 @@ on: env: AUTH_SECRET: ${{ secrets.AUTH_SECRET }} POSTGRES_URL: ${{ secrets.POSTGRES_URL }} + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} jobs: build: diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8753a73..da643e6 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -8,6 +8,7 @@ on: env: AUTH_SECRET: ${{ secrets.AUTH_SECRET }} POSTGRES_URL: ${{ secrets.POSTGRES_URL }} + OPENAI_KEY: ${{ secrets.OPENAI_KEY }} jobs: build: @@ -45,6 +46,7 @@ jobs: run: | sst secret set AuthSecret ${{ secrets.AUTH_SECRET }} --stage=prod sst secret set PostgresURL ${{ secrets.POSTGRES_URL }} --stage=prod + sst secret set OpenAIKey ${{ secrets.OPENAI_KEY }} --stage=prod - name: Deploy to AWS with SST run: | diff --git a/actions.ts b/actions.ts index 7826819..e80ad59 100644 --- a/actions.ts +++ b/actions.ts @@ -5,10 +5,12 @@ import { signIn } from "@/auth"; import { registerUser } from "./services/user"; import { DatabaseErrorType } from "./types/errorTypes"; import { DatabaseError } from "pg"; +import { redirect } from "next/navigation"; export const loginAction = async (formData: FormData) => { try { await signIn("credentials", formData); + redirect("/profile"); } catch (error) { if (error instanceof AuthError) { switch (error.type) { @@ -34,7 +36,7 @@ export const registerAction = async (formData: FormData) => { const dbError = e as DatabaseError; let errorType: DatabaseErrorType; - if (dbError.message === "Email already registered") + if (dbError.message === "Email already registered 23505") errorType = "UniqueConstraintViolation"; else if (dbError.message === "Error registering the user") errorType = "ConnectionError"; diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 7b78044..b516b23 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -26,18 +26,6 @@ const Login = () => {

Log in to Feedback Flow

- -
{ const activeUserId = await getUserId(); const isManagedBy = await getUserManagedBy(activeUserId, params.userId); const user = await getUserInfoById(params.userId); + const radarData = await getOverallStatistics(params.userId); - const radarData = [ - { statistic: "Communication", punctuation: 90 }, - { statistic: "Motivation", punctuation: 68 }, - { statistic: "Coworker Support", punctuation: 74 }, - { statistic: "Manager Support", punctuation: 85 }, - { statistic: "Punctuality", punctuation: 89 }, - ]; - + const productivityScore = await getProductivityScore(params.userId); + const selfPerceptionScore = await getSelfPerceptionScore(params.userId); + const stressScore = await getStressScore(params.userId); const gaugeData = [ { title: "Productivity Level", - percentage: 78, + percentage: productivityScore, type: "half", gradient: { start: "#988511", end: "#FEDE1C" }, }, { title: "Self Perception Level", - percentage: 64, + percentage: selfPerceptionScore, type: "half", gradient: { start: "#295A95", end: "#4598FB" }, }, { title: "Stress Level", - percentage: 56, + percentage: stressScore, type: "half", gradient: { start: "#881931", end: "#EE2B55" }, }, ]; - const PCPData = { - percentage: 63, - type: "full", - gradient: { start: "#4598FB", end: "#6640D5" }, - }; + const PCPData = await getPCPStatus(params.userId); const emotionsData = await getRulerGraphInfo(params.userId); + getCalendarInfo(params.userId); - const completedSurveys = [ - { date: new Date(2024, 0, 5), color: "red" }, - { date: new Date(2024, 3, 5), color: "red" }, - { date: new Date(2024, 3, 5), color: "green" }, - { date: new Date(2024, 3, 5), color: "yellow" }, - { date: new Date(2024, 3, 16), color: "yellow" }, - { date: new Date(2024, 3, 23), color: "blue" }, - { date: new Date(2024, 4, 15), color: "blue" }, - ]; - + const completedSurveys = await getCalendarInfo(params.userId); return ( - - -
- } - > + <> {isManagedBy && ( )} -
+
@@ -83,10 +68,10 @@ const Dashboard = async ({ params }: { params: { userId: string } }) => {
- +
- + ); }; diff --git a/app/(base)/loading.tsx b/app/(base)/loading.tsx new file mode 100644 index 0000000..3864c82 --- /dev/null +++ b/app/(base)/loading.tsx @@ -0,0 +1,11 @@ +import Loader from "@/components/Loader"; + +const Loading = () => { + return ( +
+ +
+ ); +}; + +export default Loading; diff --git a/app/(base)/pcp/page.tsx b/app/(base)/pcp/page.tsx index 6a42561..bccccd5 100644 --- a/app/(base)/pcp/page.tsx +++ b/app/(base)/pcp/page.tsx @@ -1,102 +1,527 @@ "use client"; -import PipResource from "@/components/PipResource"; -import PipTask from "@/components/PipTask"; import ProgressBar from "@/components/ProgressBar"; +import NoDataCard from "@/components/NoDataCard"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getProjects } from "@/services/project"; import { - getUserTasks, - getUserResources, + getUserResourcesForCurrentSprint, + getUserResourcesHistory, + getUserTasksForCurrentSprintByProjectId, + getUserTasksHistory, updateTask, } from "@/services/tasks-and-resources"; -import { Resource, Task } from "@/types/types"; -import { useState, useEffect } from "react"; -import NoDataCard from "@/components/NoDataCard"; - -const PIP = () => { - const [tasks, setTasks] = useState([]); - const [resources, setResources] = useState([]); +import { useEffect, useState } from "react"; +import DialogComponent from "@/components/DialogComponent"; +import { Menu } from "@headlessui/react"; +import { SelectPipResource, SelectPipTask } from "@/db/schema"; +import InfoIcon from "@/components/icons/InfoIcon"; +import VideoIcon from "@/components/icons/VideoIcon"; +import BookIcon from "@/components/icons/BookIcon"; +import ArticleIcon from "@/components/icons/ArticleIcon"; +import Link from "next/link"; +import ChevronRightIcon from "@/components/icons/ChevronRightIcon"; - const handleCheckTask = (index: number) => { - const newTasks = [...tasks]; - newTasks[index].isDone = !newTasks[index].isDone; - setTasks(newTasks); - updateTask(newTasks[index].id, newTasks[index].isDone); - }; +const statusOptions = [ + { label: "Pending", color: "bg-red-500", value: "PENDING" }, + { label: "In Progress", color: "bg-yellow-500", value: "IN_PROGRESS" }, + { label: "Done", color: "bg-blue-500", value: "DONE" }, +]; - useEffect(() => { - async function fetchTasks() { - try { - const data = await getUserTasks(); - setTasks(data); - } catch (error) { - console.error("Failed to fetch tasks:", error); - } - } +const PCP = () => { + const [projectId, setProjectId] = useState(); - fetchTasks(); - }, []); + const projectsQuery = useQuery({ + queryKey: ["projects"], + queryFn: () => getProjects(), + retry: false, + refetchOnWindowFocus: false, + }); useEffect(() => { - async function fetchResources() { - try { - const data = await getUserResources(); - setResources(data); - } catch (error) { - console.error("Failed to fetch tasks:", error); - } - } + if (!projectsQuery.data) return; + setProjectId(projectsQuery.data[0].id); + }, [projectsQuery.data]); - fetchResources(); - }, []); + const progressPercentage = 100; - const progressPercentage = Math.round( - (tasks.filter((task) => task.isDone).length / tasks.length) * 100, - ); + if ( + projectsQuery.isError || + (projectsQuery.data && projectsQuery.data.length === 0) + ) { + return ( + + ); + } + + if (projectsQuery.isLoading) { + return

loading...

; + } return (
-

Personal Career Plan

+
+

Personal Career Plan

+

+ Sprint 28/05/2024 +

+
-
-

Tasks

-
- {tasks.length === 0 && ( -
- + +
+ +
+ + {projectId && ( + <> + + + + )} +
+ ); +}; + +const PCPTasks = ({ projectId }: { projectId: number }) => { + const [isDialogOpen, setIsDialogOpen] = useState(false); + const openDialog = () => { + setIsDialogOpen(true); + }; + const closeDialog = () => { + setIsDialogOpen(false); + }; + + const tasksQuery = useQuery({ + queryKey: ["tasks", projectId], + queryFn: () => getUserTasksForCurrentSprintByProjectId(projectId), + retry: false, + refetchOnWindowFocus: false, + }); + + return ( +
+
+
+

Tasks

+ +
+ + {tasksQuery.isLoading ? ( +

loading...

+ ) : tasksQuery.isError ? ( + + ) : ( + tasksQuery.data && ( +
+
+ {tasksQuery.data.map((task) => ( + + ))} +
+
+ ) + )} + + + + +
+
+ ); +}; + +const PCPTasksDialogContent = ({ projectId }: { projectId: number }) => { + const queryClient = useQueryClient(); + + const tasksHistoryQuery = useQuery({ + queryKey: ["tasks-history", projectId], + queryFn: () => getUserTasksHistory(projectId), + retry: false, + refetchOnWindowFocus: false, + }); + + const { mutateAsync } = useMutation({ + mutationFn: updateTask, + }); + + if (tasksHistoryQuery.isError) { + return ; + } + + if (tasksHistoryQuery.isLoading || !tasksHistoryQuery.data) { + return

loading...

; + } + + const formatDate = (date: Date) => { + return new Date(date).toLocaleString("default", { + day: "2-digit", + month: "short", + year: "numeric", + }); + }; + + return ( +
+ {tasksHistoryQuery.data.map((sprint) => ( +
+ {sprint.scheduledAt && ( +

{`Sprint ${formatDate(sprint.scheduledAt)}`}

+ )} + {sprint.processed ? ( + sprint.tasks.map((task) => { + const currentStatusOption = + statusOptions.find((option) => option.value === task.status) || + statusOptions[0]; + + return ( +
+

{task.title}

+
+ {/* Description Dropdown */} + +
+ + + +
+ +
+ {task.description} +
+
+
+ + {/* Status Change Button */} + +
+ + Change status + +
+ +
+ {statusOptions.map((option) => ( + + {({ active }) => ( + + )} + + ))} +
+
+
+
+
+ ); + }) + ) : ( +
+
)} - {tasks.map((task, index) => ( - handleCheckTask(index)} - key={index} - /> - ))}
-
-
-

Resources

-
- {resources.length === 0 && ( -
- + ))} +
+ ); +}; + +const PCPTask = ({ + task, + projectId, +}: { + task: SelectPipTask; + projectId: number; +}) => { + const queryClient = useQueryClient(); + + const { mutateAsync } = useMutation({ + mutationFn: updateTask, + }); + + const selectedStatus = + statusOptions.find((option) => option.value === task.status) || + statusOptions[0]; + + return ( +
+
+

{task.title}

+
+ +
+ + Change status + +
+ +
+ {statusOptions.map((option) => ( + + {({ active }) => ( + + )} + + ))} +
+
+
+
+
+

+ {task.description} +

+
+ ); +}; + +const PCPResources = ({ projectId }: { projectId: number }) => { + const [isDialogOpen, setIsDialogOpen] = useState(false); + const openDialog = () => { + setIsDialogOpen(true); + }; + const closeDialog = () => { + setIsDialogOpen(false); + }; + + const resourcesQuery = useQuery({ + queryKey: ["resources", projectId], + queryFn: () => getUserResourcesForCurrentSprint(projectId), + retry: false, + refetchOnWindowFocus: false, + }); + + return ( +
+
+
+

Resources

+ +
+ + {resourcesQuery.isLoading ? ( +

loading...

+ ) : resourcesQuery.isError ? ( + + ) : ( + resourcesQuery.data && ( +
+
+ {resourcesQuery.data.map((resource) => ( + + ))} +
+
+ ) + )} + + + + +
+
+ ); +}; + +const PCPResource = ({ resource }: { resource: SelectPipResource }) => { + const renderIcon = () => { + switch (resource.kind) { + case "VIDEO": + return ; + case "BOOK": + return ; + case "ARTICLE": + return ; + default: + return ; + } + }; + + const renderButtonLabel = () => { + switch (resource.kind) { + case "VIDEO": + return "Watch it"; + case "BOOK": + return "Get it"; + case "ARTICLE": + return "Read it"; + default: + return "Read it"; + } + }; + + return ( +
+
+ {renderIcon()} +

{resource.title}

+
+

+ {resource.description} +

+ +
+ ); +}; + +const PCPResourcesDialogContent = ({ projectId }: { projectId: number }) => { + const resourcesHistoryQuery = useQuery({ + queryKey: ["resources-history", projectId], + queryFn: () => getUserResourcesHistory(projectId), + retry: false, + refetchOnWindowFocus: false, + }); + + if (resourcesHistoryQuery.isError) { + return ; + } + + if (resourcesHistoryQuery.isLoading || !resourcesHistoryQuery.data) { + return

loading...

; + } + + const formatDate = (date: Date) => { + return new Date(date).toLocaleString("default", { + day: "2-digit", + month: "short", + year: "numeric", + }); + }; + + return ( +
+ {resourcesHistoryQuery.data.map((sprint) => ( +
+ {sprint.scheduledAt && ( +

{`Sprint ${formatDate(sprint.scheduledAt)}`}

+ )} + {sprint.processed ? ( + sprint.resources.map((resource) => ( +
+

{resource.title}

+
+ {/* Description Dropdown */} + +
+ + + +
+ +
+ {resource.description} +
+
+
+ + + +
+
+ )) + ) : ( +
+
)} - {resources.map((task, index) => ( - - ))}
-
+ ))}
); }; -export default PIP; +export default PCP; diff --git a/app/(base)/profile/[id]/page.tsx b/app/(base)/profile/[id]/page.tsx index 3bd4b98..5317020 100644 --- a/app/(base)/profile/[id]/page.tsx +++ b/app/(base)/profile/[id]/page.tsx @@ -1,11 +1,10 @@ -import React, { Suspense } from "react"; +import React from "react"; import { getUserInfoById, getUserTraitsById } from "@/services/user"; import ProfileBanner from "@/components/Profile/ProfileBanner"; import ProfileSection from "@/components/Profile/ProfileSection"; import CoWorkersCarousel from "@/components/CoWorkersCarousel"; import ProjectsCarousel from "@/components/ProjectsCarousel"; import ProfileTraits from "@/components/Profile/ProfileTraits"; -import Loader from "@/components/Loader"; const Profile: React.FC<{ params: { id: string } }> = async ({ params }) => { const user = await getUserInfoById(params.id); @@ -13,48 +12,39 @@ const Profile: React.FC<{ params: { id: string } }> = async ({ params }) => { return (
- - -
- } - > - - -
-
- - - - - - -
-
- - -
-
- + +
+
+ + + + + + +
+
+ + +
+
); }; diff --git a/app/(base)/projects/[projectId]/page.tsx b/app/(base)/projects/[projectId]/page.tsx index f1174f4..a1dd6d1 100644 --- a/app/(base)/projects/[projectId]/page.tsx +++ b/app/(base)/projects/[projectId]/page.tsx @@ -4,13 +4,27 @@ import React, { useState } from "react"; import { RadarChart, AreaChart } from "@mantine/charts"; import GaugeChart from "@/components/GaugeChart"; import { useMutation, useQuery } from "@tanstack/react-query"; -import { deleteProjectById, updateFeedback } from "@/services/project"; +import { + deleteProjectById, + getUpdateFeedbackHistory, + getProjectById, + updateFeedback, +} from "@/services/project"; import { useRouter } from "next/navigation"; import { getUserRole } from "@/services/user"; +import { + getOverallStatistics, + getDetailedProjectStatistics, + getGrowthData, // Importar el nuevo servicio +} from "@/services/sprintSurvey"; + import ChevronDownIcon from "@/components/icons/ChevronDownIcon"; +import NoDataCard from "@/components/NoDataCard"; +import Loader from "@/components/Loader"; const Project = ({ params }: { params: { projectId: string } }) => { const router = useRouter(); + const projectId = parseInt(params.projectId); const [isUpdateFeedbackPopupOpen, setIsUpdateFeedbackPopupOpen] = useState(false); @@ -28,87 +42,107 @@ const Project = ({ params }: { params: { projectId: string } }) => { queryFn: () => getUserRole(), }); - const radarData = [ - { - statistic: "Communication", - punctuation: 90, - }, - { - statistic: "Motivation", - punctuation: 68, - }, - { - statistic: "Coworker Support", - punctuation: 74, - }, - { - statistic: "Manager Support", - punctuation: 85, - }, - { - statistic: "Punctuality", - punctuation: 89, - }, - ]; - const areaData = [ - { - month: "Jan", - growthSupport: 89, - growthOportunities: 70, - }, - { - month: "Feb", - growthSupport: 76, - growthOportunities: 82, - }, - { - month: "Mar", - growthSupport: 95, - growthOportunities: 89, - }, - { - month: "Apr", - growthSupport: 72, - growthOportunities: 85, - }, - { - month: "May", - growthSupport: 83, - growthOportunities: 78, - }, - ]; - const gaugeData = [ - { - title: "Resources Satisfaction", - percentage: 78, - type: "half", - gradient: { start: "#988511", end: "#FEDE1C" }, - }, - { - title: "Listening Feeling", - percentage: 64, - type: "half", - gradient: { start: "#295A95", end: "#4598FB" }, - }, - { - title: "Recognition Feeling", - percentage: 56, - type: "half", - gradient: { start: "#881931", end: "#EE2B55" }, - }, - { - title: "Respect and Trust Environment", - percentage: 85, - type: "half", - gradient: { start: "#35216F", end: "#6640D5" }, - }, - ]; + const { data: projectData, isLoading: isLoadingProjectData } = useQuery({ + queryKey: ["project-data", projectId], + queryFn: () => getProjectById(projectId), + }); + + const { data: statistics, isLoading: isLoadingStatistics } = useQuery({ + queryKey: ["project-overall-statistics", projectId], + queryFn: () => getOverallStatistics(projectId), + }); + + const { data: detailedStatistics, isLoading: isLoadingDetailedStatistics } = + useQuery({ + queryKey: ["project-detailed-statistics", projectId], + queryFn: () => getDetailedProjectStatistics(projectId), + }); + + const { data: growthData, isLoading: isLoadingGrowthData } = useQuery({ + queryKey: ["project-growth-data", projectId], + queryFn: () => getGrowthData(projectId), // Consumir el nuevo servicio + }); + const radarData = statistics + ? [ + { + statistic: "Communication", + punctuation: statistics.communication, + }, + { + statistic: "Motivation", + punctuation: statistics.motivation, + }, + { + statistic: "Coworker Support", + punctuation: statistics.coworkerSupport, + }, + { + statistic: "Manager Support", + punctuation: statistics.managerSupport, + }, + { + statistic: "Punctuality", + punctuation: statistics.punctuality, + }, + ] + : []; + const areaData = growthData + ? growthData.growthSupportData.map((item, index) => ({ + month: item.month, + growthSupport: item.averageAnswer, + growthOportunities: + growthData.growthOpportunitiesData[index]?.averageAnswer || 0, + })) + : []; + + const gaugeData = detailedStatistics + ? [ + { + title: "Resources Satisfaction", + percentage: detailedStatistics.resourcesSatisfaction, + type: "half", + gradient: { start: "#988511", end: "#FEDE1C" }, + }, + { + title: "Listening Feeling", + percentage: detailedStatistics.listeningFeeling, + type: "half", + gradient: { start: "#295A95", end: "#4598FB" }, + }, + { + title: "Recognition Feeling", + percentage: detailedStatistics.recognitionFeeling, + type: "half", + gradient: { start: "#881931", end: "#EE2B55" }, + }, + { + title: "Respect and Trust Environment", + percentage: detailedStatistics.respectTrustEnvironment, + type: "half", + gradient: { start: "#35216F", end: "#6640D5" }, + }, + ] + : []; const progressBarPercentage = 74; + if ( + isLoadingStatistics || + isLoadingDetailedStatistics || + isLoadingProjectData || + isLoadingGrowthData // Agregar el estado de carga de los datos de crecimiento + ) { + return ( +
+ +
+ ); + } + console.log(gaugeData); + return (
-

Project 1

+

{projectData?.name}

{userRoleQuery.data && (userRoleQuery.data === "MANAGER" || userRoleQuery.data === "ADMIN") && ( @@ -117,19 +151,7 @@ const Project = ({ params }: { params: { projectId: string } }) => {
{/* popup */} {isUpdateFeedbackPopupOpen && ( -
-

- Update History -

-
-

Survey 04/04/2024

-

Completed

-
-
-

Survey 04/04/2024

-

Completed

-
-
+ )} {/* open-popup-button */} @@ -172,7 +194,7 @@ const Project = ({ params }: { params: { projectId: string } }) => { className="flex w-fit flex-col rounded-xl bg-white px-10 py-5 drop-shadow-lg" > @@ -258,4 +280,59 @@ const Project = ({ params }: { params: { projectId: string } }) => { ); }; +const UpdateFeedbackHistoryPopup = ({ projectId }: { projectId: number }) => { + const updateFeedbackHistoryQuery = useQuery({ + queryKey: ["update-feedback-history"], + queryFn: () => getUpdateFeedbackHistory({ projectId }), + retry: false, + }); + + const formatDate = (date: Date) => { + return new Date(date).toLocaleString("default", { + day: "numeric", + month: "short", + year: "numeric", + }); + }; + + if (updateFeedbackHistoryQuery.isError) { + return ; + } + + if ( + updateFeedbackHistoryQuery.isLoading || + !updateFeedbackHistoryQuery.data + ) { + return

Loading...

; + } + + return ( +
+

Update History

+ {updateFeedbackHistoryQuery.data.map((survey, idx) => ( +
+

+ {survey.type === "SPRINT" + ? "Sprint Survey" + : survey.type === "FINAL" + ? "Final Survey" + : "UNREACHABLE"}{" "} + -{" "} + + {formatDate(survey.scheduledAt)} + +

+ {survey.status === "COMPLETED" ? ( +

Completed

+ ) : survey.status === "PENDING" ? ( +

Pending

+ ) : survey.status === "NOT_AVAILABLE" ? ( +

Pending

+ ) : null} +
+ ))} +
+ ); +}; + export default Project; diff --git a/components/BannerImagePanel.tsx b/components/BannerImagePanel.tsx index f5996b4..e83a6dc 100644 --- a/components/BannerImagePanel.tsx +++ b/components/BannerImagePanel.tsx @@ -41,6 +41,7 @@ const BannerImagePanel = ({ closeModal }: { closeModal: () => void }) => {
{images.map((src, index) => ( + +
+ ); +}; + +export default InfoToolTip; diff --git a/components/NavigationBar.tsx b/components/NavigationBar.tsx index 5521933..acd991c 100644 --- a/components/NavigationBar.tsx +++ b/components/NavigationBar.tsx @@ -1,98 +1,35 @@ -"use client"; -import { usePathname } from "next/navigation"; -import PIPIcon from "./PIPIconNavbar"; +import PCPIcon from "./PCPIconNavbar"; import Notifications from "./Notifications"; import ProjectNavbarIcon from "./ProjectNavbarIcon"; import UserIconNavbar from "./UserIconNavbar"; import DashboardIconNavbar from "./DashboardIconNavbar"; -import ProjectSurvey from "./modals/ProjectSurvey"; -import { useState } from "react"; -import SprintSurvey from "./modals/SprintSurvey"; -import { useQuery } from "@tanstack/react-query"; import { getUserRole } from "@/services/user"; import Link from "next/link"; -import RulerSurvey from "./modals/ruler/RulerSurvey"; -import Settings from "./modals/SettingsDialog"; import ComboBoxSearch from "./ComboBoxSearch"; -const NavigationBar = () => { - const userRoleQuery = useQuery({ - queryKey: ["user-role"], - queryFn: () => getUserRole(), - }); +const NavigationBar = async () => { + const userRoleQuery = await getUserRole(); - const pathname = usePathname(); - const [showProjectModal, setShowProjectModal] = useState(false); - const [showSprintModal, setShowSprintModal] = useState(false); - const [showRulerModal, setShowRulerModal] = useState(false); - const [showSettingsModal, setShowSettingsModal] = useState(false); - const [notificationId, setNotificationId] = useState(0); - - const isManager = true; return ( - <> - {showProjectModal && ( - setShowProjectModal(false)} - projectSurveyId={notificationId} - /> - )} - - {showSprintModal && ( - setShowSprintModal(false)} - sprintSurveyId={notificationId} - /> - )} - - {showRulerModal && ( - setShowRulerModal(false)} - rulerSurveyId={notificationId} - /> - )} - - {showSettingsModal && ( - setShowSettingsModal(false)} - /> - )} - - - + ); }; export default NavigationBar; diff --git a/components/NotificationCard.tsx b/components/NotificationCard.tsx index cfd4185..758c996 100644 --- a/components/NotificationCard.tsx +++ b/components/NotificationCard.tsx @@ -11,7 +11,7 @@ const NotificationCard = ({ notification }: NotificationCardProps) => { const getTitle = (type: string) => { switch (type) { case "RULER": - return " Daily Ruler Survey"; + return "Daily Ruler Survey"; case "SPRINT": return "Sprint Survey"; case "FINAL": @@ -23,6 +23,7 @@ const NotificationCard = ({ notification }: NotificationCardProps) => { return (
void; - showSprintModal: () => void; - showRulerModal: () => void; - setNotificationId: Dispatch>; -} - -const Notifications = ({ - showProjectModal, - showSprintModal, - showRulerModal, - setNotificationId, -}: NotificationProps) => { +const Notifications = () => { const [isActive, setIsActive] = useState(false); + const [showProjectModal, setShowProjectModal] = useState(false); + const [showSprintModal, setShowSprintModal] = useState(false); + const [showRulerModal, setShowRulerModal] = useState(false); + const [notificationId, setNotificationId] = useState(0); const notificationsQuery = useQuery({ queryKey: ["notifications"], @@ -32,15 +27,15 @@ const Notifications = ({ const onNotificationClick = (notificationType: string) => { switch (notificationType) { case "FINAL": - showProjectModal(); + setShowProjectModal(true); break; case "SPRINT": - showSprintModal(); + setShowSprintModal(true); break; case "RULER": - showRulerModal(); + setShowRulerModal(true); break; default: @@ -68,75 +63,102 @@ const Notifications = ({ } return ( - - setIsActive(!isActive)} - className={`${isActive ? "bg-primary" : "bg-white transition-all delay-0 hover:scale-[1.175]"} group rounded-full p-2 drop-shadow-lg`} - > - + {showProjectModal && ( + setShowProjectModal(false)} + projectSurveyId={notificationId} /> - {notificationsQuery.data.length > 0 && ( - - )} - + )} - - - - {({ close }) => ( -
-
-

Notifications

-

({notificationsQuery.data.length})

+ {showSprintModal && ( + setShowSprintModal(false)} + sprintSurveyId={notificationId} + /> + )} + + {showRulerModal && ( + setShowRulerModal(false)} + /> + )} + + setIsActive(!isActive)} + className={`${isActive ? "bg-primary" : "bg-white transition-all delay-0 hover:scale-[1.175]"} group rounded-full p-2 drop-shadow-lg`} + > + + {notificationsQuery.data.length > 0 && ( + + )} + + + + + + {({ close }) => ( +
+
+

+ Notifications +

+

({notificationsQuery.data.length})

+
+
- + )} +
+ {notificationsQuery.data.length > 0 && + notificationsQuery.data.map((notification, index) => { + return ( + + {({ close }) => ( + + )} + + ); + })} + {notificationsQuery.data.length === 0 && ( +
+ +

+ No tienes ninguna notificación +

+
)} - - {notificationsQuery.data.length > 0 && - notificationsQuery.data.map((notification, index) => { - return ( - - {({ close }) => ( - - )} - - ); - })} - {notificationsQuery.data.length === 0 && ( -
- -

- No tienes ninguna notificación -

-
-
- )} -
-
-
+ + +
+ ); }; diff --git a/components/PIPIconNavbar.tsx b/components/PCPIconNavbar.tsx similarity index 59% rename from components/PIPIconNavbar.tsx rename to components/PCPIconNavbar.tsx index 32fe53e..bbd4215 100644 --- a/components/PIPIconNavbar.tsx +++ b/components/PCPIconNavbar.tsx @@ -1,18 +1,20 @@ +"use client"; import Link from "next/link"; -import PIPIcon from "./icons/PIPIcon"; +import PCPIcon from "./icons/PCPIcon"; +import { usePathname } from "next/navigation"; -interface PIPIconInterface { +interface PCPIconInterface { path: string; - currentPath: string; } -const PIPIconNavbar = ({ path, currentPath }: PIPIconInterface) => { +const PCPIconNavbar = ({ path }: PCPIconInterface) => { + const currentPath = usePathname(); const isActive = currentPath === path; return ( - @@ -20,4 +22,4 @@ const PIPIconNavbar = ({ path, currentPath }: PIPIconInterface) => { ); }; -export default PIPIconNavbar; +export default PCPIconNavbar; diff --git a/components/PipResource.tsx b/components/PipResource.tsx deleted file mode 100644 index c1bba4d..0000000 --- a/components/PipResource.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import Link from "next/link"; -import ArticleIcon from "../components/icons/ArticleIcon"; -import BookIcon from "../components/icons/BookIcon"; -import VideoIcon from "../components/icons/VideoIcon"; - -interface InterfacePipResource { - title: string | null; - description: string | null; - type: string | null; -} - -const PipResource = ({ title, description, type }: InterfacePipResource) => { - const renderIcon = () => { - switch (type) { - case "video": - return ; - case "book": - return ; - case "article": - return ; - default: - return ; - } - }; - - const renderButtonLabel = () => { - switch (type) { - case "video": - return "Watch it"; - case "book": - return "Get it"; - case "article": - return "Read it"; - default: - return "Read it"; - } - }; - - return ( -
-
- {renderIcon()} -

{title}

-
-

- {description} -

- -
- ); -}; - -export default PipResource; diff --git a/components/PipTask.tsx b/components/PipTask.tsx deleted file mode 100644 index 9b9692a..0000000 --- a/components/PipTask.tsx +++ /dev/null @@ -1,37 +0,0 @@ -interface InterfacePipTask { - title: string | null; - description: string | null; - isDone: boolean | null; - handleCheck: () => void; -} - -const PipTask = ({ - title, - description, - isDone, - handleCheck, -}: InterfacePipTask) => { - return ( -
-
-

{title}

-
- -
-
-

- {description} -

-
- ); -}; - -export default PipTask; diff --git a/components/Profile/ProfileBanner.tsx b/components/Profile/ProfileBanner.tsx index f226d71..6f27eda 100644 --- a/components/Profile/ProfileBanner.tsx +++ b/components/Profile/ProfileBanner.tsx @@ -38,6 +38,7 @@ const ProfileBanner: React.FC = async ({ user }) => { )}
Banner Image = ({ }); return ( -
+

{title}

{showMore && ( diff --git a/components/ProjectCard.tsx b/components/ProjectCard.tsx index 8a3b0d8..e25fa46 100644 --- a/components/ProjectCard.tsx +++ b/components/ProjectCard.tsx @@ -21,7 +21,7 @@ export default function ProjectCard({ description, }: ProjectProps) { const coworkersQuery = useQuery({ - queryKey: ["coworkers-in-project"], + queryKey: ["coworkers-in-project", id], queryFn: () => getEmployeesInProjectById(id), }); @@ -30,15 +30,15 @@ export default function ProjectCard({ } return ( -
-
+
+

{name}

{date &&

{date}

} {description && (

{description}

)}

In progress

-
+
    {coworkersQuery.data.map((coworker, index) => (
  • diff --git a/components/ProjectNavbarIcon.tsx b/components/ProjectNavbarIcon.tsx index 95ad070..8dcc006 100644 --- a/components/ProjectNavbarIcon.tsx +++ b/components/ProjectNavbarIcon.tsx @@ -1,14 +1,13 @@ +"use client"; import Link from "next/link"; import ProjectIcon from "../components/icons/ProjectIcon"; +import { usePathname } from "next/navigation"; interface ProjectIconNavbarInterface { path: string; - currentPath: string; } -const ProjectNavbarIcon = ({ - path, - currentPath, -}: ProjectIconNavbarInterface) => { +const ProjectNavbarIcon = ({ path }: ProjectIconNavbarInterface) => { + const currentPath = usePathname(); const isActive = currentPath === path; return ( +
      {projectsQuery.data.map((project, index) => (
    • diff --git a/components/UserIconNavbar.tsx b/components/UserIconNavbar.tsx index 6adac6d..a0b97e6 100644 --- a/components/UserIconNavbar.tsx +++ b/components/UserIconNavbar.tsx @@ -6,17 +6,14 @@ import { signOut } from "next-auth/react"; import { useQuery } from "@tanstack/react-query"; import { getUserInfo } from "@/services/user"; import UserProfileButton from "./UserProfileButton"; +import SettingsDialog from "./modals/SettingsDialog"; +import { usePathname } from "next/navigation"; interface UserIconInterface { path: string; - currentPath: string; - showSettingsModal: () => void; } -const UserIconNavbar = ({ - path, - currentPath, - showSettingsModal, -}: UserIconInterface) => { +const UserIconNavbar = ({ path }: UserIconInterface) => { + const currentPath = usePathname(); const onSite = currentPath.startsWith(path); const { data: user } = useQuery({ queryKey: ["user"], @@ -26,6 +23,7 @@ const UserIconNavbar = ({ const handleSignOut = async () => { await signOut(); }; + const [showSettingsModal, setShowSettingsModal] = useState(false); useEffect(() => { const onClickOutsideButton = (e: MouseEvent) => { @@ -38,92 +36,105 @@ const UserIconNavbar = ({ }, []); return ( - - setIsClicked(!isClicked)} - className={`${onSite || isClicked ? "bg-primary" : "bg-white transition-all delay-0 hover:scale-[1.175]"} flex rounded-full drop-shadow-lg`} - > - {/* + {showSettingsModal && ( + setShowSettingsModal(false)} + /> + )} + + setIsClicked(!isClicked)} + className={`${onSite || isClicked ? "bg-primary" : "bg-white transition-all delay-0 hover:scale-[1.175]"} flex rounded-full drop-shadow-lg`} + > + {/* */} - - - - -
      - -

      - Hello {user?.name.split(" ")[0]}! -

      -
      -
      -
      - - {({ active }) => ( - - Go Profile - - )} - - + )} + + +
      +
      {({ active }) => ( )} - -
      - - )} - - -
      -
      -
      +
+ + + + ); }; export default UserIconNavbar; diff --git a/components/icons/InfoIcon.tsx b/components/icons/InfoIcon.tsx new file mode 100644 index 0000000..ac2e991 --- /dev/null +++ b/components/icons/InfoIcon.tsx @@ -0,0 +1,22 @@ +interface InterfaceInfoIcon { + color: string; + size: string; +} +const InfoIcon = ({ color, size }: InterfaceInfoIcon) => ( + + + +); + +export default InfoIcon; diff --git a/components/icons/PIPIcon.tsx b/components/icons/PCPIcon.tsx similarity index 86% rename from components/icons/PIPIcon.tsx rename to components/icons/PCPIcon.tsx index e274e4f..03a120b 100644 --- a/components/icons/PIPIcon.tsx +++ b/components/icons/PCPIcon.tsx @@ -1,9 +1,9 @@ -interface PIPIconProps { +interface PCPIconProps { color: string; size: string; } -const PIPIcon = ({ color, size }: PIPIconProps) => { +const PCPIcon = ({ color, size }: PCPIconProps) => { return ( { ); }; -export default PIPIcon; +export default PCPIcon; diff --git a/components/modals/ProjectSurvey.tsx b/components/modals/ProjectSurvey.tsx index b760261..f7c4299 100644 --- a/components/modals/ProjectSurvey.tsx +++ b/components/modals/ProjectSurvey.tsx @@ -12,6 +12,8 @@ import { getUserId } from "@/services/user"; import toast from "react-hot-toast"; import Loader from "../Loader"; +import { useRouter } from "next/navigation"; + interface ProjectSurveyProps { showModal: boolean; onClose: () => void; @@ -22,15 +24,16 @@ const ProjectSurvey = ({ onClose, projectSurveyId, }: ProjectSurveyProps) => { + const router = useRouter(); + const { mutate } = useMutation({ mutationFn: submitProjectAnswer, onSuccess: () => { - console.log("Encuesta enviada"); + router.refresh(); toast.success("Encuesta enviada exitosamente!"); onClose(); }, onError: () => { - console.log("Error al enviar la encuesta"); toast.error("Error al enviar la encuesta"); onClose(); }, diff --git a/components/modals/SettingsDialog.tsx b/components/modals/SettingsDialog.tsx index 009edf9..0e1efbe 100644 --- a/components/modals/SettingsDialog.tsx +++ b/components/modals/SettingsDialog.tsx @@ -55,6 +55,7 @@ const SettingsDialog = ({ showModal, onClose }: SettingsDialogProps) => { Profile Image diff --git a/components/modals/SprintStepFour.tsx b/components/modals/SprintStepFour.tsx index 3c6f290..e09b843 100644 --- a/components/modals/SprintStepFour.tsx +++ b/components/modals/SprintStepFour.tsx @@ -18,6 +18,7 @@ const SprintStepFour = ({ }: SprintStepFour) => { const [usersSelected, setUsersSelected] = useState([]); const [selectableUsers, setSelectableUsers] = useState(users); + const maxWordCount = 300; const onChangeValue = (comment: string, userId: string) => { const newCoworkersComments = [...sprintSurveyAnswer.coworkersComments]; const index = newCoworkersComments.findIndex( @@ -135,16 +136,29 @@ const SprintStepFour = ({ />
-