diff --git a/src/api/api_types.ts b/src/api/api_types.ts index bdfa719..9d8cda9 100644 --- a/src/api/api_types.ts +++ b/src/api/api_types.ts @@ -6,8 +6,11 @@ import type PocketBase from 'pocketbase' import type { RecordService } from 'pocketbase' export enum Collections { + Applications = "applications", + Chats = "chats", Company = "company", Experience = "experience", + Messages = "messages", Response = "response", Resume = "resume", Users = "users", @@ -38,12 +41,30 @@ export type AuthSystemFields = { // Record types for each collection +export enum ApplicationsStatusOptions { + "created" = "created", + "accepted" = "accepted", + "archived" = "archived", +} +export type ApplicationsRecord = { + resume: RecordIdString + status: ApplicationsStatusOptions + vacancy: RecordIdString +} + +export type ChatsRecord = { + is_group?: boolean + title?: string + users?: RecordIdString[] +} + export type CompanyRecord = { description?: HTMLString email?: string field?: string name?: string phone?: string + user?: RecordIdString vacansies?: RecordIdString[] website?: string } @@ -52,10 +73,17 @@ export type ExperienceRecord = { company_name?: string description?: HTMLString end_date?: IsoDateString - field?: RecordIdString + resume?: RecordIdString start_date?: IsoDateString } +export type MessagesRecord = { + attachments?: string[] + chat?: RecordIdString + text: string + user?: RecordIdString +} + export type ResponseRecord = { resume?: RecordIdString vacancy?: RecordIdString @@ -86,6 +114,7 @@ export type ResumeRecord = { employment_type?: ResumeEmploymentTypeOptions experience?: RecordIdString[] full_name?: string + img?: string phone_number?: string salary?: number skills?: string @@ -97,9 +126,11 @@ export enum UsersRoleOptions { } export type UsersRecord = { avatar?: string + chats?: RecordIdString[] company?: RecordIdString + messages?: RecordIdString[] resume?: RecordIdString - role?: UsersRoleOptions + role: UsersRoleOptions } export enum VacancyExperienceOptions { @@ -129,12 +160,15 @@ export type VacancyRecord = { publication_date?: IsoDateString remote?: boolean skills?: string - title?: string + title: string } // Response types include system fields and match responses from the PocketBase API +export type ApplicationsResponse = Required & BaseSystemFields +export type ChatsResponse = Required & BaseSystemFields export type CompanyResponse = Required & BaseSystemFields export type ExperienceResponse = Required & BaseSystemFields +export type MessagesResponse = Required & BaseSystemFields export type ResponseResponse = Required & BaseSystemFields export type ResumeResponse = Required & BaseSystemFields export type UsersResponse = Required & AuthSystemFields @@ -143,8 +177,11 @@ export type VacancyResponse = Required & BaseS // Types containing all Records and Responses, useful for creating typing helper functions export type CollectionRecords = { + applications: ApplicationsRecord + chats: ChatsRecord company: CompanyRecord experience: ExperienceRecord + messages: MessagesRecord response: ResponseRecord resume: ResumeRecord users: UsersRecord @@ -152,8 +189,11 @@ export type CollectionRecords = { } export type CollectionResponses = { + applications: ApplicationsResponse + chats: ChatsResponse company: CompanyResponse experience: ExperienceResponse + messages: MessagesResponse response: ResponseResponse resume: ResumeResponse users: UsersResponse @@ -164,8 +204,11 @@ export type CollectionResponses = { // https://github.com/pocketbase/js-sdk#specify-typescript-definitions export type TypedPocketBase = PocketBase & { + collection(idOrName: 'applications'): RecordService + collection(idOrName: 'chats'): RecordService collection(idOrName: 'company'): RecordService collection(idOrName: 'experience'): RecordService + collection(idOrName: 'messages'): RecordService collection(idOrName: 'response'): RecordService collection(idOrName: 'resume'): RecordService collection(idOrName: 'users'): RecordService diff --git a/src/api/auth.ts b/src/api/auth.ts index 91abc72..a0dac51 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,6 +1,6 @@ "use server"; import { pocketbase } from "./pocketbase"; -import { UsersRoleOptions } from "./api_types"; +import { AuthSystemFields, UsersRoleOptions } from "./api_types"; import { AuthModel, RecordModel } from "pocketbase"; import { cookies } from "next/headers"; @@ -60,7 +60,15 @@ export const isLoggedIn = async () => { }; export const getUser = async () => { - return pocketbase().authStore.model as Promise; + return pocketbase().authStore.model as Promise; +}; + +export const getUserById = async (userId: string) => { + const { items, totalItems } = await pocketbase() + .collection("users") + .getList(1, 1, { filter: `id = "${userId}"` }); + + return totalItems > 0 ? items[0] : null; }; export const existsUser = async (email: string) => { diff --git a/src/api/resume.ts b/src/api/resume.ts index 6bb86c7..dd37c75 100644 --- a/src/api/resume.ts +++ b/src/api/resume.ts @@ -1,4 +1,7 @@ +import { UserExpandResume } from "@/types/resume"; +import { ResumeRecord } from "./api_types"; import { pocketbase } from "./pocketbase"; +import { error } from "console"; export const getResume = async (id: string) => { const pb = pocketbase(); @@ -19,3 +22,13 @@ export const hasResume = async (userId: string) => { }); return resumes.totalItems > 0; }; + +export const resumeById = async (userId: string) => { + const pb = pocketbase(); + const { expand }: UserExpandResume = await pb + .collection("users") + .getFirstListItem(`id = "${userId}" && resume != ""`, { + expand: "resume", + }); + return expand.resume; +}; diff --git a/src/app/(with-nav)/resume/[id]/page.tsx b/src/app/(with-nav)/resume/[id]/page.tsx index 5b1daa9..ae88623 100644 --- a/src/app/(with-nav)/resume/[id]/page.tsx +++ b/src/app/(with-nav)/resume/[id]/page.tsx @@ -1,3 +1,6 @@ +import { getUser, getUserById } from "@/api/auth"; +import { getResume, hasResume, resumeById } from "@/api/resume"; +import BackButton from "@/components/BackButton"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; @@ -5,33 +8,43 @@ import Image from "next/image"; import React from "react"; import { GoTriangleLeft, GoTriangleRight } from "react-icons/go"; -const data = { - fullName: "John Doe", - age: 25, - workExperience: [ - { - company: "Tech Company", - startDate: "2020-01-15", - endDate: "2022-05-20", - jobDescription: - "Worked as a full-stack developer, building web applications using Remix.", - }, - ], - education: "Bachelor of Computer Science", - placesOfStudy: ["MIT", "Community College"], - skills: ["JavaScript", "React", "Remix"], - expectedSalary: 50000, - typeOfEmployment: "Full-time", - img: "/placeholder-user_2.jpg", - aboutMyself: - "I am a passionate developer with a strong interest in web technologies and software development.", - phone: "1234567890", - email: "example@mail.com", - viewed: 123, - suitable: 123, -}; +const resumePage = async ({ params }: { params: { id: string } }) => { + const user = await getUserById(params.id); + if (!user) { + return ( + + ); + } + + const resumeCheck = await hasResume(params.id); + if (!resumeCheck) { + return ; + } + + const resume = await resumeById(params.id); + + const data = { + fullName: resume.full_name, + workExperience: [ + { + company: "Tech Company", + startDate: "2020-01-15", + endDate: "2022-05-20", + jobDescription: + "Worked as a full-stack developer, building web applications using Remix.", + }, + ], + education: resume.education_levels, + placesOfStudy: [resume.education], + skills: resume.skills, + expectedSalary: resume.salary, + typeOfEmployment: resume.employment_type, + img: "/placeholder-user_2.jpg", + aboutMyself: resume.about, + phone: resume.phone_number, + email: resume.email, + }; -const resumePage = () => { return (
@@ -112,18 +125,6 @@ const resumePage = () => { {"Name: "}

{` ${data.fullName}`}

- {/* */} -
-
-

- {"Age: "} -

-

{data.age}

- {/* */}
@@ -135,9 +136,6 @@ const resumePage = () => { height={400} className="absolute bottom-0 right-5 " /> -
-

{`Suitable vacancies: ${data.suitable} • Viewed: ${data.viewed}`}

-
@@ -209,12 +207,14 @@ const resumePage = () => {
- {data.skills.length > 0 ? ( - data.skills.map((skill, index) => ( -
- {skill} -
- )) + {data.skills && data.skills.length > 0 ? ( + data.skills.split(",").map((item, index) => { + return ( +
+ {item} +
+ ); + }) ) : (

No skills added

)} @@ -239,7 +239,7 @@ const resumePage = () => { - {data.education.length > 0 ? ( + {resume.education && resume.education.length > 0 ? ( @@ -250,7 +250,7 @@ const resumePage = () => {
{data.placesOfStudy.length > 0 ? ( data.placesOfStudy.map((place, index) => ( -
+

{`${index + 1}. ${place}`}

)) @@ -293,10 +293,14 @@ const resumePage = () => {

{data.expectedSalary}

{data.typeOfEmployment}

-
- -

{data.aboutMyself}

-
+ {data.aboutMyself && ( +
+ +
+
+ )} @@ -337,4 +341,14 @@ const resumePage = () => { ); }; +const ErrorPage = ({ text }: { text: string }) => { + return ( + <> +
+

{text}

+
+ + ); +}; + export default resumePage; diff --git a/src/middleware.ts b/src/middleware.ts index 450caa3..6e5a6f5 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,19 +1,32 @@ import { NextResponse } from "next/server"; import { type NextRequest } from "next/server"; -import { isLoggedIn } from "./api/auth"; +import { getUser, isLoggedIn } from "./api/auth"; +import { hasResume } from "./api/resume"; export const config = { - matcher: ["/resume/create", "/resume/create/:path*"], + matcher: ["/resume/:path*"], }; export async function middleware(request: NextRequest) { const auth = await isLoggedIn(); + const from = request.nextUrl.pathname; - if (!auth) { - const from = request.nextUrl.pathname; + if (from === "/resume/create" && !auth) { const url = new URL("/auth/sign-in", request.url); url.searchParams.set("from", from); + return NextResponse.redirect(url); + } else if (!auth) { + NextResponse.next(); + } + + const user = await getUser(); + const resumeCheck = await hasResume(user.id); + + if (from === `/resume/${user.id}` && !resumeCheck) { + const url = new URL("/resume/create", request.url); + url.searchParams.set("from", from); + return NextResponse.redirect(url); } diff --git a/src/types/resume.ts b/src/types/resume.ts index bafa332..fd2c52e 100644 --- a/src/types/resume.ts +++ b/src/types/resume.ts @@ -1,6 +1,12 @@ +import { ResumeRecord } from "@/api/api_types"; + export type Experience = { company: string; jobDescription: string; startDate: string; endDate: string; }; + +export type UserExpandResume = { + expand: { resume: ResumeRecord }; +};