This commit is contained in:
rgrgogu
2026-05-05 23:16:22 +08:00
parent 683b25956a
commit 745c51682e
125 changed files with 18925 additions and 2 deletions
@@ -0,0 +1,200 @@
/***********************************************************************************************************************************************************************
* File Name: NavbarLayout.jsx
* Type of Program: Layout Route
* Description: A Layout router for Landing page.
* Module: Public User
* Author: lash0000
* Date Created: Oct. 10, 2025
***********************************************************************************************************************************************************************
* Change History:
* DATE AUTHOR LOG NUMBER DESCRIPTION
* Oct. 10, 2025 lash0000 001 Initial creation - STAR Phase 1 Project
***********************************************************************************************************************************************************************/
import { Fragment, useEffect, useRef } from 'react';
import { useTheme } from '@/contexts/ThemeContext';
import { NavLink, Link } from 'react-router-dom';
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from "@/components/ui/drawer"
import { Button } from '@/components/ui/button';
import { Toaster } from "@/components/ui/sonner"
import { RippleButton } from '@/components/custom/ripple-button';
import { Moon, Sun, Equal, Home, NotepadText, CircleUser, CroissantIcon, DoorOpen } from 'lucide-react';
import Footer from '../pages/LandingFooter';
function LandingLayout({ children }) {
const { theme, toggleTheme } = useTheme();
const drawerCloseRef = useRef(null);
useEffect(() => {
const handleResize = () => {
const isLargeScreen = window.matchMedia('(min-width: 1024px)').matches;
if (isLargeScreen && drawerCloseRef.current) {
drawerCloseRef.current.click();
}
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
const scrollToId = (id) => {
const el = document.getElementById(id)
if (!el) return
const offset = 150
const top = el.getBoundingClientRect().top + window.scrollY - offset
window.scrollTo({ top, behavior: 'smooth' })
}
return (
<Fragment>
<nav className="fixed top-0 left-0 w-full h-fit flex items-center justify-between xs:px-6 lg:px-16 py-3 font-medium bg-background z-50 border">
<div className="inline-flex items-center space-x-2 text-card-foreground selection:bg-card-foreground selection:text-white dark:selection:bg-card-foreground dark:selection:text-black">
<div className="xs:w-32 lg:w-48">
<img src="/philpro-white.png" alt="" className="block dark:hidden w-auto" />
<img src="/philpro-dark.png" alt="" className="hidden dark:block w-auto" />
</div>
</div>
<div className='flex items-center'>
<div className="xs:hidden lg:block">
<div className="flex text-card-foreground selection:bg-card-foreground selection:text-white dark:selection:bg-card-foreground dark:selection:text-black gap-1.5">
<Button asChild variant="ghost" className="px-3">
<NavLink
to="."
onClick={() => scrollToId("home")}
className={({ isActive }) => `${isActive ? 'underline' : ''}`}
>
Home
</NavLink>
</Button>
<Button asChild variant="ghost" className="px-3">
<NavLink
to="."
onClick={() => scrollToId("about")}
className={({ isActive }) => ` ${isActive ? 'underline' : ''}`}
>
About
</NavLink>
</Button>
<Button asChild variant="ghost" className="px-3">
<NavLink
to="."
onClick={() => scrollToId("contacts")}
className={({ isActive }) => `${isActive ? 'underline' : ''}`}
>
Contacts
</NavLink>
</Button>
<Button asChild variant="ghost" className="px-3">
<NavLink
to="."
onClick={() => scrollToId("contacts")}
className={({ isActive }) => ` ${isActive ? 'underline' : ''}`}
>
FAQs
</NavLink>
</Button>
<Button className="dark:bg-blue-600 dark:text-white dark:hover:opacity-80" asChild>
<NavLink
to="/login"
onClick={() => scrollToId("contacts")}
className={({ isActive }) => `${isActive ? 'underline' : ''}`}
>
Sign in
</NavLink>
</Button>
<Button
variant="outline"
onClick={toggleTheme}
className="text-card-foreground cursor-pointer"
>
{theme === 'light' ? (<> <Moon /> Dark </>) : (<> <Sun /> Light </>)}
</Button>
</div>
</div>
<Drawer>
<Button
variant="outline"
onClick={toggleTheme}
className="mr-3 text-card-foreground cursor-pointer lg:hidden"
>
{theme === 'light' ? (<> <Moon /> Dark </>) : (<> <Sun /> Light </>)}
</Button>
<DrawerTrigger asChild>
<Button size="icon" className=" dark:bg-blue-500 dark:text-white lg:hidden">
<Equal />
</Button>
</DrawerTrigger>
<DrawerContent className="font-geist">
<DrawerHeader>
<DrawerTitle className="text-lg">
Made with {String.fromCodePoint('0x1f499')}
</DrawerTitle>
<DrawerDescription>Explore more.</DrawerDescription>
</DrawerHeader>
<div className="flex flex-col gap-2 p-4 font-medium text-sm">
<Link to="">
<RippleButton rippleColor="#ADD8E6" className="w-full justify-start text-card-foreground" onClick={() => drawerCloseRef.current?.click()}>
<div className='flex items-center space-x-3'>
<Home size={16} />
<label>Home</label>
</div>
</RippleButton>
</Link>
<Link to="">
<RippleButton rippleColor="#ADD8E6" className="w-full justify-start text-card-foreground" onClick={() => drawerCloseRef.current?.click()}>
<div className='flex items-center space-x-3'>
<NotepadText size={16} />
<label>About</label>
</div>
</RippleButton>
</Link>
<Link to="">
<RippleButton rippleColor="#ADD8E6" className="w-full justify-start text-card-foreground" onClick={() => drawerCloseRef.current?.click()}>
<div className='flex items-center space-x-3'>
<CircleUser size={16} />
<label>Contacts</label>
</div>
</RippleButton>
</Link>
<Link to="">
<RippleButton rippleColor="#ADD8E6" className="w-full justify-start text-card-foreground" onClick={() => drawerCloseRef.current?.click()}>
<div className='flex items-center space-x-3'>
<CroissantIcon size={16} />
<label>FAQs</label>
</div>
</RippleButton>
</Link>
<Link to="/login">
<RippleButton className="dark:bg-blue-500 dark:text-white w-full justify-start bg-primary text-primary-foreground" onClick={() => drawerCloseRef.current?.click()}>
<div className='flex items-center space-x-3'>
<DoorOpen size={16} />
<label>Sign in</label>
</div>
</RippleButton>
</Link>
</div>
<DrawerFooter>
<DrawerClose ref={drawerCloseRef} />
</DrawerFooter>
</DrawerContent>
</Drawer>
</div>
</nav>
<main id="main-page" className='pt-28'>
{children}
<Toaster position="top-center" />
</main>
{/* add footer */}
<Footer />
</Fragment>
);
}
export default LandingLayout;
+104
View File
@@ -0,0 +1,104 @@
/***********************************************************************************************************************************************************************
* File Name: footer.jsx
* Type of Program: Frontend Layout
* Description: A footer page for landing page.
* Module: Public
* Author: lash0000
* Date Created: Oct. 10, 2025
***********************************************************************************************************************************************************************
* Change History:
* DATE AUTHOR LOG NUMBER DESCRIPTION
* Oct. 10, 2025 lash0000 001 Initial creation - STAR Phase 1 Project
***********************************************************************************************************************************************************************/
import { Link } from "react-router-dom"
export default function Footer() {
return (
<footer className="bg-background border-t">
<div className="lg:container lg:mx-auto xs:px-6 lg:px-16 py-12">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8 lg:gap-12">
{/* Resources */}
<div>
<h3 className="font-semibold text-primary mb-4 text-2xl tracking-tighter">Resources</h3>
<ul className="space-y-3 text-muted-foreground">
<li>
<Link to="">Philpro Learnings</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
</ul>
</div>
{/* Company */}
<div>
<h3 className="font-semibold text-primary mb-4 text-2xl tracking-tighter">Company</h3>
<ul className="space-y-3 text-muted-foreground">
<li>
<Link to="">Philpro Learnings</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
</ul>
</div>
{/* Socials */}
<div>
<h3 className="font-semibold text-primary mb-4 text-2xl tracking-tighter">Socials</h3>
<ul className="space-y-3 text-muted-foreground">
<li>
<Link to="">Philpro Learnings</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
<li>
<Link to="">Resources 1</Link>
</li>
</ul>
</div>
{/* Logo */}
<div className="flex items-start justify-start lg:justify-end">
<div className="flex items-center gap-2 w-48">
<img
src="/philpro-white.png"
alt="PhilProperties Logo"
className="w-auto dark:hidden object-cover p-1"
/>
<img
src="/philpro-dark.png"
alt="PhilProperties Logo"
className="hidden dark:block w-auto object-cover p-1"
/>
</div>
</div>
</div>
</div>
{/* Copyright */}
<div className="p-8 border-t">
<p className="text-center text-muted-foreground text-sm">
&copy; {new Date().getFullYear()} Philproperties International Corporation. All rights reserved.
</p>
</div>
</footer>
)
}
+234
View File
@@ -0,0 +1,234 @@
/***********************************************************************************************************************************************************************
* File Name: LandingPage.jsx
* Type of Program: Frontend
* Description: A frontend page for landing page.
* Module: Public
* Author: lash0000
* Date Created: Oct. 10, 2025
***********************************************************************************************************************************************************************
* Change History:
* DATE AUTHOR LOG NUMBER DESCRIPTION
* Oct. 10, 2025 lash0000 001 Initial creation - STAR Phase 1 Project
***********************************************************************************************************************************************************************/
import { Fragment, useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "sonner"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button";
import { Tabs, TabsList, TabsTrigger } from "@/components/custom/original-tabs"
import { InteractiveHoverButton } from "@/components/custom/interactive-hover-button"
import { Check, Megaphone, Sparkles, Send, BookOpen, CheckCheck, RefreshCw, Clipboard, GitCompare } from "lucide-react";
import { ContactData } from "@/data/landingPage.data"
function LandingPage() {
const [copied, setCopied] = useState("")
const handleCopy = (text) => {
navigator.clipboard.writeText(text)
setCopied(text)
setTimeout(() => setCopied(""), 3000)
}
return (
<Fragment>
<div id="home" className="lg:container lg:mx-auto lg:max-w-4xl flex xs:items-start md:items-center flex-col gap-8 pb-12">
{/* Hero Section */}
<div className="xs:px-8 xs:mt-0 lg:mt-12 w-full xs:items-start md:items-center justify-center flex flex-col gap-4">
<div className="flex flex-wrap gap-2">
<Badge variant="outline" className="rounded-full">
<BookOpen /> Online Courses
</Badge>
<Badge className="rounded-full">
<Megaphone /> Sales
</Badge>
<Badge variant="outline" className="rounded-full">
<GitCompare /> Alpha Testing
</Badge>
</div>
<h1 className="xs:text-5xl lg:text-6xl font-bold tracking-tighter leading-tight text-primary max-w-2xl md:text-center break-words">
Fueling Growth, Elevate your performance
</h1>
<p className="text-muted-foreground text-xl">Access the application, Achieve the transformation.</p>
<div className="flex gap-4">
<Button size="lg" className="cursor-pointer dark:bg-blue-600 dark:text-white dark:hover:opacity-80">
<Sparkles /> Start Learning
</Button>
<Button size="lg" variant="outline" className="cursor-pointer">
<Send /> Explore
</Button>
</div>
</div>
{/* Banner Section */}
<div className="border xs:w-full lg:w-[1040px]">
<div className="xs:hidden lg:block relative bg-[#9B5DE0] e w-full h-[480px] overflow-hidden">
<div
className="absolute inset-0 bg-cover bg-center"
style={{
backgroundImage:
"url('https://cq5as7pc73.ufs.sh/f/pHNnzIw3VjcggN4lGlIPcojb20WHEOIkxuly6JvSqsQZemMV')",
}}
/>
<div className="absolute top-8 left-1/2 -translate-x-1/2 bg-white rounded-full overflow-hidden">
<Tabs defaultValue="tab-1" className="items-center">
<TabsList className="gap-1 bg-muted">
<TabsTrigger
value="tab-1"
className="group dark:data-[state=active]:text-blue-500 rounded-full data-[state=active]:shadow-none dark:data-[state=active]:border-blue-500 dark:data-[state=active]:border">
<BookOpen
className="-ms-0.5 me-1.5 opacity-60"
size={16}
aria-hidden="true"
/>
To-do
<Badge
className="bg-primary dark:bg-blue-500 text-primary-foreground ms-2 min-w-5 rounded-full transition-opacity group-data-[state=inactive]:opacity-50 dark:group-data-[state=inactive]:bg-white"
variant="secondary"
>3</Badge>
</TabsTrigger>
<TabsTrigger
value="tab-2"
className="group dark:data-[state=active]:text-blue-500 rounded-full data-[state=active]:shadow-none dark:data-[state=active]:border-blue-500 dark:data-[state=active]:border">
<RefreshCw
className="-ms-0.5 me-1.5 opacity-60"
size={16}
aria-hidden="true"
/>
Pending
<Badge
className="bg-primary dark:bg-blue-500 text-primary-foreground ms-2 min-w-5 rounded-full transition-opacity group-data-[state=inactive]:opacity-50 dark:group-data-[state=inactive]:bg-white"
variant="secondary"
>8</Badge>
</TabsTrigger>
<TabsTrigger
value="tab-3"
className="group dark:data-[state=active]:text-blue-500 rounded-full data-[state=active]:shadow-none dark:data-[state=active]:border-blue-500 dark:data-[state=active]:border">
<CheckCheck
className="-ms-0.5 me-1.5 opacity-60"
size={16}
aria-hidden="true"
/>
Completed
<Badge
className="bg-primary dark:bg-blue-500 text-primary-foreground ms-2 min-w-5 -mr-1 rounded-full transition-opacity group-data-[state=inactive]:opacity-50 dark:group-data-[state=inactive]:bg-white"
variant="secondary"
>20</Badge>
</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 bg-muted rounded-tl-lg rounded-tr-lg p-2 pb-0 space-y-4 xs:w-[460px] lg:w-[540px]">
<div className="border-t border-l border-r rounded-tl-lg rounded-tr-lg space-y-2 p-2">
<div className="flex gap-2">
<Badge className="text-sm rounded-full px-4 py-1.5 bg-[#9b5de0]/20 dark:bg-green-500/20 text-[#9b5de0] dark:text-blue-500">
Onboarding
</Badge>
<Badge className="text-sm rounded-full px-4 py-1.5 bg-[#9b5de0]/20 dark:bg-green-500/20 text-[#9b5de0] dark:text-blue-500">
Checklist
</Badge>
</div>
<div className="space-y-2">
<div className="font-bold tracking-tighter text-xl">Visit Philproperties Website</div>
<Link to="https://philproperties.ph" target="_blank" className="text-[#9b5de0]">
https://philproperties.ph/
</Link>
</div>
</div>
</div>
</div>
{/* Call to Action */}
<div id="about" className="xs:p-8 lg:p-12 flex flex-col xs:text-2xl lg:text-4xl font-bold tracking-tighter text-primary gap-8 hover:bg-muted dark:hover:bg-muted/20">
<div>
“The Sales Training and Recruitment (STAR) makes building a winning sales team simple. From hiring the right people to fast-tracking their skills, it combines smart recruitment, clear onboarding, and practical training to create confident, high-performing professionals.”
</div>
<div>
With Philproperties, you’re assured of faster onboarding, stronger sales talent, and a career journey built for growth. Less turnover, more success all in one system.
</div>
</div>
{/* For sales, why choose us? */}
<div className="grid xs:grid-cols-1 lg:grid-cols-3 border-t">
<div className="flex flex-col gap-4 xs:border-b lg:border-r xs:p-8 lg:p-12 hover:bg-muted dark:hover:bg-muted/20">
<h1 className="text-primary font-bold tracking-tighter text-4xl">Hire Smarter</h1>
<p className="text-muted-foreground">Find the right talent faster with a streamlined recruitment process.</p>
</div>
<div className="flex flex-col gap-4 xs:border-b lg:border-r xs:p-8 lg:p-12 hover:bg-muted dark:hover:bg-muted/20">
<h1 className="text-primary font-bold tracking-tighter text-4xl">Train Better</h1>
<p className="text-muted-foreground">Equip every recruit with clear onboarding, mandatory modules, and practical sales training.</p>
</div>
<div className="flex flex-col gap-4 xs:p-8 lg:p-12 xs:border-b hover:bg-muted dark:hover:bg-muted/20">
<h1 className="text-primary font-bold tracking-tighter text-4xl">Grow</h1>
<p className="text-muted-foreground">Build confident professionals, reduce turnover, and boost long-term sales performance.</p>
</div>
</div>
{/* Testimonials */}
<div className="grid xs:grid-cols-1 lg:grid-cols-2">
<div className="flex flex-col gap-4 lg:border-r xs:p-8 lg:p-12 hover:bg-muted dark:hover:bg-muted/20">
<div className="space-y-4">
<h1 className="text-primary font-bold tracking-tighter text-4xl">Frequently Asked Questions</h1>
<p className="text-muted-foreground">Here are useful questions.</p>
</div>
<div className="space-y-4">
<InteractiveHoverButton className="w-full rounded-md xs:text-left md:text-center lg:text-lg">
<p>Is Philproperites real-estate firm?</p>
</InteractiveHoverButton>
<InteractiveHoverButton className="w-full rounded-md xs:text-xs xs:text-left md:text-center lg:text-lg">
What this online course platform offers?
</InteractiveHoverButton>
<InteractiveHoverButton className="w-full rounded-md xs:text-xs xs:text-left md:text-center lg:text-lg">
Premium plan offers?
</InteractiveHoverButton>
</div>
</div>
<div id="contacts" className="flex flex-col gap-4 xs:p-8 lg:p-12 hover:bg-muted dark:hover:bg-muted/20">
<div className="space-y-4">
<h1 className="text-primary font-bold tracking-tighter text-4xl">Contact Us</h1>
<p className="text-muted-foreground">Find the right talent faster with a streamlined recruitment process.</p>
</div>
<div className="space-y-4">
{ContactData.map(({ id, icon: Icon, label, value }) => (
<div
key={id}
className="flex items-center justify-between bg-primary dark:bg-blue-600 rounded-md px-4 py-2 text-sm text-primary-foreground dark:text-white"
>
<div className="flex items-center flex-wrap gap-2">
<Icon className="size-4" />
{label && <span className="opacity-90">{label}</span>}
<span className="font-medium ">{value}</span>
</div>
<Button
variant="ghost"
size="icon"
onClick={() => handleCopy(value) + toast.success("Copied text successfully!")}
>
{copied === value ? (
<Check className="size-4" />
) : (
<Clipboard className="size-4" />
)}
</Button>
</div>
))}
</div>
</div>
</div>
{/* Closing Remarks */}
<div className="xs:p-8 lg:p-12 flex flex-col xs:text-4xl leading-tight border-t font-bold tracking-tighter text-primary gap-8 hover:bg-muted dark:hover:bg-muted/20">
<div>
Ready to supercharge? {<br />} Start by leveraging your limits.
</div>
</div>
</div>
</div>
</Fragment>
)
}
export default LandingPage;
+36
View File
@@ -0,0 +1,36 @@
import { useNavigate } from 'react-router-dom'
import { useAuth } from '@/contexts/AuthContext'
export default function NotFound() {
const { user } = useAuth()
const navigate = useNavigate()
const goHome = () => {
switch (user?.acc_type) {
case 'admin': navigate('/admin'); break
case 'client': navigate('/client'); break
case 'staff': navigate('/staff'); break
default: navigate(-1)
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-muted/40 px-4">
<div className="text-center space-y-6 max-w-md">
<p className="text-8xl font-bold text-muted-foreground/30">404</p>
<div className="space-y-2">
<h1 className="text-2xl font-semibold">Page not found</h1>
<p className="text-muted-foreground">
The page you're looking for doesn't exist or has been moved.
</p>
</div>
<button
onClick={goHome}
className="inline-flex items-center gap-2 px-4 py-2 rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground text-sm font-medium transition-colors"
>
Go to my dashboard
</button>
</div>
</div>
)
}
+36
View File
@@ -0,0 +1,36 @@
import { useNavigate } from 'react-router-dom'
import { useAuth } from '@/contexts/AuthContext'
export default function Unauthorized() {
const { user } = useAuth()
const navigate = useNavigate()
const goHome = () => {
switch (user?.acc_type) {
case 'admin': navigate('/admin'); break
case 'client': navigate('/client'); break
case 'staff': navigate('/staff'); break
default: navigate(-1)
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-muted/40 px-4">
<div className="text-center space-y-6 max-w-md">
<p className="text-8xl font-bold text-muted-foreground/30">403</p>
<div className="space-y-2">
<h1 className="text-2xl font-semibold">Access not authorized</h1>
<p className="text-muted-foreground">
You don't have permission to view this page.
</p>
</div>
<button
onClick={goHome}
className="inline-flex items-center gap-2 px-4 py-2 rounded-md border border-input bg-background hover:bg-accent hover:text-accent-foreground text-sm font-medium transition-colors"
>
Go to my dashboard
</button>
</div>
</div>
)
}