diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 00000000..f49d0739 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,19 @@ +name: build-test + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build-lint-test: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v3 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: 'yarn.lock' + - run: yarn + - run: yarn build diff --git a/public/cross.svg b/public/cross.svg new file mode 100644 index 00000000..7cbbe0ed --- /dev/null +++ b/public/cross.svg @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/public/google_icon.svg b/public/google_icon.svg new file mode 100644 index 00000000..d949ddeb --- /dev/null +++ b/public/google_icon.svg @@ -0,0 +1,44 @@ + \ No newline at end of file diff --git a/public/icons/lock.svg b/public/icons/lock.svg new file mode 100644 index 00000000..a8318821 --- /dev/null +++ b/public/icons/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/profile-icon.svg b/public/profile-icon.svg new file mode 100644 index 00000000..6c2483e4 --- /dev/null +++ b/public/profile-icon.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/tick.svg b/public/tick.svg new file mode 100644 index 00000000..28ee9e4b --- /dev/null +++ b/public/tick.svg @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/src/app/(routes)/faculty/layout.tsx b/src/app/(routes)/faculty/layout.tsx new file mode 100644 index 00000000..718257b2 --- /dev/null +++ b/src/app/(routes)/faculty/layout.tsx @@ -0,0 +1,11 @@ +import Link from "next/link"; + +interface Props { + children: React.ReactNode; +} + +const FacultyLayout = ({ children }: Props) => { + return
{children}
; +}; + +export default FacultyLayout; diff --git a/src/app/(routes)/faculty/page.tsx b/src/app/(routes)/faculty/page.tsx new file mode 100644 index 00000000..38783f0b --- /dev/null +++ b/src/app/(routes)/faculty/page.tsx @@ -0,0 +1,74 @@ +"use client"; +import { fetchApprovals, updateApproval } from "@/helpers/faculty/api"; +import Cookies from "js-cookie"; +import TableComponent from "@/components/TableComponent/TableComponent"; +import generateColumns from "@/components/TableComponent/ColumnMapping"; +import { useState, useEffect } from "react"; +import loadingImg from "@/components/Faculty/loadingSpinner.svg"; +import { number } from "yup"; + +const dto = [ + { + status: "string", + remarks: "string", + salary: { + salaryPeriod: "string", + totalCTC: "number", + job: { + role: "string", + company: { + name: "string", + }, + season: { + year: "string", + type: "string", + }, + }, + }, + }, +]; + +const dynamicColumns = generateColumns(dto); + +const FacultyPage = () => { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchData = async () => { + try { + const jsonData = await fetchApprovals( + Cookies.get("accessToken"), + undefined + ); + setData(jsonData); + } catch (error) { + console.error("Error fetching data:", error); + } finally { + setLoading(false); + } + }; + + fetchData(); + }, []); + return ( +
+

Approvals

+
+ {loading && ( + + )} + {data && ( + + )} +
+
+ ); +}; + +export default FacultyPage; diff --git a/src/app/(routes)/faculty/profile/page.tsx b/src/app/(routes)/faculty/profile/page.tsx new file mode 100644 index 00000000..162767af --- /dev/null +++ b/src/app/(routes)/faculty/profile/page.tsx @@ -0,0 +1,31 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import FacultyProfile from "@/components/Faculty/Profile"; +import { fetchProfile } from "@/helpers/faculty/api"; +import Cookies from "js-cookie"; +import loadingImg from "@/components/Faculty/loadingSpinner.svg"; +import { ProfileFC } from "@/helpers/faculty/api"; + +const Profile = ({ params }: { params: { facultyId: string } }) => { + const [data, setData] = useState(); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const profileData = async () => { + const jsonData = await fetchProfile(Cookies.get("accessToken")); + setData(jsonData); + setLoading(false); + }; + profileData(); + }, []); + + return ( +
+ {loading && } + {data && } +
+ ); +}; + +export default Profile; diff --git a/src/app/(routes)/student/jobs/[jobId]/page.tsx b/src/app/(routes)/student/job/[jobId]/page.tsx similarity index 85% rename from src/app/(routes)/student/jobs/[jobId]/page.tsx rename to src/app/(routes)/student/job/[jobId]/page.tsx index 3cdd18da..9e3cc509 100644 --- a/src/app/(routes)/student/jobs/[jobId]/page.tsx +++ b/src/app/(routes)/student/job/[jobId]/page.tsx @@ -1,7 +1,6 @@ "use client"; import React, { useEffect, useState } from "react"; import { Separator } from "@/components/ui/separator"; -import Link from "next/link"; import { Table, TableHeader, @@ -12,20 +11,20 @@ import { TableCell, } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; -import { fetchEachJob } from "@/helpers/api"; import HorizontalTimeline from "@/components/HorizontalTimeline"; import { Job, CustomEvent, EventData, CalenderEvent } from "@/helpers/student/types"; import { GetJobById } from "@/helpers/student/api"; +import Cookies from "js-cookie"; function transformEvents(events: CustomEvent[]): EventData[] { - // Get the current date + const currentDate = new Date(); - // Sort events by startDateTime + const sortedEvents = events.sort((a, b) => new Date(a.startDateTime).getTime() - new Date(b.startDateTime).getTime()); - // Transform the sorted events into the desired format + const result: EventData[] = sortedEvents.map(event => { const eventDate = new Date(event.startDateTime); let status: string; @@ -37,18 +36,18 @@ function transformEvents(events: CustomEvent[]): EventData[] { } return { - date: eventDate.toLocaleDateString('en-GB'), // Convert date to DD/MM/YYYY format + date: eventDate.toLocaleDateString('en-GB'), status, title: event.type, }; }); - // Find the first event after the current date and mark it as 'selected' + const firstFutureEventIndex = result.findIndex(event => new Date(event.date.split('/').reverse().join('-')) >= currentDate); if (firstFutureEventIndex !== -1) { result[firstFutureEventIndex].status = "selected"; } else if (result.length > 0) { - // If all events have passed, mark the last event as 'selected' + result[result.length - 1].status = "selected"; } @@ -57,10 +56,10 @@ function transformEvents(events: CustomEvent[]): EventData[] { const transformEventsCalender = (jobData: Job): CalenderEvent[] => { return jobData.events.map(event => ({ - day: new Date(event.startDateTime).setHours(0, 0, 0, 0), // startDate at midnight + day: new Date(event.startDateTime).setHours(0, 0, 0, 0), description: event.metadata, id: event.id, - label: "red", // assuming a fixed label as the original data doesn't provide this + label: "red", timeFrom: event.startDateTime, timeTo: event.endDateTime, title: jobData.companyDetailsFilled.name, @@ -85,18 +84,13 @@ function formatNumber(num: number): string { return num.toString(); } -const faculty = [ - { name: 'Emily Johnson', designation: 'Professor', email: 'emily.johnson@example.com', phoneNumber: '123-456-7890' }, - { name: 'Alex Smith', designation: 'Assistant Professor', email: 'alex.smith@example.com', phoneNumber: '987-654-3210' }, -]; - const JobPage = ({ params }: { params: { jobId: string } }) => { const [jobData, setJobData] = useState(null); useEffect(() => { const fetchJobData = async () => { - const data = await GetJobById(params.jobId); + const data = await GetJobById(params.jobId, Cookies.get("accessToken")); setJobData(data); storeCalenderEvents(data); }; @@ -122,7 +116,7 @@ const JobPage = ({ params }: { params: { jobId: string } }) => {
Website
{" "} - Link + Link
Domain
{" "} @@ -201,7 +195,6 @@ const JobPage = ({ params }: { params: { jobId: string } }) => {
- {/* */}
@@ -209,7 +202,7 @@ const JobPage = ({ params }: { params: { jobId: string } }) => {
diff --git a/src/app/(routes)/student/jobs/salary/[jobId]/page.tsx b/src/app/(routes)/student/job/salary/[jobId]/page.tsx similarity index 71% rename from src/app/(routes)/student/jobs/salary/[jobId]/page.tsx rename to src/app/(routes)/student/job/salary/[jobId]/page.tsx index f800c79a..0bf9740d 100644 --- a/src/app/(routes)/student/jobs/salary/[jobId]/page.tsx +++ b/src/app/(routes)/student/job/salary/[jobId]/page.tsx @@ -1,20 +1,11 @@ "use client"; import React, { useEffect, useState } from "react"; -import { - Table, - TableHeader, - TableBody, - TableFooter, - TableHead, - TableRow, - TableCell, -} from "@/components/ui/table"; -import { SampleJobData } from "@/dummyData/job"; -import { Separator } from "@/components/ui/separator"; import SalaryCard from "@/components/jobs/SalaryCard"; -import { GetJobById } from "@/helpers/student/api"; +import Cookies from "js-cookie"; +import { GetJobById, GetResumes } from "@/helpers/student/api"; +import { Resume } from "@/helpers/student/types"; + -interface Props {} interface Salary { id: string; salaryPeriod: string; @@ -34,11 +25,15 @@ interface Salary { const SalaryPage = ({ params }: { params: { jobId: string } }) => { const [salaryData, setSalaryData] = useState([]); + const [resumes, setResumes] = useState([]); useEffect(() => { const fetchSalaryData = async () => { - const data = await GetJobById(params.jobId); + const data = await GetJobById(params.jobId, Cookies.get("accessToken")); setSalaryData(data.salaries); + + const res = await GetResumes(Cookies.get("accessToken")); + setResumes(res); }; fetchSalaryData(); @@ -53,7 +48,7 @@ const SalaryPage = ({ params }: { params: { jobId: string } }) => {
): (salaryData.map((item,index)=>(
- +
)))}
diff --git a/src/app/(routes)/student/jobs/page.tsx b/src/app/(routes)/student/jobs/page.tsx new file mode 100644 index 00000000..2ca3fcd4 --- /dev/null +++ b/src/app/(routes)/student/jobs/page.tsx @@ -0,0 +1,45 @@ +"use client"; +import React, { useEffect, useState } from "react"; +import JobCard from "@/components/jobs/JobCard"; +import Cookies from "js-cookie"; +import { GetJobs, GetResumes } from "@/helpers/student/api"; +import { Jobs, Resume } from "@/helpers/student/types"; + +const StudentPage = () => { + + const [jobs, setJobs] = useState([]); + const [resumes, setResumes] = useState([]) + + useEffect(() => { + const fetchJobs = async () => { + const data = await GetJobs(Cookies.get("accessToken")); + setJobs(data); + + const res = await GetResumes(Cookies.get("accessToken")); + setResumes(res); + + }; + + fetchJobs(); + // setJobs(Jobs); + }, []); + + return ( +
+
+

Apply

+
+ {jobs.length===0? ( +
+ No Jobs +
+ ): (jobs.map((job)=>( +
+ +
+ )))} +
+ ); +}; + +export default StudentPage; diff --git a/src/app/(routes)/student/offCampus/page.tsx b/src/app/(routes)/student/offCampus/page.tsx index a3671d74..a0952d1e 100644 --- a/src/app/(routes)/student/offCampus/page.tsx +++ b/src/app/(routes)/student/offCampus/page.tsx @@ -25,7 +25,7 @@ const OffCampusPage = () => { return (
-

Apply

+

Off Campus Offers

{offCampusOffers.length===0? (
diff --git a/src/app/(routes)/student/onCampus/page.tsx b/src/app/(routes)/student/onCampus/page.tsx index 4c3c0f2e..29e8f459 100644 --- a/src/app/(routes)/student/onCampus/page.tsx +++ b/src/app/(routes)/student/onCampus/page.tsx @@ -8,40 +8,34 @@ import { Button } from "@/components/ui/button"; import Cookies from "js-cookie"; import { GetOnCampusOffers, GetResumes } from "@/helpers/student/api"; import { OnCampusOffers, Resume } from "@/helpers/student/types"; - -interface Props {} +import OnCampusCard from "@/components/jobs/OnCampusCard"; const StudentPage = () => { const [onCampusOffers, setOnCampusOffers] = useState([]); - const [resumes, setResumes] = useState([]) useEffect(() => { - const fetchJobs = async () => { + const fetchOffers = async () => { const oco = await GetOnCampusOffers(Cookies.get("accessToken")); setOnCampusOffers(oco); - const res = await GetResumes(Cookies.get("accessToken")); - setResumes(res); - }; - fetchJobs(); - // setJobs(Jobs); + fetchOffers(); }, []); return (
-

Apply

+

On Campus Offers

{onCampusOffers.length===0? (
- No Jobs + No Offers
): (onCampusOffers.map((job)=>(
- +
)))}
diff --git a/src/app/(routes)/student/profile/page.tsx b/src/app/(routes)/student/profile/page.tsx index d4bc60d2..3906f300 100644 --- a/src/app/(routes)/student/profile/page.tsx +++ b/src/app/(routes)/student/profile/page.tsx @@ -34,7 +34,9 @@ const ProfilePage = () => { } } - fetchStudentData(); + if(studentData===null){ + fetchStudentData(); + } }) @@ -104,36 +106,40 @@ const ProfilePage = () => {
-
- -
- -

Penalties

- - - - Sr. - Reason - Penalty - - - - {studentData.penalties.map((item,index)=>( - - {index+1} - {item.reason} - {item.penalty} - - ))} - - - - - - {totalPenalty} - - -
+ {totalPenalty>0 && ( + <> +
+ +
+ +

Penalties

+ + + + Sr. + Reason + Penalty + + + + {studentData.penalties.map((item,index)=>( + + {index+1} + {item.reason} + {item.penalty} + + ))} + + + + + + {totalPenalty} + + +
+ + )}
@@ -164,10 +170,6 @@ const ProfilePage = () => { -
- -
-
diff --git a/src/app/(routes)/student/resumes/page.tsx b/src/app/(routes)/student/resumes/page.tsx index 1293e386..69be55c2 100644 --- a/src/app/(routes)/student/resumes/page.tsx +++ b/src/app/(routes)/student/resumes/page.tsx @@ -18,6 +18,7 @@ import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { uploadResume } from "@/helpers/student/api"; import toast, { Toaster } from "react-hot-toast"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; const baseUrl = process.env.NEXT_PUBLIC_BACKEND_URL; @@ -31,17 +32,13 @@ const url = (NextUrl: string) => { const ResumePage = () => { const [resumeData, setResumeData] = useState([]); - const [showUploadForm, setShowUploadForm] = useState(false); + const [dialogOpen, setDialogOpen] = useState(false); const fetchResumes = async () => { const data = await GetResumes(Cookies.get("accessToken")); setResumeData(data); } - const handleUploadForm = () => { - setShowUploadForm(true); - } - const [file, setFile] = useState(null); const handleFileChange = (event: ChangeEvent) => { @@ -67,6 +64,7 @@ const ResumePage = () => { toast.success("Uploaded Successfully"); fetchResumes(); setFile(null); + setDialogOpen(false) } else{ toast.error("Error uploading file") @@ -136,19 +134,45 @@ const ResumePage = () => {
- + + + + + + + Upload Resume + + Select and upload your resume file. + + +
+
+ + +
+
+ + +
+ + + +
+
+
- {showUploadForm && ( -
-
-
- - -
- -
-
- )} ); }; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 6670d4a6..d549816f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -15,7 +15,7 @@ import { Suspense } from "react"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { - title: "Create Next App", + title: "TPC Portal", description: "Generated by create next app", }; diff --git a/src/components/Faculty/FeedbackFrom.tsx b/src/components/Faculty/FeedbackFrom.tsx new file mode 100644 index 00000000..ad8567f1 --- /dev/null +++ b/src/components/Faculty/FeedbackFrom.tsx @@ -0,0 +1,166 @@ +import { + Dialog, + DialogOverlay, + DialogContent, + DialogTrigger, + DialogClose, + DialogHeader, + DialogTitle, + DialogDescription, +} from "../ui/dialog"; +import TextArea from "antd/es/input/TextArea"; +import { Button } from "@/components/ui/button"; +import { useState, useEffect } from "react"; +import { updateApproval } from "@/helpers//faculty/api"; +import Cookies from "js-cookie"; +import tick from "@/../public/tick.svg"; +import cross from "@/../public/cross.svg"; + +interface ButtonProps { + children?: React.ReactNode; + rows?: any[]; + remarks?: string; + finalButton: boolean; +} + +const UpdateApprovalPATCH = (rows: any[], remarks: string, status: string) => { + for (var i = 0; i < rows.length; i++) { + updateApproval(Cookies.get("accessToken"), { + id: rows[i].id, + remarks: remarks, + status: status, + }); + } + window.location.reload(); +}; + +const AcceptButton: React.FC = ({ + rows = [], + children, + remarks = "", + finalButton, +}) => { + return ( + + ); +}; + +const RejectButton: React.FC = ({ + rows = [], + children, + remarks = "", + finalButton, +}) => { + return ( + + ); +}; + +interface Props { + checkedRows: any[]; // Define the prop type as an array of any type +} + +const FeedbackForm: React.FC = ({ checkedRows }) => { + const [isClient, setIsClient] = useState(false); + + useEffect(() => { + setIsClient(true); + }, []); + + const [feedbackText, setFeedbackText] = useState(""); + + return ( + <> +

Write Feedback

+