Skip to content

Commit

Permalink
Putting the finishing touches && fix bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
aliamerj committed Mar 8, 2024
1 parent 6e92835 commit 2051719
Show file tree
Hide file tree
Showing 41 changed files with 576 additions and 353 deletions.
19 changes: 9 additions & 10 deletions app/(main)/components/navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ import Link from "next/link";
import { Button, Tooltip } from "@nextui-org/react";
import { MdLibraryAdd } from "react-icons/md";
import { AppRouter } from "@/utils/router/app_router";
import { databaseDrizzle } from "@/db/database";
import { eq } from "drizzle-orm";

import getTableCount from "@/utils/api_handler/get_table_count";

export const NavBar = async () => {
const session = await getServerSession(authOptions);
var notifications = [];
var notifications = 0;
if (session?.user.id)
notifications = await databaseDrizzle.query.notification.findMany({
where: (n) => eq(n.receiverId, session?.user.id!),
columns: {
id: true,
},
});
notifications = await getTableCount(
"notification",
"receiver_id",
`'${session.user.id}'`,
);

return (
<Navbar className={styles.container}>
Expand Down Expand Up @@ -60,7 +59,7 @@ export const NavBar = async () => {
email={session.user.email!}
userImage={session.user.image!}
userId={session.user.id!}
notificationsCount={notifications.length}
notificationsCount={notifications}
/>
) : (
<Link className={styles.getStartedBtn} href="/api/auth/signin">
Expand Down
27 changes: 9 additions & 18 deletions app/(main)/components/notification_item/notification_item.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,25 @@
"use client";
import { NotificationSelection } from "@/db/schemes/notificationSchema";
import { AppRouter } from "@/utils/router/app_router";
import Link from "next/link";
import { FaCheck, FaTimes, FaUserPlus } from "react-icons/fa";
import { NotificationBtn } from "../notifications_btn/notification_btn";
import { useState } from "react";
export type AllNotification = NotificationSelection & {
projectName?: string;
requesterName?: string;
};
import { NotificationQuery } from "../../notifications/page";

export const NotificationItem = ({
notifications,
}: {
notifications: AllNotification[];
notifications: NotificationQuery[];
}) => {
const [notific, setNotific] = useState(notifications);
const handleClientNotific = (notifId: number) =>
setNotific((current) => current.filter((n) => n.id !== notifId));

const getMessage = (notification: AllNotification) => {
const getMessage = (notification: NotificationQuery) => {
let message;
let icon;
const {
notificationType,
senderId,
requesterName,
projectName,
projectId,
} = notification;
const { notificationType, senderId, sender, projectId, project } =
notification;

switch (notificationType) {
case "JOIN_REQUEST":
Expand All @@ -38,14 +29,14 @@ export const NotificationItem = ({
href={AppRouter.viewProfile + senderId}
className="text-blue-500 hover:underline"
>
{requesterName}
{sender.name}
</Link>{" "}
has requested to join the development team for your project{" "}
<Link
href={AppRouter.viewProject + projectId}
className="text-blue-500 hover:underline"
>
{projectName}
{project.name}
</Link>
.
</>
Expand All @@ -60,7 +51,7 @@ export const NotificationItem = ({
href={AppRouter.viewProject + projectId}
className="text-blue-500 hover:underline"
>
{projectName}
{project.name}
</Link>{" "}
has been accepted.
</>
Expand All @@ -75,7 +66,7 @@ export const NotificationItem = ({
href={AppRouter.viewProject + projectId}
className="text-blue-500 hover:underline"
>
{projectName}
{project.name}
</Link>{" "}
has been rejected.
</>
Expand Down
1 change: 0 additions & 1 deletion app/(main)/components/projects_box/project_search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const ProjectSearch = () => {
placeholder="Search for projects..."
onChange={(e) => {
e.preventDefault();
console.log(e.target.value);
if (e.target.value === "") {
router.replace("?", { scroll: false });
return;
Expand Down
2 changes: 1 addition & 1 deletion app/(main)/components/userAvatar/userAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const UserAvatar = ({
<div className="flex justify-between">
<p>Notifications</p>
{notifications && (
<p className="rounded-full bg-red-600 px-2 font-bold text-white">
<p className="rounded-full bg-red-600 px-2 py-1 text-xs font-bold text-white">
{notificationsCount}
</p>
)}
Expand Down
81 changes: 53 additions & 28 deletions app/(main)/dashboard/[projectId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,64 +6,89 @@ import { parseInt } from "lodash";
import { notFound } from "next/navigation";
import { DashboardProvider } from "../context/dashboard_context";
import NotAllowedPage from "../../project/_components/not_allowed/not_allowed";
import { FeatureSelection } from "@/db/schemes/featureSchema";
import { TaskSelection } from "@/db/schemes/taskSchema";
import { ProjectSelection } from "@/db/schemes/projectSchema";

interface Props {
params: { projectId: string };
searchParams: { feature?: string };
}
export interface DevInfo {
id: string;
image: string | null;
name: string | null;
email: string;
}
export interface FeatureQuery extends FeatureSelection {
tasks?: TaskSelection[];
}
export interface ProjectQuery extends ProjectSelection {
features: FeatureQuery[];
devs?: Array<{
contributor: DevInfo;
}>;
creator: DevInfo;
}
export default async function DashboardPage({ params, searchParams }: Props) {
// Retrieve session using authOptions
const session = await getServerSession(authOptions);

const selectedFeatureId = parseInt(searchParams.feature ?? "");
// Retrieve project from database
const projectId = parseInt(params.projectId);
const project = await databaseDrizzle.query.project.findFirst({
where: (p, o) => o.eq(p.id, projectId,),
with: {
creator: {
columns: {
id: true,
image: true,
email: true,
name: true,
const project: ProjectQuery | undefined =
await databaseDrizzle.query.project.findFirst({
where: (p, o) => o.eq(p.id, projectId),
with: {
creator: {
columns: {
id: true,
image: true,
email: true,
name: true,
},
},
},
features: {
with: {
tasks: true,
features: {
with: {
tasks: true,
},
},
},
devs: {
columns: {},
with: {
contributor: {
columns: {
id: true,
image: true,
email: true,
name: true,
devs: {
columns: {},
with: {
contributor: {
columns: {
id: true,
image: true,
email: true,
name: true,
},
},
},
},
},
},
});
});
// Return 404 error if project is not found
if (!project) {
return notFound();
}
if(project.type === 'private' && project.owner === session?.user || project.devs.find(d =>d.contributor.id === session?.user.id)) return <NotAllowedPage/>

// Check if the current user is the owner of the project
const isOwner =
session?.user.id === project?.owner ? session?.user.id : undefined;

// Check if the current user is a developer associated with the project
const isDev =
session?.user.id &&
project.devs.find((d) => d.contributor.id === session.user.id)?.contributor
.id;
project.devs?.find((d) => d.contributor.id === session?.user.id)
?.contributor.id;

if (project.type === "private") {
if (!isOwner || !isDev) {
return <NotAllowedPage />;
}
}
// Return JSX element rendering TaskMangementPage component wrapped in DashboardProvider component
return (
<DashboardProvider project={project} isDev={isDev} isOwner={isOwner}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
Button,
Input,
Textarea,
Checkbox,
} from "@nextui-org/react";
import {
Controller,
Expand Down Expand Up @@ -117,6 +118,7 @@ export const AddFeatureModal = ({
tags: data.tag?.join(";") ?? "",
startDate: data.timePeriod?.startDate ?? null,
endDate: data.timePeriod?.endDate ?? null,
includeFeature: true,
};
pushFeature(newFeature);
}
Expand Down Expand Up @@ -260,6 +262,29 @@ export const AddFeatureModal = ({
)}
/>
</div>
<div>
<Controller
control={control}
name="includeFeature"
render={({ field, fieldState: { error } }) => (
<>
{error && (
<p className="mb-1 text-xs italic text-red-500">
{error.message}
</p>
)}
<Checkbox
radius="md"
isSelected={field.value}
onValueChange={(v) => field.onChange(v)}
color="primary"
>
Include this feature in project page
</Checkbox>
</>
)}
/>
</div>
</div>
</ModalBody>
<ModalFooter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ export const AddTaskModal = ({
task.featureId === featureId ? Math.max(max, task.order) : max,
0,
);
console.log({ selectedTaskToUpdate });

const { control, handleSubmit, reset } = useForm<TaskSchema>({
defaultValues: {
status: "New",
Expand Down
52 changes: 29 additions & 23 deletions app/(main)/dashboard/_component/board_feature/board_feature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@ import {
AiOutlineFileText,
} from "react-icons/ai";
import { DroppableArea } from "../droppable_area/droppable_area";
import { KanbanTask } from "../kanban_task/kanban_task";
import { Button, useDisclosure } from "@nextui-org/react";

import { Button, Spinner, useDisclosure } from "@nextui-org/react";
import { AddTaskModal } from "../add_task_modal/add_task_modal";
import { useSetTasksToUpdate } from "../../context/hooks";
import { useCurrentProject, useSetTasksToUpdate } from "../../context/hooks";
import { FeatureSelection } from "@/db/schemes/featureSchema";

import dynamic from "next/dynamic";
const KanbanTask = dynamic(() => import("../kanban_task/kanban_task"), {
ssr: false,
loading: () => (
<div className="flex h-60 w-full items-center justify-center ">
<Spinner color="primary" size="lg" />
</div>
),
});
export const BoardFeature = ({
provided,
isDraggingOver,
isDev,
isOwner,
feature,
}: {
provided: DroppableProvided;
Expand All @@ -29,6 +35,7 @@ export const BoardFeature = ({
feature: FeatureSelection;
}) => {
const { isOpen, onOpen, onOpenChange } = useDisclosure();
const { isOwner, isDev } = useCurrentProject();
const setSelectedTaskToUpdate = useSetTasksToUpdate();

return (
Expand All @@ -53,7 +60,7 @@ export const BoardFeature = ({
<div className="mt-2 flex items-center gap-2">
{feature.tags?.split(";").map((tag, index) => (
<span
key={index}
key={tag + index}
className="flex items-center gap-2 rounded-full bg-blue-100 px-3 py-1 text-sm text-blue-800"
>
<AiFillTag className="text-blue-700" />
Expand All @@ -71,22 +78,21 @@ export const BoardFeature = ({
{feature.endDate ? formatDate(feature.endDate) : "N/A"}
</span>
</div>
{isDev ||
(isOwner && (
<div className="mt-5">
<Button
color="primary"
variant="shadow"
size="lg"
onPress={(_) => {
setSelectedTaskToUpdate(undefined);
onOpen();
}}
>
Create New Task
</Button>
</div>
))}
{(isDev || isOwner) && (
<div className="mt-5">
<Button
color="primary"
variant="shadow"
size="lg"
onPress={(_) => {
setSelectedTaskToUpdate(undefined);
onOpen();
}}
>
Create New Task
</Button>
</div>
)}
</div>
<KanbanTask featureId={feature.id} onOpen={onOpen} />
<AddTaskModal
Expand Down
Loading

0 comments on commit 2051719

Please sign in to comment.