Compare commits
5 Commits
a022e3cca3
...
75746f4cab
| Author | SHA1 | Date | |
|---|---|---|---|
| 75746f4cab | |||
| ec8207fca7 | |||
| 33a4f9dfa9 | |||
| fa39f2af9d | |||
| 52a489759f |
+25
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
|
"playwright": {
|
||||||
|
"type": "local",
|
||||||
|
"command": [
|
||||||
|
"playwright-mcp.cmd",
|
||||||
|
"--browser",
|
||||||
|
"chrome",
|
||||||
|
"--user-data-dir",
|
||||||
|
"C:\\Users\\Caitlin\\AppData\\Local\\Google\\Chrome\\User Data\\Default",
|
||||||
|
],
|
||||||
|
"enabled": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
+68
-9
@@ -1,4 +1,6 @@
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=Audiowide&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Bangers&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@800&display=swap');
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&family=Audiowide&display=swap');
|
||||||
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@@ -92,16 +94,17 @@
|
|||||||
--destructive-foreground: 210 40% 98%;
|
--destructive-foreground: 210 40% 98%;
|
||||||
--border: 214.3 31.8% 91.4%;
|
--border: 214.3 31.8% 91.4%;
|
||||||
--input: 214.3 31.8% 91.4%;
|
--input: 214.3 31.8% 91.4%;
|
||||||
--ring: 0 100% 40%;
|
--ring: 221.2 83.2% 53.3%;
|
||||||
--sidebar: 0 0% 100%;
|
--sidebar: 216 12% 92%;
|
||||||
--sidebar-foreground: 222.2 84% 4.9%;
|
--sidebar-foreground: 0 0% 20%;
|
||||||
--sidebar-primary: 0 100% 40%;
|
--sidebar-primary: 0 91.2% 59.8%;
|
||||||
--sidebar-primary-foreground: 0 0% 100%;
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
--sidebar-accent: 210 40% 96.1%;
|
--sidebar-accent: 218 16% 87%;
|
||||||
--sidebar-accent-foreground: 222.2 84% 4.9%;
|
--sidebar-accent-foreground: 0 0% 20%;
|
||||||
--sidebar-border: 214.3 31.8% 91.4%;
|
--sidebar-border: 220 11% 84%;
|
||||||
--sidebar-ring: 0 100% 40%;
|
--sidebar-ring: 0 76.3% 48%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
|
--theme-primary: hsl(var(--primary));
|
||||||
--event-meeting: 217 91% 55%;
|
--event-meeting: 217 91% 55%;
|
||||||
--event-call: 160 84% 39%;
|
--event-call: 160 84% 39%;
|
||||||
--event-chat: 271 81% 53%;
|
--event-chat: 271 81% 53%;
|
||||||
@@ -393,6 +396,62 @@
|
|||||||
--sidebar-ring: 351 85% 52%;
|
--sidebar-ring: 351 85% 52%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bc-logo {
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 18px;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: hsl(var(--sidebar-foreground));
|
||||||
|
padding: 8px 16px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bc-logo .accent {
|
||||||
|
color: var(--theme-primary, #3b91f7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bc-logo::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
border-top: 1.5px solid var(--theme-primary, #3b91f7);
|
||||||
|
border-left: 1.5px solid var(--theme-primary, #3b91f7);
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
animation: drawTL 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bc-logo::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
border-bottom: 1.5px solid var(--theme-primary, #3b91f7);
|
||||||
|
border-right: 1.5px solid var(--theme-primary, #3b91f7);
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
animation: drawBR 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes drawTL {
|
||||||
|
0%, 5% { width: 0; height: 0; }
|
||||||
|
30% { width: 100%; height: 0; }
|
||||||
|
55% { width: 100%; height: 100%; }
|
||||||
|
85% { width: 100%; height: 100%; }
|
||||||
|
100% { width: 0; height: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes drawBR {
|
||||||
|
0%, 5% { width: 0; height: 0; }
|
||||||
|
30% { width: 100%; height: 0; }
|
||||||
|
55% { width: 100%; height: 100%; }
|
||||||
|
85% { width: 100%; height: 100%; }
|
||||||
|
100% { width: 0; height: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
|
|||||||
+6
-12
@@ -1,8 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from "react"
|
import { useState, useEffect, useRef } from "react"
|
||||||
import { useRouter } from "next/navigation"
|
import { useSearchParams } from "next/navigation"
|
||||||
import { COMPANY_NAME } from "@/lib/constants"
|
|
||||||
import { Eye, EyeOff, Loader2 } from "lucide-react"
|
import { Eye, EyeOff, Loader2 } from "lucide-react"
|
||||||
|
|
||||||
const waves = [
|
const waves = [
|
||||||
@@ -28,11 +27,11 @@ export default function LoginPage() {
|
|||||||
const testimonials = [
|
const testimonials = [
|
||||||
{
|
{
|
||||||
text: "This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.",
|
text: "This CRM transformed how we manage our sales pipeline. We've seen a 40% increase in lead conversion.",
|
||||||
author: "Marcus Johnson, Sales Lead at Coast IT",
|
author: "Marcus Johnson, Sales Lead at Black Cipher",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "When you're not sure, flip a coin, because when the coin is in the air, you realize which option you're actually hoping for.",
|
text: "When you're not sure, flip a coin, because when the coin is in the air, you realize which option you're actually hoping for.",
|
||||||
author: "Dillen van der Merwe, Madman of Coast IT",
|
author: "Dillen van der Merwe, Madman of Black Cipher",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -230,16 +229,11 @@ export default function LoginPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-[#0a0a0f]">
|
<div className="flex min-h-screen bg-[#0a0a0f]">
|
||||||
<div className="left-panel flex flex-1 flex-col p-12">
|
<div className="left-panel flex flex-1 flex-col p-12">
|
||||||
<div className="relative z-10">
|
|
||||||
<img
|
|
||||||
src="/logo/CompanyLogo.png"
|
|
||||||
alt={COMPANY_NAME}
|
|
||||||
className="h-44 w-auto object-contain"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative z-10 flex-1 flex items-center justify-center">
|
<div className="relative z-10 flex-1 flex items-center justify-center">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
|
<div className="bc-logo mb-8" style={{"--theme-primary":"#1BB0CE"}}>
|
||||||
|
Black <span className="accent">Cipher</span>
|
||||||
|
</div>
|
||||||
<h1 className="text-[34px] font-extrabold text-[#e8e8ef] leading-tight tracking-[-0.6px]">
|
<h1 className="text-[34px] font-extrabold text-[#e8e8ef] leading-tight tracking-[-0.6px]">
|
||||||
Your Agency's{" "}
|
Your Agency's{" "}
|
||||||
<span className="growth-word">Growth</span>{" "}
|
<span className="growth-word">Growth</span>{" "}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { usePathname } from "next/navigation"
|
|||||||
import { motion, AnimatePresence } from "framer-motion"
|
import { motion, AnimatePresence } from "framer-motion"
|
||||||
import { Sidebar } from "./sidebar"
|
import { Sidebar } from "./sidebar"
|
||||||
import { Topbar } from "./topbar"
|
import { Topbar } from "./topbar"
|
||||||
import { SystemMonitor } from "./system-monitor"
|
|
||||||
|
|
||||||
interface AppShellProps {
|
interface AppShellProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
@@ -80,7 +79,6 @@ export function AppShell({ children }: AppShellProps) {
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SystemMonitor />
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
collapsed={sidebarCollapsed}
|
collapsed={sidebarCollapsed}
|
||||||
onToggle={toggleSidebar}
|
onToggle={toggleSidebar}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Link from "next/link"
|
|||||||
import { usePathname } from "next/navigation"
|
import { usePathname } from "next/navigation"
|
||||||
import { motion, AnimatePresence } from "framer-motion"
|
import { motion, AnimatePresence } from "framer-motion"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { SystemMonitor } from "./system-monitor"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"
|
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
|
||||||
@@ -21,7 +22,7 @@ import {
|
|||||||
Facebook,
|
Facebook,
|
||||||
Calendar,
|
Calendar,
|
||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import { COMPANY_NAME } from "@/lib/constants"
|
|
||||||
import { useUser } from "@/providers/user-provider"
|
import { useUser } from "@/providers/user-provider"
|
||||||
import { useNotifications } from "@/providers/notification-provider"
|
import { useNotifications } from "@/providers/notification-provider"
|
||||||
import { FacebookAccountsDialog } from "@/components/settings/facebook-accounts-dialog"
|
import { FacebookAccountsDialog } from "@/components/settings/facebook-accounts-dialog"
|
||||||
@@ -62,24 +63,11 @@ export function Sidebar({ collapsed, onToggle, mobileOpen, onMobileClose }: Side
|
|||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<div className={cn("flex h-16 items-center border-b border-sidebar-border px-4", collapsed ? "justify-center" : "justify-between")}>
|
<div className={cn("flex h-16 items-center border-b border-sidebar-border px-4", collapsed ? "justify-center" : "justify-between")}>
|
||||||
<Link href="/" className="flex items-center gap-3 overflow-hidden">
|
<Link href="/" className="flex items-center gap-3 overflow-hidden">
|
||||||
<img
|
{collapsed ? (
|
||||||
src="/logo/CompanyLogo.png"
|
<div className="bc-logo" style={{padding: "8px 3px", fontSize: "16px"}}>B<span className="accent">C</span></div>
|
||||||
alt={COMPANY_NAME}
|
) : (
|
||||||
className="h-8 w-8 shrink-0 rounded-lg object-contain"
|
<div className="bc-logo">Black<span className="accent">Cipher</span></div>
|
||||||
/>
|
)}
|
||||||
<AnimatePresence mode="wait">
|
|
||||||
{!collapsed && (
|
|
||||||
<motion.span
|
|
||||||
initial={{ opacity: 0, x: -10 }}
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
exit={{ opacity: 0, x: -10 }}
|
|
||||||
transition={{ duration: 0.15 }}
|
|
||||||
className="text-sm font-semibold whitespace-nowrap"
|
|
||||||
>
|
|
||||||
{COMPANY_NAME}
|
|
||||||
</motion.span>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</Link>
|
</Link>
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<Button
|
<Button
|
||||||
@@ -198,6 +186,8 @@ export function Sidebar({ collapsed, onToggle, mobileOpen, onMobileClose }: Side
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<SystemMonitor collapsed={collapsed} />
|
||||||
|
|
||||||
{/* User info */}
|
{/* User info */}
|
||||||
<div className={cn("border-t border-sidebar-border p-3", collapsed && "flex justify-center")}>
|
<div className={cn("border-t border-sidebar-border p-3", collapsed && "flex justify-center")}>
|
||||||
{collapsed ? (
|
{collapsed ? (
|
||||||
@@ -224,7 +214,7 @@ export function Sidebar({ collapsed, onToggle, mobileOpen, onMobileClose }: Side
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Desktop sidebar */}
|
{/* Desktop sidebar */}
|
||||||
<aside className="hidden lg:fixed lg:inset-y-0 lg:z-30 lg:flex">
|
<aside className="hidden lg:fixed lg:inset-y-0 lg:z-30 lg:flex border-r border-sidebar-border">
|
||||||
{sidebarContent}
|
{sidebarContent}
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
@@ -246,7 +236,7 @@ export function Sidebar({ collapsed, onToggle, mobileOpen, onMobileClose }: Side
|
|||||||
animate={{ x: 0 }}
|
animate={{ x: 0 }}
|
||||||
exit={{ x: -300 }}
|
exit={{ x: -300 }}
|
||||||
transition={{ type: "spring", damping: 30, stiffness: 300 }}
|
transition={{ type: "spring", damping: 30, stiffness: 300 }}
|
||||||
className="fixed inset-y-0 left-0 z-50 lg:hidden"
|
className="fixed inset-y-0 left-0 z-50 lg:hidden border-r border-sidebar-border"
|
||||||
>
|
>
|
||||||
{sidebarContent}
|
{sidebarContent}
|
||||||
</motion.aside>
|
</motion.aside>
|
||||||
|
|||||||
@@ -3,9 +3,13 @@
|
|||||||
import { useState, useEffect } from "react"
|
import { useState, useEffect } from "react"
|
||||||
|
|
||||||
const RAM_LIMIT_MB = 8192
|
const RAM_LIMIT_MB = 8192
|
||||||
const CPU_LIMIT_PCT = 400 // 4 cores * 100%
|
const CPU_LIMIT_PCT = 400
|
||||||
|
|
||||||
export function SystemMonitor() {
|
interface SystemMonitorProps {
|
||||||
|
collapsed: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SystemMonitor({ collapsed }: SystemMonitorProps) {
|
||||||
const [rssMB, setRssMB] = useState(0)
|
const [rssMB, setRssMB] = useState(0)
|
||||||
const [cpuPct, setCpuPct] = useState(0)
|
const [cpuPct, setCpuPct] = useState(0)
|
||||||
|
|
||||||
@@ -30,14 +34,33 @@ export function SystemMonitor() {
|
|||||||
const ramOver = rssMB > RAM_LIMIT_MB
|
const ramOver = rssMB > RAM_LIMIT_MB
|
||||||
const cpuOver = cpuPct > CPU_LIMIT_PCT
|
const cpuOver = cpuPct > CPU_LIMIT_PCT
|
||||||
|
|
||||||
|
if (collapsed) {
|
||||||
|
return (
|
||||||
|
<div className="border-t border-sidebar-border px-3 py-2 flex justify-center gap-1.5">
|
||||||
|
<span className={ramOver ? "text-red-400" : "text-[var(--theme-primary,#3b91f7)]"}>
|
||||||
|
{rssMB}
|
||||||
|
</span>
|
||||||
|
<span className={cpuOver ? "text-red-400" : "text-[var(--theme-primary,#3b91f7)]"}>
|
||||||
|
{cpuPct}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 z-[9999] flex items-center gap-3 px-3 py-1 text-[11px] font-mono bg-black/80 rounded-br-lg select-none">
|
<div className="border-t border-sidebar-border px-3 py-2.5">
|
||||||
<span className={ramOver ? "text-red-400" : "text-green-400"}>
|
<div className="flex items-center justify-between text-[11px] font-mono">
|
||||||
RAM: {rssMB}MB / 8192MB
|
<span className="text-sidebar-foreground/50">RAM</span>
|
||||||
</span>
|
<span className={ramOver ? "text-red-400 font-semibold" : "text-sidebar-foreground/80"}>
|
||||||
<span className={cpuOver ? "text-red-400" : "text-green-400"}>
|
{rssMB}MB / {RAM_LIMIT_MB}MB
|
||||||
CPU: {cpuPct}%
|
</span>
|
||||||
</span>
|
</div>
|
||||||
|
<div className="flex items-center justify-between text-[11px] font-mono mt-1">
|
||||||
|
<span className="text-sidebar-foreground/50">CPU</span>
|
||||||
|
<span className={cpuOver ? "text-red-400 font-semibold" : "text-sidebar-foreground/80"}>
|
||||||
|
{cpuPct}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ export const LEAD_SOURCES = [
|
|||||||
|
|
||||||
export const ITEMS_PER_PAGE = 10
|
export const ITEMS_PER_PAGE = 10
|
||||||
|
|
||||||
export const COMPANY_NAME = "Coast IT"
|
export const COMPANY_NAME = "Black Cipher"
|
||||||
|
|||||||
Reference in New Issue
Block a user