Add Deliverables link to Header and SideNav components, and integrate button in Dashboard for improved navigation
This commit is contained in:
parent
7db02adbd7
commit
83c95afec1
@ -14,6 +14,7 @@ import {
|
||||
Bell,
|
||||
Settings,
|
||||
LogOut,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import { useAppTheme } from "@/components/ThemeProvider";
|
||||
import { ThemeToggle } from "@/components/ThemeToggle";
|
||||
@ -98,6 +99,19 @@ export function Header() {
|
||||
<Calendar className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
<span className="hidden sm:inline">Book Appointment</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="/deliverables"
|
||||
className={`flex items-center gap-1 sm:gap-2 px-2 sm:px-3 md:px-4 py-1.5 sm:py-2 rounded-lg text-xs sm:text-sm font-medium transition-colors ${
|
||||
pathname === "/deliverables"
|
||||
? "bg-linear-to-r from-rose-500 to-pink-600 text-white"
|
||||
: isDark
|
||||
? "text-gray-300 hover:bg-gray-800"
|
||||
: "text-gray-600 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<FileText className="w-4 h-4 sm:w-5 sm:h-5" />
|
||||
<span className="hidden sm:inline">Documentation</span>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
{/* Right Side Actions */}
|
||||
|
||||
@ -12,6 +12,7 @@ import {
|
||||
Menu,
|
||||
X,
|
||||
Heart,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import { useAppTheme } from "@/components/ThemeProvider";
|
||||
import { useAuth } from "@/hooks/useAuth";
|
||||
@ -20,6 +21,7 @@ import { toast } from "sonner";
|
||||
const navItems = [
|
||||
{ label: "Dashboard", icon: LayoutGrid, href: "/admin/dashboard" },
|
||||
{ label: "Book Appointment", icon: Calendar, href: "/admin/booking" },
|
||||
{ label: "Deliverables", icon: FileText, href: "/deliverables" },
|
||||
];
|
||||
|
||||
export default function SideNav() {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@ -8,6 +9,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Users,
|
||||
UserCheck,
|
||||
@ -18,6 +20,7 @@ import {
|
||||
TrendingUp,
|
||||
ArrowUpRight,
|
||||
ArrowDownRight,
|
||||
FileText,
|
||||
} from "lucide-react";
|
||||
import { useAppTheme } from "@/components/ThemeProvider";
|
||||
import { getAllUsers } from "@/lib/actions/auth";
|
||||
@ -242,6 +245,16 @@ export default function Dashboard() {
|
||||
Here's an overview of your practice today
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Link href="/deliverables">
|
||||
<Button
|
||||
variant="outline"
|
||||
className={`flex items-center gap-2 ${isDark ? "bg-gray-800 border-gray-700 text-gray-100 hover:bg-gray-700" : "bg-white border-gray-200 text-gray-900 hover:bg-gray-50"}`}
|
||||
>
|
||||
<FileText className="w-4 h-4" />
|
||||
<span className="hidden sm:inline">Deliverables</span>
|
||||
</Button>
|
||||
</Link>
|
||||
<Select value={timePeriod} onValueChange={setTimePeriod}>
|
||||
<SelectTrigger className={`w-full sm:w-[200px] cursor-pointer ${isDark ? "bg-gray-800 border-gray-700 text-gray-100" : "bg-white border-gray-200 text-gray-900"}`}>
|
||||
<SelectValue placeholder="Select period" />
|
||||
@ -253,6 +266,7 @@ export default function Dashboard() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
|
||||
311
app/(pages)/deliverables/page.tsx
Normal file
311
app/(pages)/deliverables/page.tsx
Normal file
@ -0,0 +1,311 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft, Heart } from "lucide-react";
|
||||
import React from "react";
|
||||
import ReactMarkdown, { Components } from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
|
||||
const ReadmePage = () => {
|
||||
const readmeContent = `
|
||||
## Attune Heart Therapy
|
||||
|
||||
Welcome to your Attune Heart Therapy platform! This documentation provides everything you need to understand and navigate the complete system, including the landing page, booking system, user/client dashboard, and admin dashboard.
|
||||
|
||||
---
|
||||
|
||||
## 📂 What's Included
|
||||
|
||||
Your Attune Heart Therapy platform includes a comprehensive system for managing therapy appointments and client interactions:
|
||||
|
||||
| Section | Description |
|
||||
| --------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| Landing Page | Public-facing homepage with navigation, services overview, and booking access |
|
||||
| Booking System | User-friendly appointment booking flow where clients can request therapy sessions |
|
||||
| User Dashboard | Client portal to view appointments, manage profile, and track booking status |
|
||||
| Admin Dashboard | Administrative interface to manage appointments, view statistics, and schedule sessions |
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Admin Dashboard Access
|
||||
|
||||
### Step 1: Navigate to Login
|
||||
|
||||
1. Go to your website's homepage
|
||||
2. Click on the **"Admin Panel"** link in the footer (under Quick Links)
|
||||
3. Or navigate directly to: \`https://attunehearttherapy.com/login\`
|
||||
|
||||
### Step 2: Login Credentials
|
||||
|
||||
**Email Address:** \`Hello@AttuneHeartTherapy.com\`
|
||||
|
||||
**Password:** \`G&n2S;ffTc8f\`
|
||||
|
||||
### Step 3: Access Dashboard
|
||||
|
||||
1. Enter your admin email address
|
||||
2. Enter your password
|
||||
3. Click **"Sign In"**
|
||||
4. You will be automatically redirected to the Admin Dashboard
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Quick Access Links
|
||||
|
||||
[Visit Attune Heart Therapy](https://attunehearttherapy.com/) - Official website
|
||||
|
||||
[Access Admin Dashboard](https://attunehearttherapy.com/login) - Login to manage your practice
|
||||
|
||||
[Book an Appointment](https://attunehearttherapy.com/book-now) - Client booking page
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support & Contact
|
||||
|
||||
For technical assistance, questions, or issues:
|
||||
|
||||
**Email:** [info@BlackBusinessLabs.com](mailto:info@BlackBusinessLabs.com)
|
||||
|
||||
**Phone:** [(646) 895-4856](tel:+16468954856) - *CEO Tray Bailey's direct mobile*
|
||||
|
||||
---
|
||||
|
||||
*For questions or additional support, please contact Black Business Labs at the information provided above.*`;
|
||||
|
||||
const components: Components = {
|
||||
h1: ({ node, ...props }) => (
|
||||
<h1
|
||||
style={{
|
||||
fontSize: "2.2em",
|
||||
fontWeight: "600",
|
||||
marginTop: "1.2em",
|
||||
marginBottom: "0.6em",
|
||||
borderBottom: "1px solid #eaeaea",
|
||||
paddingBottom: "0.3em",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
h2: ({ node, children, ...props }) => {
|
||||
// Extract text content from children
|
||||
const extractText = (child: any): string => {
|
||||
if (typeof child === 'string') return child;
|
||||
if (typeof child === 'number') return String(child);
|
||||
if (React.isValidElement(child)) {
|
||||
const childProps = child.props as any;
|
||||
if (childProps?.children) {
|
||||
return React.Children.toArray(childProps.children).map(extractText).join('');
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
const textContent = React.Children.toArray(children).map(extractText).join('');
|
||||
|
||||
// Check if this is the title heading
|
||||
if (textContent.includes('Attune Heart Therapy - System Overview')) {
|
||||
return (
|
||||
<h2
|
||||
style={{
|
||||
fontSize: "1.8em",
|
||||
fontWeight: "600",
|
||||
marginTop: "1.2em",
|
||||
marginBottom: "0.6em",
|
||||
borderBottom: "1px solid #eaeaea",
|
||||
paddingBottom: "0.3em",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "0.5em",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<span>{children}</span>
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<h2
|
||||
style={{
|
||||
fontSize: "1.8em",
|
||||
fontWeight: "600",
|
||||
marginTop: "1.2em",
|
||||
marginBottom: "0.6em",
|
||||
borderBottom: "1px solid #eaeaea",
|
||||
paddingBottom: "0.3em",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
},
|
||||
h3: ({ node, ...props }) => (
|
||||
<h3
|
||||
style={{
|
||||
fontSize: "1.5em",
|
||||
fontWeight: "600",
|
||||
marginTop: "1.2em",
|
||||
marginBottom: "0.6em",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
p: ({ node, ...props }) => (
|
||||
<p style={{ marginBottom: "1.2em", lineHeight: "1.8" }} {...props} />
|
||||
),
|
||||
a: ({ node, ...props }) => (
|
||||
<a
|
||||
style={{ color: "#0366d6", textDecoration: "none", fontWeight: "500" }}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
ul: ({ node, ...props }) => (
|
||||
<ul
|
||||
style={{
|
||||
paddingLeft: "1.5em",
|
||||
marginBottom: "1.2em",
|
||||
listStyleType: "disc",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
ol: ({ node, ...props }) => (
|
||||
<ol
|
||||
style={{
|
||||
paddingLeft: "1.5em",
|
||||
marginBottom: "1.2em",
|
||||
listStyleType: "decimal",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
li: ({ node, ...props }) => (
|
||||
<li style={{ marginBottom: "0.4em" }} {...props} />
|
||||
),
|
||||
table: ({ node, ...props }) => (
|
||||
<table
|
||||
style={{
|
||||
width: "100%",
|
||||
borderCollapse: "collapse",
|
||||
marginBottom: "1.2em",
|
||||
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
||||
border: "1px solid #dfe2e5",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
th: ({ node, ...props }) => (
|
||||
<th
|
||||
style={{
|
||||
border: "1px solid #dfe2e5",
|
||||
padding: "0.6em 0.8em",
|
||||
textAlign: "left",
|
||||
backgroundColor: "#f6f8fa",
|
||||
fontWeight: "600",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
td: ({ node, ...props }) => (
|
||||
<td
|
||||
style={{
|
||||
border: "1px solid #dfe2e5",
|
||||
padding: "0.6em 0.8em",
|
||||
textAlign: "left",
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
),
|
||||
pre: ({ node, children, ...props }) => (
|
||||
<pre
|
||||
style={{
|
||||
backgroundColor: "#f6f8fa",
|
||||
padding: "1em",
|
||||
borderRadius: "6px",
|
||||
overflowX: "auto",
|
||||
fontSize: "0.9em",
|
||||
lineHeight: "1.5",
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</pre>
|
||||
),
|
||||
code: (props) => {
|
||||
// Using `props: any` and casting to bypass TypeScript error with `inline` prop.
|
||||
const {
|
||||
node,
|
||||
inline: isInline,
|
||||
className,
|
||||
children,
|
||||
// Destructure known non-HTML props from react-markdown to prevent them from being spread onto the <code> tag
|
||||
index,
|
||||
siblingCount,
|
||||
ordered,
|
||||
checked,
|
||||
style: _style, // if style is passed in props, avoid conflict with style object below
|
||||
...htmlProps // Spread remaining props, assuming they are valid HTML attributes for <code>
|
||||
} = props as any;
|
||||
|
||||
const codeStyleBase = {
|
||||
fontFamily:
|
||||
'SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace',
|
||||
};
|
||||
|
||||
if (isInline) {
|
||||
return (
|
||||
<code
|
||||
className={className}
|
||||
style={{
|
||||
...codeStyleBase,
|
||||
backgroundColor: "rgba(27,31,35,0.07)", // Slightly adjusted for better visibility
|
||||
padding: "0.2em 0.4em",
|
||||
margin: "0 0.1em",
|
||||
fontSize: "85%",
|
||||
borderRadius: "3px",
|
||||
}}
|
||||
{...htmlProps}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
// For block code (inside <pre>)
|
||||
return (
|
||||
<code
|
||||
className={className} // className might contain "language-js" etc.
|
||||
style={{
|
||||
...codeStyleBase,
|
||||
// Most styling for block code is handled by the <pre> wrapper
|
||||
// However, ensure no extra padding/margin if pre handles it
|
||||
padding: 0,
|
||||
backgroundColor: "transparent", // Pre has the background
|
||||
}}
|
||||
{...htmlProps}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
||||
<div className="bg-white p-8 rounded-lg shadow-md">
|
||||
<Button
|
||||
className="bg-gray-100 hover:bg-gray-50 shadow-md text-black"
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
<ArrowLeft className="mr-2" />
|
||||
</Button>
|
||||
<ReactMarkdown components={components} remarkPlugins={[remarkGfm]}>
|
||||
{readmeContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReadmePage;
|
||||
72
app/(pages)/deliverables/swot/page.tsx
Normal file
72
app/(pages)/deliverables/swot/page.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function SwotAnalysisPage() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
|
||||
// This ensures the PDF viewer only renders on the client side
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="mb-6">
|
||||
<Link href="/docs">
|
||||
<Button variant="outline" className="mb-4">
|
||||
← Back to Documentation
|
||||
</Button>
|
||||
</Link>
|
||||
<h1 className="text-3xl font-bold mb-2">WODEY SWOT Analysis</h1>
|
||||
<p className="text-gray-600 mb-6">
|
||||
An in-depth analysis of Strengths, Weaknesses, Opportunities, and
|
||||
Threats for the WODEY platform.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mb-4">
|
||||
<a
|
||||
href="/docs/SWOT-Analysis.pdf"
|
||||
download="WODEY-SWOT-Analysis.pdf"
|
||||
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded shadow-md"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
|
||||
/>
|
||||
</svg>
|
||||
Download PDF
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{isClient ? (
|
||||
<div className="w-full h-[calc(100vh-200px)] rounded-lg overflow-hidden">
|
||||
<Image
|
||||
src="/WODEY-SWOT-Analysis.jpg"
|
||||
alt="SWOT Analysis"
|
||||
width={600}
|
||||
height={600}
|
||||
className="w-full h-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center w-full h-[calc(100vh-200px)] bg-gray-100 rounded-lg">
|
||||
Loading viewer...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -29,12 +29,15 @@
|
||||
"react": "19.2.0",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "19.2.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
|
||||
877
pnpm-lock.yaml
877
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user