Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@
}
}

@keyframes shiny {
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
}

.shiny-text {
background: linear-gradient(
90deg,
#ffd700 0%,
#fff44f 25%,
#ffeb3b 50%,
#fff44f 75%,
#ffd700 100%
);
background-size: 200% auto;
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: shiny 3s linear infinite;
}

@layer base {
:root {
--background: 0 0% 100%;
Expand Down
54 changes: 29 additions & 25 deletions app/hire/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { Loader } from "@/components/ui/loader";
import { useEmployerApplications, useOwnedJobs, useProfile } from "@/hooks/use-employer-api";
import { useMobile } from "@/hooks/use-mobile";
import { cn } from "@/lib/utils";
import { Bell, Plus } from "lucide-react";
import { Bell, Briefcase, Plus } from "lucide-react";
import Link from "next/link";
import { useState, useRef, useEffect } from "react";
import { useAuthContext } from "../authctx";
import { Job } from "@/lib/db/db.types";
import { FadeIn } from "@/components/animata/fade";
import { useModal } from "@/hooks/use-modal";
import { getNotificationPermission, requestNotificationPermission, checkNotificationSupport, shouldShowNotification, sendNotification } from "@/lib/notification-service";
import { HeaderIcon, HeaderText } from "@/components/ui/text";

function DashboardContent() {
const { isMobile } = useMobile();
Expand Down Expand Up @@ -86,31 +87,34 @@ function DashboardContent() {
</div>
</NotifPermsModal>
<div className={cn("flex-1 flex flex-col w-full py-4", isMobile ? "px-1" : "px-4")}>
<h3 className="text-primary tracking-tighter">Welcome back, {profile.data?.name}</h3>
<div className="flex flex-row items-center gap-3 mb-2">
<HeaderIcon icon={Briefcase} />
<HeaderText>Job listings</HeaderText>
</div>
<div className="flex flex-col flex-1">
<div>
<div className="flex gap-4 mb-4">
<span className="text-gray-500 pb-2"><span className="text-primary font-bold">{activeJobs.length}</span> active listing{activeJobs.length !== 1 ? "s" : ""}</span>
<span className="text-gray-500 pb-2"><span className="text-primary font-bold">{inactiveJobs.length}</span> inactive listing{inactiveJobs.length !== 1 ? "s" : ""}</span>
</div>
<JobsContent
applications={applications.employer_applications}
jobs={ownedJobs}
employerId={profile.data?.id || ""}
updateJob={handleUpdateJob}
isLoading={isLoading}
/>
{isMobile && (
<Link href="listings/create">
<button
aria-label="Create new listing"
className="fixed bottom-8 right-2 bg-primary rounded-full p-5 z-10 shadow-xl z-[1000]"
>
<Plus className="h-5 w-5 text-white"/>
</button>
</Link>
)}
</div>
<div>
<div className="flex gap-4 mb-4">
<span className="text-gray-500 pb-2"><span className="text-primary font-bold">{activeJobs.length}</span> active listing{activeJobs.length !== 1 ? "s" : ""}</span>
<span className="text-gray-500 pb-2"><span className="text-primary font-bold">{inactiveJobs.length}</span> inactive listing{inactiveJobs.length !== 1 ? "s" : ""}</span>
</div>
<JobsContent
applications={applications.employer_applications}
jobs={ownedJobs}
employerId={profile.data?.id || ""}
updateJob={handleUpdateJob}
isLoading={isLoading}
/>
{isMobile && (
<Link href="listings/create">
<button
aria-label="Create new listing"
className="fixed bottom-8 right-2 bg-primary rounded-full p-5 z-10 shadow-xl z-[1000]"
>
<Plus className="h-5 w-5 text-white"/>
</button>
</Link>
)}
</div>
</div>
</div>
</ContentLayout>
Expand Down
17 changes: 10 additions & 7 deletions app/hire/forgot-password/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { EmployerUserService } from "@/lib/api/services";
import { cn } from "@/lib/utils";
import { useAppContext } from "@/lib/ctx-app";
import { AnimatePresence, motion } from "framer-motion";
import { HeaderIcon, HeaderText } from "@/components/ui/text";
import { HelpCircle } from "lucide-react";

/**
* Display the layout for the forgot password page.
Expand Down Expand Up @@ -68,9 +70,10 @@ const ForgotPasswordForm = ({}) => {
className="w-full"
>
<Card className="flex flex-col gap-4">
<h2 className="text-3xl tracking-tighter font-bold text-gray-700">
Forgot password
</h2>
<div className="flex flex-row items-center gap-3 mb-2">
<HeaderIcon icon={HelpCircle} />
<HeaderText>Reset password</HeaderText>
</div>
{error && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
<p className="text-sm text-red-600 justify-center">{error}</p>
Expand All @@ -86,7 +89,10 @@ const ForgotPasswordForm = ({}) => {
onChange={(e) => setEmail(e.target.value)}
value={email}
/>
<div className="flex justify-end items-center w-[100%]">
<div className="flex justify-between items-center w-[100%]">
<span className="text-sm text-gray-500">
Remember your password? <a className="text-blue-600 hover:text-blue-800 underline font-medium" href="/login">Log in here.</a>
</span>
<Button
type="submit"
onClick={handle_request}
Expand All @@ -95,9 +101,6 @@ const ForgotPasswordForm = ({}) => {
{isLoading ? "Sending request..." : "Request password reset"}
</Button>
</div>
<span className="text-sm text-gray-500">
Remember your password? <a className="text-blue-600 hover:text-blue-800 underline font-medium" href="/login">Log in here.</a>
</span>
<span className="text-muted-foreground text-sm">
Need help? Contact us at <a href="tel://09276604999" className="text-blue-600 hover:text-blue-800 underline font-medium">0927 660 4999</a> or on <a href="viber://add?number=639276604999" className="text-blue-600 hover:text-blue-800 underline font-medium">Viber</a>.
</span>
Expand Down
8 changes: 6 additions & 2 deletions app/hire/help/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import ContentLayout from "@/components/features/hire/content-layout";
import { Card } from "@/components/ui/card";
import { useAppContext } from "@/lib/ctx-app";
import { cn } from "@/lib/utils";
import { BadgeInfo, Bug, Calendar, Facebook, LucideMessageCircleMore, Mail, Phone } from "lucide-react";
import { BadgeInfo, Bug, Calendar, Facebook, HelpCircle, LucideMessageCircleMore, Mail, Phone } from "lucide-react";
import { AnimatePresence, motion } from "framer-motion";
import Link from "next/link";
import { HeaderIcon, HeaderText } from "@/components/ui/text";

export default function HelpPage() {
const { isMobile } = useAppContext();
Expand All @@ -25,7 +26,10 @@ export default function HelpPage() {
transition={{ duration: 0.3, ease: "easeOut" }}
>

<h3 className="text-primary tracking-tighter">Help center</h3>
<div className="flex flex-row items-center gap-3 mb-2">
<HeaderIcon icon={HelpCircle} />
<HeaderText>Help</HeaderText>
</div>
<div className="grid gap-2 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1">
<Card
className="flex flex-col gap-2"
Expand Down
15 changes: 13 additions & 2 deletions app/hire/listings/details/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client";

import ContentLayout from "@/components/features/hire/content-layout";
import JobHeader from "@/components/features/hire/dashboard/JobHeader";
import JobDetailsPage from "@/components/features/hire/listings/jobDetails";
import { Loader } from "@/components/ui/loader";
import { JobService } from "@/lib/api/services";
Expand Down Expand Up @@ -46,6 +47,10 @@ function JobDetailsPageRouteContent() {
fetchJobData();
}, [jobId]);

const handleJobUpdate = (updates: Partial<Job>) => {
setJobData(prev => prev ? { ...prev, ...updates } : null);
};

if (loading || !jobData) {
return (
<ContentLayout>
Expand All @@ -55,11 +60,17 @@ function JobDetailsPageRouteContent() {
}

return (
<ContentLayout>
<ContentLayout className="!p-0">
<div className="w-full h-full">
<JobDetailsPage
<JobHeader
job={jobData}
onJobUpdate={handleJobUpdate}
/>
<div className="flex-1 overflow-auto pt-4 px-2 sm:px-8">
<JobDetailsPage
job={jobData}
/>
</div>
</div>
</ContentLayout>
)
Expand Down
10 changes: 6 additions & 4 deletions app/hire/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import {
} from "@/components/EditForm";

import { Card } from "@/components/ui/card";
import { MailCheck, TriangleAlert } from "lucide-react";
import { MailCheck, TriangleAlert, User } from "lucide-react";
import { Loader } from "@/components/ui/loader";
import { AnimatePresence, motion } from "framer-motion";
import { HeaderIcon, HeaderText } from "@/components/ui/text";

export default function LoginPage() {
return (
Expand Down Expand Up @@ -108,13 +109,14 @@ function LoginContent() {
<div className="flex items-center w-full max-w-2xl h-full">
<Card className="w-full">
{/* Welcome Message */}
<div className="text-3xl tracking-tighter font-bold text-gray-700 mb-4">
Employer Login
<div className="flex flex-row items-center gap-3 mb-2">
<HeaderIcon icon={User} />
<HeaderText>Log in</HeaderText>
</div>
{/* Error Message */}
{error && (
<div className={cn(
"flex gap-2 items-center mb-4 p-3 bg-destructive/10 text-destructive border border-destructive/50 rounded-lg",
"flex gap-2 items-center mb-4 p-3 bg-destructive/10 text-destructive border border-destructive/50 rounded-[0.33em]",
isMobile ? "flex-col items-start" : ""
)}>
<TriangleAlert size={isMobile ? 24 : 20} />
Expand Down
36 changes: 18 additions & 18 deletions app/hire/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import { Loader } from "@/components/ui/loader";
import { cn } from "@/lib/utils";
import { useAppContext } from "@/lib/ctx-app";
import Link from "next/link";
import { TriangleAlert } from "lucide-react";
import { TriangleAlert, User } from "lucide-react";
import { AnimatePresence, motion } from "framer-motion";
import { HeaderIcon, HeaderText } from "@/components/ui/text";

const [EmployerRegisterForm, useEmployerRegisterForm] =
createEditForm<Employer>();
Expand Down Expand Up @@ -189,14 +190,13 @@ const EmployerEditor = ({
className="w-full"
>
<Card>
<div className="mb-4">
<h2 className="text-3xl tracking-tighter font-bold text-gray-700">
Employer Registration
</h2>
<div className="flex flex-row items-center gap-3 mb-2">
<HeaderIcon icon={User} />
<HeaderText>Register</HeaderText>
</div>
{missingFields.length > 0 && (
<div className={cn(
"flex gap-2 items-center mb-4 p-3 bg-destructive/10 text-destructive border border-destructive/50 rounded-lg",
"flex gap-2 items-center mb-4 p-3 bg-destructive/10 text-destructive border border-destructive/50 rounded-[0.33em]",
isMobile ? "flex-col items-start" : ""
)}>
<TriangleAlert size={isMobile ? 24 : 20} />
Expand Down Expand Up @@ -231,7 +231,6 @@ const EmployerEditor = ({
)}
/>
<div>
<ErrorLabel value={formErrors.phone_number} />
<FormInput
label="Phone Number"
value={formData.phone_number ?? ""}
Expand All @@ -240,9 +239,9 @@ const EmployerEditor = ({
missingFields.find((field) => field === "Phone number") ? "border-destructive" : ""
)}
/>
<ErrorLabel value={formErrors.phone_number} />
</div>
<div>
<ErrorLabel value={formErrors.email} />
<FormInput
label="Email"
value={formData.email ?? ""}
Expand All @@ -251,6 +250,7 @@ const EmployerEditor = ({
missingFields.find((field) => field === "Contact email") ? "border-destructive" : ""
)}
/>
<ErrorLabel value={formErrors.email} />
</div>
<div>
<FormInput
Expand All @@ -268,16 +268,6 @@ const EmployerEditor = ({
</div>
<div className="mb-4 flex flex-col space-y-3">
<div>
<FormInput
label="Company Name (optional)"
value={formData.name ?? ""}
setter={fieldSetter("name")}
required={false}
maxLength={100}
/>
</div>
<div>
<ErrorLabel value={formErrors.legal_entity_name} />
<FormInput
label="Legal Entity Name"
value={formData.legal_entity_name ?? ""}
Expand All @@ -288,6 +278,16 @@ const EmployerEditor = ({
missingFields.find((field) => field === "Legal entity name") ? "border-destructive" : ""
)}
/>
<ErrorLabel value={formErrors.legal_entity_name} />
</div>
<div>
<FormInput
label="Company Name (optional)"
value={formData.name ?? ""}
setter={fieldSetter("name")}
required={false}
maxLength={100}
/>
</div>
<FormInput
label="Office City"
Expand Down
6 changes: 4 additions & 2 deletions app/student/forms/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ export default function FormsPage() {

// Profile check only runs if authenticated
useEffect(() => {
if (!isAuthenticated()) return; // Exit if not authenticated
if (!isAuthenticated()) {
return; // Exit if not authenticated
}

if (!profile.data?.department && !profile.isPending)
router.push("/profile");
router.push("/profile/complete-profile");
}, [isAuthenticated, profile.data?.department, profile.isPending, router]);

// Query 1: Check for updates (cheap query - just a timestamp)
Expand Down
Loading