diff --git a/frontend/README.md b/frontend/README.md index c4033664..9698ad45 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -2,16 +2,16 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next ## Getting Started -First, run the development server: +First, install all the dependencies: + +```bash +npm ci +``` + +Second, run the development server: ```bash npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. diff --git a/frontend/src/app/(authenticated)/approved-service-requests/_hooks/use-approved-service-requests.ts b/frontend/src/app/(authenticated)/approved-service-requests/_hooks/use-approved-service-requests.ts new file mode 100644 index 00000000..035e7cf6 --- /dev/null +++ b/frontend/src/app/(authenticated)/approved-service-requests/_hooks/use-approved-service-requests.ts @@ -0,0 +1,106 @@ +import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" + +const DUMMY_SERVICE_REQUESTS: ServiceRequest[] = [ + { + id: "1", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.RUNNING, + created_on: "2024-02-21T19:50:01", + created_by: "User 1", + last_updated: "2024-02-21T19:50:01", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "2", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.RUNNING, + created_on: "2024-02-21T18:50:01", + created_by: "User 2", + last_updated: "2024-02-21T18:50:01", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "3", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.SUCCESS, + created_on: "2024-02-21T17:00:00", + created_by: "User 3", + last_updated: "2024-02-21T17:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "4", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.SUCCESS, + created_on: "2024-02-21T00:00:00", + created_by: "User 4", + last_updated: "2024-02-21T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "5", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.SUCCESS, + created_on: "2024-02-20T00:00:00", + created_by: "User 1", + last_updated: "2024-02-20T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "6", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.SUCCESS, + created_on: "2024-02-10T00:00:00", + created_by: "User 2", + last_updated: "2024-02-10T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, +] + +const useApprovedServiceRequest = () => { + return { serviceRequests: DUMMY_SERVICE_REQUESTS } +} + +export default useApprovedServiceRequest diff --git a/frontend/src/app/(authenticated)/approved-service-requests/columns.tsx b/frontend/src/app/(authenticated)/approved-service-requests/columns.tsx new file mode 100644 index 00000000..a4e2fe3c --- /dev/null +++ b/frontend/src/app/(authenticated)/approved-service-requests/columns.tsx @@ -0,0 +1,55 @@ +"use client" + +import { formatDateString, formatTimeDifference } from "@/lib/utils" +import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" +import { ColumnDef } from "@tanstack/react-table" +import Link from "next/link" +import StatusBadge from "@/components/layouts/status-badge" + +export const approvedServiceRequestColumns: ColumnDef[] = [ + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => { + const status: ServiceRequestStatus = row.getValue("status") + return + }, + }, + { + accessorKey: "pipeline_id", + header: "Pipeline", + cell: ({ row }) => { + const pipelineId: string = row.getValue("pipeline_id") + return ( + + {pipelineId} + + ) + }, + }, + { + accessorKey: "created_on", + header: "Created Date", + cell: ({ row }) => { + const dateIsoString: string = row.getValue("created_on") + const dateObject = new Date(dateIsoString) + return formatDateString(dateObject) + }, + }, + { + accessorKey: "created_by", + header: "Created By", + }, + { + accessorKey: "last_updated", + header: "Last Updated", + cell: ({ row }) => { + const dateIsoString: string = row.getValue("last_updated") + const dateObject = new Date(dateIsoString) + return formatTimeDifference(dateObject) + }, + }, +] diff --git a/frontend/src/app/(authenticated)/approved-service-requests/page.tsx b/frontend/src/app/(authenticated)/approved-service-requests/page.tsx new file mode 100644 index 00000000..5942c6e9 --- /dev/null +++ b/frontend/src/app/(authenticated)/approved-service-requests/page.tsx @@ -0,0 +1,26 @@ +"use client" + +import HeaderAccessory from "@/components/ui/header-accessory" +import { approvedServiceRequestColumns } from "./columns" +import { DataTable } from "@/components/layouts/data-table" +import useApprovedServiceRequest from "./_hooks/use-approved-service-requests" + +export default function ApproveServiceRequestPage() { + const { serviceRequests } = useApprovedServiceRequest() + return ( +
+ +
+

+ Your Approved Service Requests +

+
+
+ +
+
+ ) +} diff --git a/frontend/src/app/(authenticated)/pending-service-requests/_components/pending-service-request-actions.tsx b/frontend/src/app/(authenticated)/pending-service-requests/_components/pending-service-request-actions.tsx new file mode 100644 index 00000000..375ec02d --- /dev/null +++ b/frontend/src/app/(authenticated)/pending-service-requests/_components/pending-service-request-actions.tsx @@ -0,0 +1,51 @@ +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { MoreHorizontal } from "lucide-react" + +interface ApproveServiceRequestActionsProps { + pipelineId: string + approveRequest: (pipelineId: string) => void + rejectRequest: (pipelineId: string) => void +} + +export default function ApproveServiceRequestActions({ + pipelineId, + approveRequest, + rejectRequest, +}: ApproveServiceRequestActionsProps) { + return ( + + + + + + + + + + {/* TODO: Add on click logic*/} + + + + + ) +} diff --git a/frontend/src/app/(authenticated)/pending-service-requests/_hooks/use-pending-service-requests.ts b/frontend/src/app/(authenticated)/pending-service-requests/_hooks/use-pending-service-requests.ts new file mode 100644 index 00000000..9765b4b2 --- /dev/null +++ b/frontend/src/app/(authenticated)/pending-service-requests/_hooks/use-pending-service-requests.ts @@ -0,0 +1,106 @@ +import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" + +const DUMMY_SERVICE_REQUESTS: ServiceRequest[] = [ + { + id: "1", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.PENDING, + created_on: "2024-02-21T19:50:01", + created_by: "User 1", + last_updated: "2024-02-21T19:50:01", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "2", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.PENDING, + created_on: "2024-02-21T18:50:01", + created_by: "User 2", + last_updated: "2024-02-21T18:50:01", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "3", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.PENDING, + created_on: "2024-02-21T17:00:00", + created_by: "User 3", + last_updated: "2024-02-21T17:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "4", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.PENDING, + created_on: "2024-02-21T00:00:00", + created_by: "User 4", + last_updated: "2024-02-21T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "5", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.PENDING, + created_on: "2024-02-20T00:00:00", + created_by: "User 1", + last_updated: "2024-02-20T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "6", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.PENDING, + created_on: "2024-02-10T00:00:00", + created_by: "User 2", + last_updated: "2024-02-10T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, +] + +const usePendingServiceRequest = () => { + return { serviceRequests: DUMMY_SERVICE_REQUESTS } +} + +export default usePendingServiceRequest diff --git a/frontend/src/app/(authenticated)/pending-service-requests/columns.tsx b/frontend/src/app/(authenticated)/pending-service-requests/columns.tsx new file mode 100644 index 00000000..15441657 --- /dev/null +++ b/frontend/src/app/(authenticated)/pending-service-requests/columns.tsx @@ -0,0 +1,70 @@ +"use client" + +import { formatDateString, formatTimeDifference } from "@/lib/utils" +import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" +import { ColumnDef } from "@tanstack/react-table" +import Link from "next/link" +import StatusBadge from "@/components/layouts/status-badge" +import ApproveServiceRequestActions from "./_components/pending-service-request-actions" + +export const pendingServiceRequestColumns: ColumnDef[] = [ + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => { + const status: ServiceRequestStatus = row.getValue("status") + return + }, + }, + { + accessorKey: "pipeline_id", + header: "Pipeline", + cell: ({ row }) => { + const pipelineId: string = row.getValue("pipeline_id") + return ( + + {pipelineId} + + ) + }, + }, + { + accessorKey: "created_on", + header: "Created Date", + cell: ({ row }) => { + const dateIsoString: string = row.getValue("created_on") + const dateObject = new Date(dateIsoString) + return formatDateString(dateObject) + }, + }, + { + accessorKey: "created_by", + header: "Created By", + }, + { + accessorKey: "last_updated", + header: "Last Updated", + cell: ({ row }) => { + const dateIsoString: string = row.getValue("last_updated") + const dateObject = new Date(dateIsoString) + return formatTimeDifference(dateObject) + }, + }, + { + id: "actions", + header: "Actions", + cell: ({ row }) => { + const pipelineId: string = row.getValue("pipeline_id") + return ( + {}} + rejectRequest={(pipelineId: string) => {}} + /> + ) + }, + }, +] diff --git a/frontend/src/app/(authenticated)/pending-service-requests/page.tsx b/frontend/src/app/(authenticated)/pending-service-requests/page.tsx new file mode 100644 index 00000000..b79ed2a7 --- /dev/null +++ b/frontend/src/app/(authenticated)/pending-service-requests/page.tsx @@ -0,0 +1,24 @@ +"use client" + +import HeaderAccessory from "@/components/ui/header-accessory" +import usePendingServiceRequest from "./_hooks/use-pending-service-requests" +import { pendingServiceRequestColumns } from "./columns" +import { DataTable } from "@/components/layouts/data-table" + +export default function ApproveServiceRequestPage() { + const { serviceRequests } = usePendingServiceRequest() + return ( +
+ +
+

Pending Service Requests

+
+
+ +
+
+ ) +} diff --git a/frontend/src/app/(authenticated)/rejected-service-requests/_hooks/use-rejected-service-requests.ts b/frontend/src/app/(authenticated)/rejected-service-requests/_hooks/use-rejected-service-requests.ts new file mode 100644 index 00000000..0fa607de --- /dev/null +++ b/frontend/src/app/(authenticated)/rejected-service-requests/_hooks/use-rejected-service-requests.ts @@ -0,0 +1,106 @@ +import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" + +const DUMMY_SERVICE_REQUESTS: ServiceRequest[] = [ + { + id: "1", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-21T19:50:01", + created_by: "User 1", + last_updated: "2024-02-21T19:50:01", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "2", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-21T18:50:01", + created_by: "User 2", + last_updated: "2024-02-21T18:50:01", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "3", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-21T17:00:00", + created_by: "User 3", + last_updated: "2024-02-21T17:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "4", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-21T00:00:00", + created_by: "User 4", + last_updated: "2024-02-21T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "5", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-20T00:00:00", + created_by: "User 1", + last_updated: "2024-02-20T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, + { + id: "6", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-10T00:00:00", + created_by: "User 2", + last_updated: "2024-02-10T00:00:00", + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, +] + +const useRejectedServiceRequests = () => { + return { serviceRequests: DUMMY_SERVICE_REQUESTS } +} + +export default useRejectedServiceRequests diff --git a/frontend/src/app/(authenticated)/rejected-service-requests/columns.tsx b/frontend/src/app/(authenticated)/rejected-service-requests/columns.tsx new file mode 100644 index 00000000..14c669b1 --- /dev/null +++ b/frontend/src/app/(authenticated)/rejected-service-requests/columns.tsx @@ -0,0 +1,55 @@ +"use client" + +import { formatDateString, formatTimeDifference } from "@/lib/utils" +import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" +import { ColumnDef } from "@tanstack/react-table" +import Link from "next/link" +import StatusBadge from "@/components/layouts/status-badge" + +export const rejectedServiceRequestColumns: ColumnDef[] = [ + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => { + const status: ServiceRequestStatus = row.getValue("status") + return + }, + }, + { + accessorKey: "pipeline_id", + header: "Pipeline", + cell: ({ row }) => { + const pipelineId: string = row.getValue("pipeline_id") + return ( + + {pipelineId} + + ) + }, + }, + { + accessorKey: "created_on", + header: "Created Date", + cell: ({ row }) => { + const dateIsoString: string = row.getValue("created_on") + const dateObject = new Date(dateIsoString) + return formatDateString(dateObject) + }, + }, + { + accessorKey: "created_by", + header: "Created By", + }, + { + accessorKey: "last_updated", + header: "Last Updated", + cell: ({ row }) => { + const dateIsoString: string = row.getValue("last_updated") + const dateObject = new Date(dateIsoString) + return formatTimeDifference(dateObject) + }, + }, +] diff --git a/frontend/src/app/(authenticated)/rejected-service-requests/page.tsx b/frontend/src/app/(authenticated)/rejected-service-requests/page.tsx new file mode 100644 index 00000000..5c7ada4b --- /dev/null +++ b/frontend/src/app/(authenticated)/rejected-service-requests/page.tsx @@ -0,0 +1,26 @@ +"use client" + +import HeaderAccessory from "@/components/ui/header-accessory" +import { rejectedServiceRequestColumns } from "./columns" +import { DataTable } from "@/components/layouts/data-table" +import useApprovedServiceRequest from "./_hooks/use-rejected-service-requests" + +export default function RejectedServiceRequestPage() { + const { serviceRequests } = useApprovedServiceRequest() + return ( +
+ +
+

+ Your Rejected Service Requests +

+
+
+ +
+
+ ) +} diff --git a/frontend/src/app/(authenticated)/service-request-dashboard/_hooks/use-service-requests.ts b/frontend/src/app/(authenticated)/service-request-dashboard/_hooks/use-service-requests.ts index 8b3d1436..f4e39db0 100644 --- a/frontend/src/app/(authenticated)/service-request-dashboard/_hooks/use-service-requests.ts +++ b/frontend/src/app/(authenticated)/service-request-dashboard/_hooks/use-service-requests.ts @@ -63,6 +63,22 @@ const DUMMY_SERVICE_REQUESTS: ServiceRequest[] = [ description: "Pipeline 1 description", }, }, + { + id: "4", + pipeline_id: "65d48c02d62a1281c4f4ba3e", + pipeline_version: "0", + status: ServiceRequestStatus.REJECTED, + created_on: "2024-02-21T00:00:00", + last_updated: "2024-02-21T00:00:00", + + remarks: "", + form_data: { + user_id: "1", + user_name: "Test User", + name: "Pipeline 1", + description: "Pipeline 1 description", + }, + }, { id: "5", pipeline_id: "65d48c02d62a1281c4f4ba3e", diff --git a/frontend/src/app/(authenticated)/service-request-dashboard/columns.tsx b/frontend/src/app/(authenticated)/service-request-dashboard/columns.tsx index ac9d3cdc..d47276a1 100644 --- a/frontend/src/app/(authenticated)/service-request-dashboard/columns.tsx +++ b/frontend/src/app/(authenticated)/service-request-dashboard/columns.tsx @@ -5,7 +5,7 @@ import { ServiceRequest, ServiceRequestStatus } from "@/types/service-request" import { ColumnDef } from "@tanstack/react-table" import { Calendar } from "lucide-react" import Link from "next/link" -import StatusBadge from "./_components/status-badges" +import StatusBadge from "../../../components/layouts/status-badge" import ServiceRequestActions from "./_components/service-request-actions" export const columns: ColumnDef[] = [ diff --git a/frontend/src/components/layouts/sidebar.tsx b/frontend/src/components/layouts/sidebar.tsx index 1027d698..976867a1 100644 --- a/frontend/src/components/layouts/sidebar.tsx +++ b/frontend/src/components/layouts/sidebar.tsx @@ -2,7 +2,15 @@ import { cn } from "@/lib/utils" import Link from "next/link" import React from "react" import { buttonVariants } from "../ui/button" -import { GitBranchPlus, LibraryBig, Workflow } from "lucide-react" +import { + CheckCircle2, + CircleEllipsis, + CircleSlash2, + GitBranchPlus, + LibraryBig, + Workflow, + XOctagon, +} from "lucide-react" type LinkType = { title: string @@ -38,6 +46,24 @@ const links: LinkType[] = [ href: "#", variant: "ghost", }, + { + title: "Pending Service Requests", + icon: CircleEllipsis, + href: "/pending-service-requests", + variant: "ghost", + }, + { + title: "Approved Service Requests", + icon: CheckCircle2, + href: "/approved-service-requests", + variant: "ghost", + }, + { + title: "Rejected Service Requests", + icon: XOctagon, + href: "/rejected-service-requests", + variant: "ghost", + }, ] interface SidebarProps { diff --git a/frontend/src/app/(authenticated)/service-request-dashboard/_components/status-badges.tsx b/frontend/src/components/layouts/status-badge.tsx similarity index 92% rename from frontend/src/app/(authenticated)/service-request-dashboard/_components/status-badges.tsx rename to frontend/src/components/layouts/status-badge.tsx index 8c9234e6..528a65e8 100644 --- a/frontend/src/app/(authenticated)/service-request-dashboard/_components/status-badges.tsx +++ b/frontend/src/components/layouts/status-badge.tsx @@ -9,6 +9,7 @@ import { CircleOff, Moon, XCircle, + XOctagon, } from "lucide-react" const statusBadgeVariant = cva("rounded-lg border text-sm font-medium", { @@ -16,6 +17,7 @@ const statusBadgeVariant = cva("rounded-lg border text-sm font-medium", { status: { [ServiceRequestStatus.NOT_STARTED]: " text-slate-500 border-slate-300", [ServiceRequestStatus.PENDING]: "text-yellow-500 border-yellow-300", + [ServiceRequestStatus.REJECTED]: "text-red-800 border-yellow-800", [ServiceRequestStatus.RUNNING]: "text-blue-500 border-blue-300", [ServiceRequestStatus.SUCCESS]: "text-green-500 border-green-300", [ServiceRequestStatus.FAILURE]: "text-red-500 border-red-300", @@ -37,6 +39,8 @@ const StatusIcon = ({ status }: { status: ServiceRequestStatus }) => { return case ServiceRequestStatus.PENDING: return + case ServiceRequestStatus.REJECTED: + return case ServiceRequestStatus.RUNNING: return case ServiceRequestStatus.SUCCESS: diff --git a/frontend/src/types/service-request.ts b/frontend/src/types/service-request.ts index 8bb00b82..926679e4 100644 --- a/frontend/src/types/service-request.ts +++ b/frontend/src/types/service-request.ts @@ -12,6 +12,7 @@ type ServiceRequestForm = { enum ServiceRequestStatus { NOT_STARTED = "Not Started", PENDING = "Pending", + REJECTED = "Rejected", RUNNING = "Running", SUCCESS = "Success", FAILURE = "Failure", @@ -24,6 +25,8 @@ type ServiceRequest = { pipeline_version: string status: ServiceRequestStatus created_on: string + // TODO: Make field mandatory once accounts are tag to service request + created_by?: string last_updated: string remarks: string form_data: ServiceRequestForm