integratedhub/islands/MemberHome.tsx
2026-01-20 09:02:26 -06:00

544 lines
22 KiB
TypeScript

import { useEffect, useState } from "preact/hooks";
import FloatingTileButtons from "./tiles/FloatingTileButtons.tsx";
import type { OnboardingStatus } from "../services/onboardingService.ts";
interface MemberHomeProps {
onboardingStatus?: OnboardingStatus | null;
}
export default function MemberHome({ onboardingStatus }: MemberHomeProps) {
const [spinDirection, setSpinDirection] = useState<"cw" | "ccw">("cw");
const handleLogoMouseEnter = () => {
setSpinDirection(Math.random() < 0.5 ? "cw" : "ccw");
};
useEffect(() => {
const lockRoute = localStorage.getItem("lock-route");
if (lockRoute) {
localStorage.removeItem("lock-route");
globalThis.location.href = lockRoute;
return;
}
const loginPurpose = localStorage.getItem("login-purpose");
if (loginPurpose) {
localStorage.removeItem("login-purpose");
const destination = loginPurpose === "landlord"
? "/landlord/dashboard"
: loginPurpose === "tenant"
? "/tenant/dashboard"
: "";
if (destination) {
globalThis.location.href = destination;
}
}
}, []);
// Sample tile data for FloatingTileButtons
const tileIds = [
{ id: "landlord-hub", label: "Landlord" },
{ id: "tenant-hub", label: "Tenant" },
{ id: "admin-center", label: "Admin" },
{ id: "user-profile", label: "Profile" },
{ id: "dashboard", label: "Dashboard" },
{ id: "financial-calendar", label: "Financial Calendar" },
];
const tenantOnboarded = Boolean(onboardingStatus?.onboarded_tenant);
const landlordStep = onboardingStatus?.onboarding_step ?? 0;
const landlordStarted = Boolean(onboardingStatus?.onboarded_landlord) ||
landlordStep > 0;
const landlordReady = landlordStarted && landlordStep === 0;
const landlordInProgress = landlordStep > 0;
const landlordProgressHref = landlordStep >= 2
? "/landlord/onboarding/property-setup"
: "/landlord/onboarding/profile";
const landlordPrimary = landlordReady
? { label: "Landlord Dashboard", href: "/landlord/dashboard" }
: landlordInProgress
? { label: "Continue Landlord Setup", href: landlordProgressHref }
: { label: "Start Landlord Setup", href: "/onboarding#landlord" };
const tenantPrimary = tenantOnboarded
? { label: "Tenant Dashboard", href: "/tenant/dashboard" }
: { label: "Start Tenant Setup", href: "/onboarding#tenant" };
return (
<div class="min-h-screen bg-black flex items-start justify-center relative overflow-hidden w-full pt-16">
{/* Animated background grid */}
<div class="absolute inset-0 opacity-20">
<div
class="absolute inset-0"
style={{
backgroundImage:
`linear-gradient(rgba(107, 206, 220, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(107, 206, 220, 0.1) 1px, transparent 1px)`,
backgroundSize: "50px 50px",
}}
>
</div>
</div>
{/* Teal colored orbs */}
<div class="absolute w-48 h-48 bg-teal-400 rounded-full filter blur-3xl opacity-20 animate-float-teal-1">
</div>
<div class="absolute w-56 h-56 bg-cyan-400 rounded-full filter blur-3xl opacity-20 animate-float-teal-2">
</div>
<div class="absolute w-52 h-52 bg-emerald-400 rounded-full filter blur-3xl opacity-20 animate-float-teal-3">
</div>
<div class="relative z-10 flex flex-col items-center text-center px-6 w-full max-w-none mt-4">
{/* Large Logo */}
<div class="mb-4 transform hover:scale-105 transition-transform duration-300">
<img
src="/logo.svg"
alt="Cyber Hybrid Hub Logo"
width="150"
height="150"
class={`drop-shadow-2xl filter drop-shadow-[0_0_20px_rgba(107,206,220,0.5)] spin-on-hover ${
spinDirection === "cw"
? "spin-direction-cw"
: "spin-direction-ccw"
}`}
onMouseEnter={handleLogoMouseEnter}
/>
</div>
{/* Main message */}
<div class="max-w-none w-full px-4">
<h1 class="text-3xl md:text-5xl text-transparent bg-clip-text bg-linear-to-r from-teal-400 via-cyan-400 to-emerald-400 mb-6 animate-pulse">
Apps available to you!
</h1>
{/* Real Estate Hub */}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 w-full max-w-6xl mx-auto mb-10">
<div
id="landlord-hub"
class="group relative overflow-hidden bg-linear-to-br from-emerald-900/20 via-teal-900/25 to-cyan-900/20 border border-emerald-500/30 rounded-2xl p-6 backdrop-blur-sm hover:border-emerald-400/60 transition-all duration-300 hover:shadow-[0_0_40px_rgba(52,211,153,0.25)]"
>
<div class="absolute inset-0 bg-emerald-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>
<div class="relative z-10">
<div class="flex items-start justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-emerald-200">
Landlord HQ
</h2>
<p class="text-gray-400 text-sm">
Launch new listings, manage leases, and track revenue.
</p>
</div>
<span
class={`text-xs uppercase tracking-widest px-3 py-1 rounded-full border ${
landlordReady
? "border-emerald-400/40 text-emerald-200 bg-emerald-500/15"
: landlordInProgress
? "border-amber-400/40 text-amber-200 bg-amber-500/10"
: "border-slate-400/40 text-slate-200 bg-slate-500/10"
}`}
>
{landlordReady
? "Onboarded"
: landlordInProgress
? "In Progress"
: "Not Set Up"}
</span>
</div>
<div class="mt-5 grid grid-cols-2 gap-4 text-left text-sm text-gray-300">
<div>
<p class="text-emerald-200 font-semibold">
Ownership Tools
</p>
<p class="text-xs text-gray-400">
Properties, applications, rent schedules.
</p>
</div>
<div>
<p class="text-emerald-200 font-semibold">
Tenant Pipeline
</p>
<p class="text-xs text-gray-400">
Review applications and coordinate repairs.
</p>
</div>
</div>
<div class="mt-6 flex flex-col sm:flex-row gap-3">
<a
href={landlordPrimary.href}
class="inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold text-white bg-emerald-500/80 hover:bg-emerald-400/90 transition"
>
{landlordPrimary.label}
</a>
<a
href={landlordReady
? "/landlord/properties/add"
: "/onboarding#landlord"}
class="inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold text-emerald-100 border border-emerald-400/40 hover:border-emerald-300/80 hover:text-white transition"
>
Add New Property
</a>
</div>
</div>
</div>
<div
id="tenant-hub"
class="group relative overflow-hidden bg-linear-to-br from-cyan-900/20 via-teal-900/15 to-sky-900/20 border border-cyan-500/30 rounded-2xl p-6 backdrop-blur-sm hover:border-cyan-400/60 transition-all duration-300 hover:shadow-[0_0_40px_rgba(56,189,248,0.25)]"
>
<div class="absolute inset-0 bg-cyan-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>
<div class="relative z-10">
<div class="flex items-start justify-between gap-4">
<div>
<h2 class="text-2xl font-bold text-cyan-200">
Tenant Command
</h2>
<p class="text-gray-400 text-sm">
Pay rent, request maintenance, and stay in sync.
</p>
</div>
<span
class={`text-xs uppercase tracking-widest px-3 py-1 rounded-full border ${
tenantOnboarded
? "border-cyan-400/40 text-cyan-200 bg-cyan-500/15"
: "border-slate-400/40 text-slate-200 bg-slate-500/10"
}`}
>
{tenantOnboarded ? "Active" : "Not Set Up"}
</span>
</div>
<div class="mt-5 grid grid-cols-2 gap-4 text-left text-sm text-gray-300">
<div>
<p class="text-cyan-200 font-semibold">Payment Hub</p>
<p class="text-xs text-gray-400">
Auto-pay options and transaction history.
</p>
</div>
<div>
<p class="text-cyan-200 font-semibold">Service Requests</p>
<p class="text-xs text-gray-400">
Track repairs and chat with your landlord.
</p>
</div>
</div>
<div class="mt-6 flex flex-col sm:flex-row gap-3">
<a
href={tenantPrimary.href}
class="inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold text-white bg-cyan-500/80 hover:bg-cyan-400/90 transition"
>
{tenantPrimary.label}
</a>
<a
href="/onboarding#tenant"
class="inline-flex items-center justify-center rounded-full px-5 py-2 text-sm font-semibold text-cyan-100 border border-cyan-400/40 hover:border-cyan-300/80 hover:text-white transition"
>
Apply to Property
</a>
</div>
</div>
</div>
</div>
{/* App tiles container */}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 w-full max-w-6xl mx-auto">
{/* Admin Center Tile */}
<div
id="admin-center"
class="group relative overflow-hidden bg-linear-to-br from-teal-900/20 to-cyan-900/20 border border-teal-500/30 rounded-xl p-6 backdrop-blur-sm hover:border-teal-400/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(107,206,220,0.3)]"
>
<div class="absolute inset-0 bg-teal-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>
<div class="relative z-10">
<div class="w-12 h-12 bg-teal-400/20 rounded-lg flex items-center justify-center mb-4 group-hover:bg-teal-400/30 transition-colors">
<svg
class="w-6 h-6 text-teal-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
>
</path>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
>
</path>
</svg>
</div>
<h3 class="text-xl font-bold text-teal-400 mb-2">
Admin Center
</h3>
<p class="text-gray-400 text-sm">
Manage system settings and user permissions
</p>
</div>
</div>
{/* User Profile Tile */}
<div
id="user-profile"
class="group relative overflow-hidden bg-linear-to-br from-teal-900/20 to-cyan-900/20 border border-teal-500/30 rounded-xl p-6 backdrop-blur-sm hover:border-teal-400/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(107,206,220,0.3)]"
>
<div class="absolute inset-0 bg-teal-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>
<div class="relative z-10">
<div class="w-12 h-12 bg-teal-400/20 rounded-lg flex items-center justify-center mb-4 group-hover:bg-teal-400/30 transition-colors">
<svg
class="w-6 h-6 text-teal-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
>
</path>
</svg>
</div>
<h3 class="text-xl font-bold text-teal-400 mb-2">
User Profile
</h3>
<p class="text-gray-400 text-sm">
View and edit your personal information
</p>
</div>
</div>
{/* Dashboard Tile */}
<button
onClick={() => {
localStorage.setItem("lock-route", "/language/dashboard");
globalThis.location.href = "/language/dashboard";
}}
type="button"
id="dashboard"
class="group relative overflow-hidden bg-gradient-to-br from-teal-900/20 to-cyan-900/20 border border-teal-500/30 rounded-xl p-6 backdrop-blur-sm hover:border-teal-400/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(107,206,220,0.3)]"
>
<div class="absolute inset-0 bg-teal-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>
<div class="relative z-10">
<div class="w-12 h-12 bg-teal-400/20 rounded-lg flex items-center justify-center mb-4 group-hover:bg-teal-400/30 transition-colors">
<svg
class="w-6 h-6 text-teal-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
>
</path>
</svg>
</div>
<h3 class="text-xl font-bold text-teal-400 mb-2">Dashboard</h3>
<p class="text-gray-400 text-sm">
View analytics and system overview
</p>
</div>
</button>
{/* Financial Calendar Tile */}
<a
href={`/calendar/financial-calendar/${new Date().getFullYear()}/${
(new Date().getMonth() + 1).toString().padStart(2, "0")
}?`}
id="financial-calendar"
class="group relative overflow-hidden bg-linear-to-br from-teal-900/20 to-cyan-900/20 border border-teal-500/30 rounded-xl p-6 backdrop-blur-sm hover:border-teal-400/60 transition-all duration-300 hover:shadow-[0_0_30px_rgba(107,206,220,0.3)]"
>
<div class="absolute inset-0 bg-teal-400/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
</div>
<div class="relative z-10">
<div class="w-12 h-12 bg-teal-400/20 rounded-lg flex items-center justify-center mb-4 group-hover:bg-teal-400/30 transition-colors">
<svg
class="w-6 h-6 text-teal-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3M5 11h14M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5A2 2 0 003 7v12a2 2 0 002 2z"
>
</path>
</svg>
</div>
<h3 class="text-xl font-bold text-teal-400 mb-2">
Financial Calendar
</h3>
<p class="text-gray-400 text-sm">
See your incomes and obligations by day
</p>
</div>
</a>
</div>
</div>
{/* Glitch effect container */}
<div class="relative inline-block mb-8">
<div class="absolute inset-0 bg-teal-400 opacity-20 blur-xl animate-pulse">
</div>
<div class="relative bg-linear-to-r from-teal-500/20 to-cyan-500/20 border border-teal-500/50 rounded-lg px-8 py-4 backdrop-blur-sm">
<p class="text-teal-300 text-sm font-mono">
&gt; Loading applications...
</p>
</div>
</div>
{/* Animated scanlines */}
<div class="absolute inset-0 pointer-events-none">
<div
class="absolute inset-0 bg-linear-to-b from-transparent via-teal-500/5 to-transparent animate-pulse"
style={{
animation: "scan 3s linear infinite",
backgroundSize: "100% 3px",
}}
>
</div>
</div>
</div>
{/* Floating Tile Buttons */}
<FloatingTileButtons ids={tileIds} />
<style jsx>
{`
@keyframes scan {
0% { transform: translateY(-100%); }
100% { transform: translateY(100vh); }
}
@keyframes float-teal-1 {
0% {
transform: translate(-300px, -300px) rotate(0deg) scale(0.4);
opacity: 0;
}
25% {
transform: translate(-150px, -150px) rotate(90deg) scale(0.7);
opacity: 0.3;
}
50% {
transform: translate(0px, 0px) rotate(180deg) scale(1.1);
opacity: 0.5;
}
75% {
transform: translate(150px, 150px) rotate(270deg) scale(0.7);
opacity: 0.3;
}
100% {
transform: translate(300px, 300px) rotate(360deg) scale(0.4);
opacity: 0;
}
}
@keyframes float-teal-2 {
0% {
transform: translate(300px, -300px) rotate(0deg) scale(0.4);
opacity: 0;
}
25% {
transform: translate(150px, -150px) rotate(-90deg) scale(0.7);
opacity: 0.3;
}
50% {
transform: translate(0px, 0px) rotate(-180deg) scale(1.1);
opacity: 0.5;
}
75% {
transform: translate(-150px, 150px) rotate(-270deg) scale(0.7);
opacity: 0.3;
}
100% {
transform: translate(-300px, 300px) rotate(-360deg) scale(0.4);
opacity: 0;
}
}
@keyframes float-teal-3 {
0% {
transform: translate(0px, 300px) rotate(0deg) scale(0.4);
opacity: 0;
}
25% {
transform: translate(0px, 150px) rotate(180deg) scale(0.7);
opacity: 0.3;
}
50% {
transform: translate(0px, 0px) rotate(360deg) scale(1.1);
opacity: 0.5;
}
75% {
transform: translate(0px, -150px) rotate(540deg) scale(0.7);
opacity: 0.3;
}
100% {
transform: translate(0px, -300px) rotate(720deg) scale(0.4);
opacity: 0;
}
}
.animate-float-teal-1 {
animation: float-teal-1 12s ease-in-out infinite;
top: 50%;
left: 50%;
margin-top: -24px;
margin-left: -24px;
}
.animate-float-teal-2 {
animation: float-teal-2 15s ease-in-out infinite;
top: 50%;
left: 50%;
margin-top: -28px;
margin-left: -28px;
}
.animate-float-teal-3 {
animation: float-teal-3 18s ease-in-out infinite;
top: 50%;
left: 50%;
margin-top: -26px;
margin-left: -26px;
}
.spin-on-hover {
transition: transform 0.3s ease;
animation-play-state: paused;
}
.spin-direction-cw:hover {
animation: spin-cw 4s linear infinite;
}
.spin-direction-ccw:hover {
animation: spin-ccw 4s linear infinite;
}
@keyframes spin-cw {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes spin-ccw {
from { transform: rotate(0deg); }
to { transform: rotate(-360deg); }
}
`}
</style>
</div>
);
}