Add public help center with 10 topic pages and improve contractor/residence cards

- Create complete help center at /help with getting-started, residences, tasks,
  contractors, documents, sharing, dashboard, notifications, subscription, and
  account pages
- Add shared help components: sidebar, header, footer, article/section wrappers,
  screenshot placeholders, and tip/note/warning callouts
- Add /help to middleware public paths so it loads without auth
- Add Help Center link to landing page footer
- Link contractor import dialog to /help/sharing
- Make contractor cards fully clickable with consistent height
- Add clear button to contractor specialty filter
- Fix residence card height consistency in grid

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-03-08 00:10:43 -06:00
parent c4d35b3d68
commit f77f913ee8
26 changed files with 1376 additions and 88 deletions
+13 -4
View File
@@ -2,7 +2,8 @@
import { useState, useMemo } from "react"; import { useState, useMemo } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { Upload, Wrench } from "lucide-react"; import Link from "next/link";
import { Upload, Wrench, ExternalLink } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,
@@ -111,14 +112,14 @@ export default function ContractorsPage() {
> >
<Button <Button
variant="outline" variant="outline"
size="sm" className="h-11 px-6 text-base rounded-xl"
onClick={() => { onClick={() => {
setImportError(null); setImportError(null);
setImportOpen(true); setImportOpen(true);
}} }}
> >
<Upload className="size-4 mr-2" /> <Upload className="size-4 mr-2" />
Import .honeydue Import
</Button> </Button>
</PageHeader> </PageHeader>
@@ -174,7 +175,7 @@ export default function ContractorsPage() {
<DialogHeader> <DialogHeader>
<DialogTitle>Import Contractor</DialogTitle> <DialogTitle>Import Contractor</DialogTitle>
<DialogDescription> <DialogDescription>
Import a contractor from a .honeydue file shared with you. Someone shared a contractor with you? Drop the <code className="text-xs bg-muted px-1 py-0.5 rounded">.honeydue</code> file below to add them to your list.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<HoneyDueFileImport onImport={handleContractorImport} /> <HoneyDueFileImport onImport={handleContractorImport} />
@@ -184,6 +185,14 @@ export default function ContractorsPage() {
{createContractor.isPending && ( {createContractor.isPending && (
<p className="text-sm text-muted-foreground">Importing...</p> <p className="text-sm text-muted-foreground">Importing...</p>
)} )}
<Link
href="/help/sharing"
target="_blank"
className="inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-primary transition-colors"
>
Learn how to share and import contractors
<ExternalLink className="size-3" />
</Link>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</div> </div>
+69
View File
@@ -0,0 +1,69 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function AccountHelpPage() {
return (
<HelpArticle
title="Account Settings"
description="Manage your profile, password, theme preferences, and account."
>
<HelpSection id="profile" title="Profile">
<p>
Update your display name and email address from your profile settings.
Your name appears to other household members when collaborating on
shared residences.
</p>
<ScreenshotPlaceholder alt="Profile settings page with name and email fields" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to <strong>Settings</strong> <strong>Profile</strong>.</li>
<li>Edit your name or email.</li>
<li>Tap <strong>Save</strong> to apply changes.</li>
</ol>
</HelpSection>
<HelpSection id="password" title="Changing Your Password">
<p>
Change your password at any time from the profile settings. You&apos;ll
need to enter your current password for verification.
</p>
<HelpCallout type="note">
If you signed up with Apple Sign In, you don&apos;t have a password to
change authentication is handled by Apple.
</HelpCallout>
</HelpSection>
<HelpSection id="theme" title="Theme Preferences">
<p>
honeyDue supports light, dark, and system-matched themes. Choose the
one that&apos;s most comfortable for you.
</p>
<ScreenshotPlaceholder alt="Theme picker showing light, dark, and system options" />
<p>
The theme affects the app&apos;s appearance across all screens. Your
preference syncs across devices when you&apos;re logged in.
</p>
</HelpSection>
<HelpSection id="delete" title="Deleting Your Account">
<p>
You can permanently delete your account and all associated data from
the profile settings.
</p>
<HelpCallout type="warning">
Account deletion is permanent and cannot be undone. All your
residences, tasks, contractors, documents, and subscription data will
be permanently removed. Household members will lose access to any
residences you own.
</HelpCallout>
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to <strong>Settings</strong> <strong>Profile</strong>.</li>
<li>Scroll to the bottom and tap <strong>Delete Account</strong>.</li>
<li>Confirm the deletion by typing your email address.</li>
<li>Your account and all data will be permanently removed.</li>
</ol>
</HelpSection>
</HelpArticle>
);
}
+88
View File
@@ -0,0 +1,88 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function ContractorsHelpPage() {
return (
<HelpArticle
title="Contractors"
description="Build your rolodex of trusted service providers. Store contact details, specialties, and share them with household members."
>
<HelpSection id="adding" title="Adding a Contractor">
<p>
Keep track of every plumber, electrician, landscaper, and handyman
you work with. Each contractor record stores their contact details and
specialty.
</p>
<ScreenshotPlaceholder alt="Add contractor form with name, phone, email, specialty, and notes" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to your <strong>Contractors</strong> list.</li>
<li>Tap <strong>Add Contractor</strong>.</li>
<li>Enter their name, phone number, and optionally email.</li>
<li>Select their specialty (plumbing, electrical, HVAC, etc.).</li>
<li>Add any notes about your experience with them.</li>
</ol>
</HelpSection>
<HelpSection id="specialties" title="Specialties">
<p>
Specialties help you quickly filter and find the right contractor for a
job. honeyDue comes with common home service categories:
</p>
<ul className="list-disc list-inside space-y-1 ml-1">
<li>Plumbing</li>
<li>Electrical</li>
<li>HVAC</li>
<li>Roofing</li>
<li>Landscaping</li>
<li>General Handyman</li>
<li>Pest Control</li>
<li>Cleaning</li>
<li>And more...</li>
</ul>
<ScreenshotPlaceholder alt="Contractor list filtered by specialty" />
</HelpSection>
<HelpSection id="favorites" title="Favorites">
<p>
Mark your go-to contractors as favorites for quick access. Favorited
contractors appear at the top of your list and are highlighted when
linking contractors to tasks.
</p>
<HelpCallout type="tip">
Tap the star icon on any contractor card to toggle their favorite status.
</HelpCallout>
</HelpSection>
<HelpSection id="sharing" title="Sharing & Importing">
<p>
Share your trusted contractors with friends, family, or new household
members using the <strong>.honeydue file format</strong>.
</p>
<ScreenshotPlaceholder alt="Contractor import dialog with file picker and preview" />
<p>
Export your contractors as a .honeydue file and send it to anyone.
They can import it into their own honeyDue account to instantly add
your recommended pros.
</p>
<p>
See the <a href="/help/sharing" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Sharing guide</a> for
detailed export and import instructions.
</p>
</HelpSection>
<HelpSection id="linking" title="Linking to Tasks">
<p>
When creating or editing a task, you can link a contractor to it. This
creates a handy reference so you know who to call for that specific
maintenance item.
</p>
<p>
Contractors linked to tasks also appear in residence reports, giving
you a complete maintenance history with contact information.
</p>
</HelpSection>
</HelpArticle>
);
}
+76
View File
@@ -0,0 +1,76 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function DashboardHelpPage() {
return (
<HelpArticle
title="Dashboard"
description="Get a quick overview of your home maintenance status, upcoming tasks, and recent activity."
>
<HelpSection id="overview" title="Dashboard Overview">
<p>
The dashboard is your home screen after logging in. It gives you a
bird&apos;s-eye view of everything that needs your attention across all
your residences.
</p>
<ScreenshotPlaceholder alt="Dashboard showing task summary cards, activity feed, and quick actions" />
</HelpSection>
<HelpSection id="task-summary" title="Task Summary">
<p>
The task summary section shows counts for each status category:
</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li><strong>Overdue</strong> Tasks that have passed their due date</li>
<li><strong>Due Soon</strong> Tasks coming up in the next 30 days</li>
<li><strong>In Progress</strong> Tasks you&apos;re actively working on</li>
<li><strong>Completed</strong> Tasks finished this month</li>
</ul>
<p>
Tap any summary card to jump directly to those tasks on the kanban
board.
</p>
</HelpSection>
<HelpSection id="needs-attention" title="Needs Attention">
<p>
The &quot;Needs Attention&quot; section highlights items that require
immediate action overdue tasks, expiring warranties, and tasks
without due dates that may have been forgotten.
</p>
<ScreenshotPlaceholder alt="Needs attention section highlighting overdue and urgent items" />
<HelpCallout type="tip">
Check the dashboard daily it takes just a few seconds to see if
anything needs your attention.
</HelpCallout>
</HelpSection>
<HelpSection id="quick-actions" title="Quick Actions">
<p>
Quick action buttons let you jump to common tasks without navigating
through menus:
</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li>Add a new task</li>
<li>Add a new contractor</li>
<li>Upload a document</li>
<li>View your residences</li>
</ul>
</HelpSection>
<HelpSection id="template-suggestions" title="Task Suggestions">
<p>
The dashboard may suggest common maintenance tasks based on the time
of year and your property type. These suggestions come from our
template library and can be added to your residence with one tap.
</p>
<HelpCallout type="note">
Suggestions are just that suggestions. You can dismiss them or add
them to your task list as you see fit.
</HelpCallout>
</HelpSection>
</HelpArticle>
);
}
+71
View File
@@ -0,0 +1,71 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function DocumentsHelpPage() {
return (
<HelpArticle
title="Documents & Warranties"
description="Store warranties, manuals, leases, and other important home documents organized by property."
>
<HelpSection id="adding" title="Adding Documents">
<p>
Upload any document related to your home warranties, appliance
manuals, insurance papers, inspection reports, or receipts.
</p>
<ScreenshotPlaceholder alt="Add document form with title, file upload, and document type" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Open a residence and go to the <strong>Documents</strong> tab.</li>
<li>Tap <strong>Add Document</strong>.</li>
<li>Enter a title and select the document type.</li>
<li>Upload the file (PDF, image, or other supported formats).</li>
<li>Optionally add notes or tags for easier searching.</li>
</ol>
</HelpSection>
<HelpSection id="warranties" title="Warranty Tracking">
<p>
Mark documents as warranties and set expiration dates. honeyDue will
help you track when warranties are about to expire so you can take
action before coverage runs out.
</p>
<ScreenshotPlaceholder alt="Document card showing warranty with expiration date highlighted" />
<HelpCallout type="tip">
Upload a photo of the warranty card or receipt right when you purchase
an appliance. You&apos;ll thank yourself later when you need to make a
claim.
</HelpCallout>
</HelpSection>
<HelpSection id="viewing" title="Viewing Documents">
<p>
All documents for a residence are listed in one place. Tap any
document to view its details, download the file, or see associated
notes.
</p>
<ScreenshotPlaceholder alt="Documents list showing various document types with icons" />
</HelpSection>
<HelpSection id="activating" title="Activating & Deactivating">
<p>
Documents can be toggled between active and inactive states. Use this
to archive old documents without deleting them like an expired
warranty you might still need for reference.
</p>
<HelpCallout type="note">
Inactive documents are hidden from the default view but can be shown
with a filter toggle.
</HelpCallout>
</HelpSection>
<HelpSection id="editing" title="Editing Documents">
<p>
Update document titles, notes, or warranty dates at any time. You can
also replace the uploaded file if you have a better scan or updated
version.
</p>
</HelpSection>
</HelpArticle>
);
}
+88
View File
@@ -0,0 +1,88 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function GettingStartedHelpPage() {
return (
<HelpArticle
title="Getting Started"
description="Create your account, add your first home, and start tracking maintenance in minutes."
>
<HelpSection id="create-account" title="Create Your Account">
<p>
Sign up for honeyDue with your email address or use Sign in with Apple
for a faster setup. Your account is free to create and includes
everything you need to manage one property.
</p>
<ScreenshotPlaceholder alt="Registration page with email and Apple Sign In options" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Visit the <strong>Create Account</strong> page.</li>
<li>Enter your name, email, and a password (or use Apple Sign In).</li>
<li>Verify your email address via the confirmation link.</li>
<li>You&apos;re in! The onboarding flow will guide you through next steps.</li>
</ol>
</HelpSection>
<HelpSection id="add-home" title="Add Your First Home">
<p>
After signing up, you&apos;ll be prompted to add your first residence.
This is the property you want to track maintenance for.
</p>
<ScreenshotPlaceholder alt="Add residence form with name, address, and property type" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Tap <strong>Add Residence</strong>.</li>
<li>Enter a name (e.g., &quot;Our House&quot;) and optionally an address.</li>
<li>Select the property type (house, apartment, condo, etc.).</li>
<li>Your residence is created and ready for tasks.</li>
</ol>
<HelpCallout type="tip">
You can manage multiple properties by upgrading to Pro. The free plan
includes one residence.
</HelpCallout>
</HelpSection>
<HelpSection id="first-task" title="Create Your First Task">
<p>
Tasks are the heart of honeyDue. Add anything from &quot;Change HVAC
filter&quot; to &quot;Schedule annual inspection.&quot;
</p>
<ScreenshotPlaceholder alt="Create task form with title, category, priority, and due date" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Open your residence and tap <strong>Add Task</strong>.</li>
<li>Give it a title and optionally set a category, priority, and due date.</li>
<li>For recurring tasks, set a frequency (weekly, monthly, etc.).</li>
<li>The task appears on your kanban board, organized by status.</li>
</ol>
</HelpSection>
<HelpSection id="invite-household" title="Invite Household Members">
<p>
Home maintenance is a team effort. Invite your partner, roommates, or
family members so everyone can see and manage tasks together.
</p>
<ScreenshotPlaceholder alt="Share code dialog for inviting household members" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Open your residence settings.</li>
<li>Generate a <strong>share code</strong>.</li>
<li>Send the code to your household member.</li>
<li>They enter the code on their device to join your residence.</li>
</ol>
<HelpCallout type="note">
See the <a href="/help/sharing" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Sharing guide</a> for
more details on household sharing and contractor exports.
</HelpCallout>
</HelpSection>
<HelpSection id="explore" title="What to Explore Next">
<p>Now that you&apos;re set up, here are some features to try:</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li><a href="/help/tasks" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Kanban boards</a> Visualize tasks by status (overdue, due soon, in progress, etc.)</li>
<li><a href="/help/contractors" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Contractors</a> Build your rolodex of trusted service providers</li>
<li><a href="/help/documents" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Documents</a> Upload warranties, manuals, and important files</li>
<li><a href="/help/notifications" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Notifications</a> Set up push reminders so you never miss a due date</li>
</ul>
</HelpSection>
</HelpArticle>
);
}
+29
View File
@@ -0,0 +1,29 @@
import type { Metadata } from "next";
import { HelpHeader } from "@/components/help/help-header";
import { HelpSidebar } from "@/components/help/help-sidebar";
import { HelpFooter } from "@/components/help/help-footer";
export const metadata: Metadata = {
title: "Help Center",
description:
"Learn how to use honeyDue to manage your home maintenance, tasks, contractors, and documents.",
};
export default function HelpLayout({ children }: { children: React.ReactNode }) {
return (
<div className="min-h-screen bg-[#FAFAF7] text-[#2D3436] font-sans selection:bg-[#6B8F71]/20 flex flex-col">
<HelpHeader />
<div className="flex-1 max-w-7xl mx-auto w-full px-6 py-10">
<div className="flex gap-10">
<aside className="hidden lg:block w-56 shrink-0">
<div className="sticky top-24">
<HelpSidebar />
</div>
</aside>
<main className="flex-1 min-w-0">{children}</main>
</div>
</div>
<HelpFooter />
</div>
);
}
+62
View File
@@ -0,0 +1,62 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function NotificationsHelpPage() {
return (
<HelpArticle
title="Notifications"
description="Configure push notifications and reminders to stay on top of your home maintenance schedule."
>
<HelpSection id="types" title="Notification Types">
<p>honeyDue sends notifications for important events:</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li><strong>Task due reminders</strong> Before a task&apos;s due date</li>
<li><strong>Overdue alerts</strong> When a task passes its due date</li>
<li><strong>Household activity</strong> When a member completes a task or joins your residence</li>
<li><strong>Warranty expiration</strong> Before a tracked warranty expires</li>
</ul>
<ScreenshotPlaceholder alt="Notification list showing various notification types" />
</HelpSection>
<HelpSection id="preferences" title="Notification Preferences">
<p>
Customize which notifications you receive from your account settings.
You can enable or disable each notification type independently.
</p>
<ScreenshotPlaceholder alt="Notification preferences toggles for each notification type" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to <strong>Settings</strong> <strong>Notifications</strong>.</li>
<li>Toggle each notification type on or off.</li>
<li>Changes take effect immediately.</li>
</ol>
</HelpSection>
<HelpSection id="push" title="Push Notifications">
<p>
Push notifications are delivered directly to your device even when
the app isn&apos;t open. On the mobile app, you&apos;ll be prompted to allow
notifications on first launch.
</p>
<HelpCallout type="tip">
Make sure notifications are enabled in your device&apos;s system settings.
If you&apos;re not receiving notifications, check Settings honeyDue
Notifications on your phone.
</HelpCallout>
</HelpSection>
<HelpSection id="email" title="Email Notifications">
<p>
Important notifications (like overdue tasks) can also be sent to your
registered email address. Email notifications use the same preferences
disable a category and it&apos;s disabled everywhere.
</p>
<HelpCallout type="note">
Check your spam folder if you&apos;re not seeing honeyDue emails. Adding
our email to your contacts helps ensure delivery.
</HelpCallout>
</HelpSection>
</HelpArticle>
);
}
+41
View File
@@ -0,0 +1,41 @@
import Link from "next/link";
import { helpNavItems } from "@/components/help/help-nav-data";
export default function HelpIndexPage() {
return (
<div className="max-w-3xl">
<header className="mb-10">
<h1 className="font-heading text-3xl md:text-4xl font-bold tracking-tight text-[#2D3436]">
Help Center
</h1>
<p className="mt-3 text-lg text-[#8A8F87] leading-relaxed">
Everything you need to know about managing your home with honeyDue.
</p>
</header>
<div className="grid sm:grid-cols-2 gap-4">
{helpNavItems.map((item) => (
<Link
key={item.href}
href={item.href}
className="group rounded-2xl border border-[#E8E3DC] bg-white p-5 transition-all hover:shadow-lg hover:shadow-[#2D3436]/[0.04] hover:-translate-y-0.5"
>
<div className="flex items-start gap-3.5">
<div className="inline-flex items-center justify-center size-10 rounded-xl bg-[#EDF2ED] text-[#6B8F71] shrink-0">
<item.icon className="size-5" />
</div>
<div>
<h2 className="font-heading text-base font-bold text-[#2D3436] group-hover:text-[#6B8F71] transition-colors">
{item.label}
</h2>
<p className="text-sm text-[#8A8F87] mt-0.5 leading-relaxed">
{item.description}
</p>
</div>
</div>
</Link>
))}
</div>
</div>
);
}
+79
View File
@@ -0,0 +1,79 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function ResidencesHelpPage() {
return (
<HelpArticle
title="Residences"
description="Add and manage your properties, invite household members, and keep each home organized."
>
<HelpSection id="adding" title="Adding a Residence">
<p>
A residence represents a property you want to manage. It could be your
house, apartment, rental property, or vacation home.
</p>
<ScreenshotPlaceholder alt="Add residence form with property name, address, and type fields" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to your <strong>Residences</strong> list.</li>
<li>Tap <strong>Add Residence</strong>.</li>
<li>Enter a name and optionally an address.</li>
<li>Select the property type.</li>
</ol>
<HelpCallout type="note">
The free plan includes one residence. Upgrade to Pro for unlimited
properties.
</HelpCallout>
</HelpSection>
<HelpSection id="editing" title="Editing a Residence">
<p>
Update your residence details at any time change the name, address,
or property type from the residence settings.
</p>
<ScreenshotPlaceholder alt="Residence edit form with updated fields" />
</HelpSection>
<HelpSection id="details" title="Residence Details">
<p>
Each residence has its own dashboard showing tasks, documents, and
contractors associated with that property. The kanban board gives you a
visual overview of all task statuses.
</p>
<ScreenshotPlaceholder alt="Residence detail page with kanban board and task summary" />
</HelpSection>
<HelpSection id="sharing" title="Sharing a Residence">
<p>
Invite household members by generating a share code. Anyone with the
code can join and collaborate on that property&apos;s maintenance.
</p>
<p>
See the <a href="/help/sharing" className="text-[#6B8F71] underline underline-offset-2 hover:text-[#5A7A60]">Sharing guide</a> for
step-by-step instructions on inviting members and managing access.
</p>
</HelpSection>
<HelpSection id="reports" title="Residence Reports">
<p>
Generate a PDF report summarizing your residence&apos;s maintenance
history completed tasks, upcoming items, and contractor information.
Great for insurance claims or when selling your home.
</p>
<ScreenshotPlaceholder alt="Download residence report button and PDF preview" />
</HelpSection>
<HelpSection id="deleting" title="Deleting a Residence">
<p>
Only the residence owner can delete a property. Deleting a residence
removes all associated tasks, documents, and contractor links.
</p>
<HelpCallout type="warning">
Deleting a residence is permanent and cannot be undone. Make sure to
download any reports or documents you need before deleting.
</HelpCallout>
</HelpSection>
</HelpArticle>
);
}
+111
View File
@@ -0,0 +1,111 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function SharingHelpPage() {
return (
<HelpArticle
title="Sharing & Household Members"
description="Invite family members to your residences, share contractor contacts, and collaborate on home maintenance together."
>
<HelpSection id="share-codes" title="Invite with Share Codes">
<p>
Every residence has a unique share code that lets you invite household
members. When someone joins with your code, they get full access to that
residence&apos;s tasks, documents, and contractors.
</p>
<ScreenshotPlaceholder alt="Share code dialog showing the invite code and copy button" />
<p>To generate a share code:</p>
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Open the residence you want to share.</li>
<li>Tap the <strong>Share</strong> button or go to residence settings.</li>
<li>Copy the generated code and send it to your household member.</li>
</ol>
<HelpCallout type="tip">
Share codes are single-use for security. Generate a new one for each person you invite.
</HelpCallout>
</HelpSection>
<HelpSection id="joining" title="Joining a Residence">
<p>
If someone shared a code with you, you can join their residence from
the residences screen.
</p>
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to your <strong>Residences</strong> list.</li>
<li>Tap <strong>Join Residence</strong>.</li>
<li>Enter the share code you received.</li>
<li>The residence will appear in your list immediately.</li>
</ol>
<ScreenshotPlaceholder alt="Join residence screen with share code input field" />
</HelpSection>
<HelpSection id="household-members" title="Managing Household Members">
<p>
Once someone joins your residence, they can view and manage tasks,
contractors, and documents for that property. All changes sync
automatically for everyone.
</p>
<ScreenshotPlaceholder alt="Residence members list showing household users" />
<HelpCallout type="note">
The residence owner can remove members at any time from the residence
settings page.
</HelpCallout>
</HelpSection>
<HelpSection id="contractor-sharing" title="Sharing Contractors">
<p>
Contractors can be shared between household members who are part of the
same residence. When you add a contractor to a residence, everyone in
that household can see and use them.
</p>
<p>
You can also export your contractors as a <strong>.honeydue file</strong> to
share with anyone even people who aren&apos;t in your household yet.
</p>
</HelpSection>
<HelpSection id="honeydue-files" title="The .honeydue File Format">
<p>
A <code className="text-sm bg-[#F2EFE9] px-1.5 py-0.5 rounded">.honeydue</code> file
is a portable way to share contractor contacts. It contains names,
specialties, phone numbers, and notes everything someone needs to
import your trusted pros.
</p>
<ScreenshotPlaceholder alt="Export contractors dialog with .honeydue file download" />
<p><strong>To export:</strong></p>
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to your <strong>Contractors</strong> list.</li>
<li>Tap the <strong>Export</strong> button.</li>
<li>Choose which contractors to include.</li>
<li>Download or share the .honeydue file.</li>
</ol>
<p className="mt-4"><strong>To import:</strong></p>
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to your <strong>Contractors</strong> list.</li>
<li>Tap <strong>Import</strong>.</li>
<li>Select a .honeydue file from your device.</li>
<li>Review the contractors and confirm the import.</li>
</ol>
<HelpCallout type="tip">
Importing won&apos;t create duplicates if a contractor with the same name
and phone number already exists, it will be skipped.
</HelpCallout>
</HelpSection>
<HelpSection id="privacy" title="Privacy & Permissions">
<p>
Your data stays private. Sharing a residence only gives access to that
specific property not your other homes or personal account details.
</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li>Household members can only see residences they&apos;ve been invited to.</li>
<li>Removing a member revokes their access immediately.</li>
<li>Exported .honeydue files contain only contractor data, never personal information.</li>
<li>Share codes expire and cannot be reused.</li>
</ul>
</HelpSection>
</HelpArticle>
);
}
+86
View File
@@ -0,0 +1,86 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function SubscriptionHelpPage() {
return (
<HelpArticle
title="Subscription & Plans"
description="Understand the free plan, Pro features, and how to manage your subscription."
>
<HelpSection id="free-plan" title="Free Plan">
<p>
honeyDue is free to use with generous limits for individual homeowners:
</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li><strong>1 residence</strong></li>
<li><strong>Up to 25 tasks</strong> per residence</li>
<li><strong>Up to 10 contractors</strong></li>
<li><strong>Up to 10 documents</strong> per residence</li>
<li>Full kanban board, recurring tasks, and sharing</li>
</ul>
<HelpCallout type="tip">
The free plan is fully functional not a trial. It&apos;s designed for
homeowners managing a single property.
</HelpCallout>
</HelpSection>
<HelpSection id="pro" title="Pro Features">
<p>
Upgrade to Pro for unlimited everything perfect for landlords,
property managers, or households with multiple properties.
</p>
<ScreenshotPlaceholder alt="Pro plan feature comparison showing free vs pro limits" />
<ul className="list-disc list-inside space-y-2 ml-1">
<li><strong>Unlimited residences</strong></li>
<li><strong>Unlimited tasks</strong> per residence</li>
<li><strong>Unlimited contractors</strong></li>
<li><strong>Unlimited documents</strong> per residence</li>
<li><strong>Residence reports</strong> PDF exports of maintenance history</li>
<li><strong>Priority support</strong></li>
</ul>
</HelpSection>
<HelpSection id="upgrading" title="Upgrading to Pro">
<p>
Upgrade from your account settings or when you hit a free plan limit.
Subscriptions are managed through the App Store (iOS) or Google Play
(Android).
</p>
<ScreenshotPlaceholder alt="Subscription settings page with upgrade button" />
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to <strong>Settings</strong> <strong>Subscription</strong>.</li>
<li>Tap <strong>Upgrade to Pro</strong>.</li>
<li>Choose monthly or annual billing.</li>
<li>Confirm the purchase through your app store.</li>
</ol>
</HelpSection>
<HelpSection id="managing" title="Managing Your Subscription">
<p>
View your current plan, billing period, and renewal date from the
subscription settings. You can cancel at any time your Pro features
remain active until the end of the current billing period.
</p>
<HelpCallout type="note">
Subscriptions are managed by Apple or Google to cancel, go to your
device&apos;s subscription settings. Your data is never deleted when you
downgrade.
</HelpCallout>
</HelpSection>
<HelpSection id="restoring" title="Restoring Purchases">
<p>
If you reinstall the app or switch devices, you can restore your
existing subscription:
</p>
<ol className="list-decimal list-inside space-y-2 ml-1">
<li>Go to <strong>Settings</strong> <strong>Subscription</strong>.</li>
<li>Tap <strong>Restore Purchases</strong>.</li>
<li>Your Pro status will be restored if an active subscription is found.</li>
</ol>
</HelpSection>
</HelpArticle>
);
}
+101
View File
@@ -0,0 +1,101 @@
import { HelpArticle } from "@/components/help/help-article";
import { HelpSection } from "@/components/help/help-section";
import { HelpCallout } from "@/components/help/help-callout";
import { ScreenshotPlaceholder } from "@/components/help/screenshot-placeholder";
export default function TasksHelpPage() {
return (
<HelpArticle
title="Tasks"
description="Create, organize, and complete home maintenance tasks with kanban boards, recurring schedules, and priority tracking."
>
<HelpSection id="creating" title="Creating Tasks">
<p>
Tasks represent anything you need to do for your home from replacing
a filter to scheduling an annual inspection.
</p>
<ScreenshotPlaceholder alt="Create task form with title, category, priority, due date, and frequency fields" />
<p>When creating a task, you can set:</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li><strong>Title</strong> A short description (e.g., &quot;Replace HVAC filter&quot;)</li>
<li><strong>Category</strong> Group tasks by area (plumbing, electrical, exterior, etc.)</li>
<li><strong>Priority</strong> Low, medium, or high</li>
<li><strong>Due date</strong> When the task should be completed</li>
<li><strong>Frequency</strong> For recurring tasks (weekly, monthly, quarterly, etc.)</li>
<li><strong>Notes</strong> Additional details or instructions</li>
<li><strong>Contractor</strong> Link a service provider to the task</li>
</ul>
<HelpCallout type="tip">
Use task templates for common maintenance items. Start typing and
suggestions will appear based on popular home maintenance tasks.
</HelpCallout>
</HelpSection>
<HelpSection id="kanban" title="Kanban Board">
<p>
Tasks are organized on a kanban board with columns that automatically
sort based on due dates and completion status:
</p>
<ScreenshotPlaceholder alt="Kanban board showing overdue, due soon, in progress, not started, and completed columns" />
<ul className="list-disc list-inside space-y-2 ml-1">
<li><strong>Overdue</strong> Tasks past their due date</li>
<li><strong>Due Soon</strong> Tasks due within the next 30 days</li>
<li><strong>In Progress</strong> Tasks you&apos;ve started working on</li>
<li><strong>Not Started</strong> Future tasks with no due date pressure</li>
<li><strong>Completed</strong> Finished tasks</li>
</ul>
<p>
Drag and drop tasks between columns to update their status, or tap a
task to edit its details.
</p>
</HelpSection>
<HelpSection id="recurring" title="Recurring Tasks">
<p>
Many home maintenance items happen on a schedule. Set a frequency when
creating a task, and honeyDue will automatically create the next
occurrence after you complete the current one.
</p>
<ScreenshotPlaceholder alt="Task with recurring frequency set to monthly" />
<p>Available frequencies include:</p>
<ul className="list-disc list-inside space-y-2 ml-1">
<li>Weekly</li>
<li>Bi-weekly</li>
<li>Monthly</li>
<li>Quarterly</li>
<li>Semi-annually</li>
<li>Annually</li>
</ul>
<HelpCallout type="note">
When you complete a recurring task, the next due date is automatically
calculated based on the frequency. The task moves back to the
appropriate kanban column.
</HelpCallout>
</HelpSection>
<HelpSection id="completing" title="Completing Tasks">
<p>
Mark a task as complete by tapping the completion button. You can
optionally add a note and attach photos documenting the work.
</p>
<ScreenshotPlaceholder alt="Task completion dialog with notes field and photo upload" />
<p>
Completion records are saved as a history useful for tracking when
maintenance was last performed and what was done.
</p>
</HelpSection>
<HelpSection id="contractors" title="Linking Contractors">
<p>
Associate a contractor with a task to remember who did the work. This
is especially helpful for recurring tasks where you want to call the
same plumber or electrician each time.
</p>
<HelpCallout type="tip">
Linked contractors appear on the task card for quick access to their
contact information.
</HelpCallout>
</HelpSection>
</HelpArticle>
);
}
+5
View File
@@ -441,6 +441,11 @@ export default function HomePage() {
How It Works How It Works
</a> </a>
</li> </li>
<li>
<Link href="/help" className="hover:text-white transition-colors">
Help Center
</Link>
</li>
</ul> </ul>
</div> </div>
+71 -71
View File
@@ -16,80 +16,80 @@ interface ContractorCardProps {
export function ContractorCard({ contractor, onToggleFavorite }: ContractorCardProps) { export function ContractorCard({ contractor, onToggleFavorite }: ContractorCardProps) {
const { basePath } = useDataProvider(); const { basePath } = useDataProvider();
return ( return (
<div className="group rounded-2xl border border-border bg-card p-5 transition-all duration-200 hover:shadow-[var(--shadow-warm-md)] hover:-translate-y-0.5"> <Link href={`${basePath}/contractors/${contractor.id}`} className="block h-full group">
<div className="flex items-start justify-between mb-3"> <div className="h-full rounded-2xl border border-border bg-card p-5 transition-all duration-200 hover:shadow-[var(--shadow-warm-md)] hover:-translate-y-0.5 hover:border-primary/30 flex flex-col">
<div className="flex items-start gap-3 min-w-0 flex-1"> <div className="flex items-start justify-between mb-3">
<div className="size-10 rounded-full bg-primary/10 flex items-center justify-center shrink-0 text-primary font-bold text-sm"> <div className="flex items-start gap-3 min-w-0 flex-1">
{contractor.name[0]?.toUpperCase()} <div className="size-10 rounded-full bg-primary/10 flex items-center justify-center shrink-0 text-primary font-bold text-sm">
{contractor.name[0]?.toUpperCase()}
</div>
<div className="min-w-0">
<span className="font-heading font-bold text-base leading-tight group-hover:text-primary transition-colors line-clamp-1">
{contractor.name}
</span>
{contractor.company && (
<p className="text-sm text-muted-foreground mt-0.5 truncate">{contractor.company}</p>
)}
</div>
</div> </div>
<div className="min-w-0"> <Button
<Link variant="ghost"
href={`${basePath}/contractors/${contractor.id}`} size="icon"
className="font-heading font-bold text-base leading-tight hover:text-primary transition-colors line-clamp-1" className="size-8 shrink-0 -mr-1 -mt-1"
aria-label={contractor.is_favorite ? "Remove from favorites" : "Add to favorites"}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onToggleFavorite(contractor.id);
}}
>
<Star
aria-hidden="true"
className={
contractor.is_favorite
? "size-4 fill-amber-400 text-amber-400"
: "size-4 text-muted-foreground hover:text-amber-400 transition-colors"
}
/>
</Button>
</div>
{contractor.specialties.length > 0 && (
<div className="flex flex-wrap gap-1.5 mb-4">
{contractor.specialties.map((s) => (
<Badge key={s.id} variant="secondary" className="rounded-lg text-xs">
{s.icon && <span className="mr-1">{s.icon}</span>}
{s.name}
</Badge>
))}
</div>
)}
<div className="border-t border-border/60 pt-3 mt-auto flex items-center gap-4">
{contractor.phone && (
<a
href={`tel:${contractor.phone}`}
aria-label={`Call ${contractor.name}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-primary transition-colors"
> >
{contractor.name} <Phone className="size-3" aria-hidden="true" />
</Link> Call
{contractor.company && ( </a>
<p className="text-sm text-muted-foreground mt-0.5 truncate">{contractor.company}</p> )}
)} {contractor.email && (
</div> <a
href={`mailto:${contractor.email}`}
aria-label={`Email ${contractor.name}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-primary transition-colors"
>
<Mail className="size-3" aria-hidden="true" />
Email
</a>
)}
</div> </div>
<Button
variant="ghost"
size="icon"
className="size-8 shrink-0 -mr-1 -mt-1"
aria-label={contractor.is_favorite ? "Remove from favorites" : "Add to favorites"}
onClick={(e) => {
e.preventDefault();
onToggleFavorite(contractor.id);
}}
>
<Star
aria-hidden="true"
className={
contractor.is_favorite
? "size-4 fill-amber-400 text-amber-400"
: "size-4 text-muted-foreground hover:text-amber-400 transition-colors"
}
/>
</Button>
</div> </div>
</Link>
{contractor.specialties.length > 0 && (
<div className="flex flex-wrap gap-1.5 mb-4">
{contractor.specialties.map((s) => (
<Badge key={s.id} variant="secondary" className="rounded-lg text-xs">
{s.icon && <span className="mr-1">{s.icon}</span>}
{s.name}
</Badge>
))}
</div>
)}
<div className="border-t border-border/60 pt-3 mt-3 flex items-center gap-4">
{contractor.phone && (
<a
href={`tel:${contractor.phone}`}
aria-label={`Call ${contractor.name}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-primary transition-colors"
>
<Phone className="size-3" aria-hidden="true" />
Call
</a>
)}
{contractor.email && (
<a
href={`mailto:${contractor.email}`}
aria-label={`Email ${contractor.name}`}
onClick={(e) => e.stopPropagation()}
className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-primary transition-colors"
>
<Mail className="size-3" aria-hidden="true" />
Email
</a>
)}
</div>
</div>
); );
} }
@@ -4,7 +4,7 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { LookupSelect } from "@/components/shared/lookup-select"; import { LookupSelect } from "@/components/shared/lookup-select";
import { useContractorSpecialties } from "@/lib/hooks/use-lookups"; import { useContractorSpecialties } from "@/lib/hooks/use-lookups";
import { Search, Star } from "lucide-react"; import { Search, Star, X } from "lucide-react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
interface ContractorFiltersProps { interface ContractorFiltersProps {
@@ -40,13 +40,26 @@ export function ContractorFilters({
</div> </div>
{/* Specialty filter */} {/* Specialty filter */}
<div className="w-full sm:w-48"> <div className="flex items-center gap-1.5">
<LookupSelect <div className="w-full sm:w-48">
items={specialties} <LookupSelect
value={specialtyId} items={specialties}
onValueChange={onSpecialtyChange} value={specialtyId}
placeholder="All specialties" onValueChange={onSpecialtyChange}
/> placeholder="All specialties"
/>
</div>
{specialtyId != null && (
<Button
variant="ghost"
size="icon"
className="size-8 shrink-0 text-muted-foreground hover:text-foreground"
onClick={() => onSpecialtyChange(undefined)}
>
<X className="size-4" />
<span className="sr-only">Clear specialty filter</span>
</Button>
)}
</div> </div>
{/* Favorites toggle */} {/* Favorites toggle */}
+21
View File
@@ -0,0 +1,21 @@
interface HelpArticleProps {
title: string;
description: string;
children: React.ReactNode;
}
export function HelpArticle({ title, description, children }: HelpArticleProps) {
return (
<article className="max-w-3xl">
<header className="mb-10">
<h1 className="font-heading text-3xl md:text-4xl font-bold tracking-tight text-[#2D3436]">
{title}
</h1>
<p className="mt-3 text-lg text-[#8A8F87] leading-relaxed">
{description}
</p>
</header>
<div className="space-y-12">{children}</div>
</article>
);
}
+41
View File
@@ -0,0 +1,41 @@
import { Lightbulb, Info, AlertTriangle } from "lucide-react";
interface HelpCalloutProps {
type?: "tip" | "note" | "warning";
children: React.ReactNode;
}
const calloutConfig = {
tip: {
icon: Lightbulb,
label: "Tip",
classes: "bg-[#EDF2ED] border-[#6B8F71]/20 text-[#2D3436]",
iconClass: "text-[#6B8F71]",
},
note: {
icon: Info,
label: "Note",
classes: "bg-[#F2EFE9] border-[#C4856A]/20 text-[#2D3436]",
iconClass: "text-[#8A8F87]",
},
warning: {
icon: AlertTriangle,
label: "Warning",
classes: "bg-[#FDF3EE] border-[#C4856A]/30 text-[#2D3436]",
iconClass: "text-[#C4856A]",
},
};
export function HelpCallout({ type = "tip", children }: HelpCalloutProps) {
const config = calloutConfig[type];
const Icon = config.icon;
return (
<div
className={`my-6 flex gap-3 rounded-xl border p-4 ${config.classes}`}
>
<Icon className={`size-5 shrink-0 mt-0.5 ${config.iconClass}`} />
<div className="text-sm leading-relaxed">{children}</div>
</div>
);
}
+39
View File
@@ -0,0 +1,39 @@
import Link from "next/link";
import Image from "next/image";
export function HelpFooter() {
return (
<footer className="bg-[#2D3436] text-[#9A9E97]">
<div className="max-w-7xl mx-auto px-6 py-12">
<div className="flex flex-col sm:flex-row items-center justify-between gap-4">
<Link href="/" className="flex items-center gap-2.5">
<Image
src="/logo.png"
alt="honeyDue"
width={24}
height={24}
className="rounded-md"
/>
<span className="font-heading text-base font-bold text-white">
honeyDue
</span>
</Link>
<div className="flex items-center gap-6 text-sm">
<Link href="/" className="hover:text-white transition-colors">
Home
</Link>
<Link href="/demo" className="hover:text-white transition-colors">
Demo
</Link>
<Link href="/register" className="hover:text-white transition-colors">
Get Started
</Link>
</div>
</div>
<div className="mt-8 pt-6 border-t border-white/5 text-center text-xs">
&copy; {new Date().getFullYear()} honeyDue. All rights reserved.
</div>
</div>
</footer>
);
}
+95
View File
@@ -0,0 +1,95 @@
"use client";
import Link from "next/link";
import Image from "next/image";
import { Menu } from "lucide-react";
import { useState } from "react";
import { HelpSidebar } from "./help-sidebar";
export function HelpHeader() {
const [mobileOpen, setMobileOpen] = useState(false);
return (
<>
<header className="sticky top-0 z-50 bg-[#FAFAF7]/80 backdrop-blur-xl border-b border-[#E8E3DC]/60">
<div className="max-w-7xl mx-auto px-6 flex items-center justify-between h-16">
<div className="flex items-center gap-3">
<button
onClick={() => setMobileOpen(true)}
className="lg:hidden p-2 -ml-2 rounded-lg hover:bg-[#F2EFE9] transition-colors"
aria-label="Open navigation"
>
<Menu className="size-5 text-[#2D3436]" />
</button>
<Link href="/" className="flex items-center gap-2.5">
<Image
src="/logo.png"
alt="honeyDue"
width={32}
height={32}
className="rounded-lg"
/>
<span className="font-heading text-xl font-bold tracking-tight text-[#2D3436]">
honeyDue
</span>
</Link>
<span className="text-[#E8E3DC] mx-1 hidden sm:inline">/</span>
<Link
href="/help"
className="font-heading text-sm font-semibold text-[#6B8F71] hidden sm:inline"
>
Help Center
</Link>
</div>
<div className="hidden md:flex items-center gap-6">
<Link
href="/#features"
className="text-sm font-medium text-[#8A8F87] hover:text-[#2D3436] transition-colors"
>
Features
</Link>
<Link
href="/demo"
className="text-sm font-medium text-[#8A8F87] hover:text-[#2D3436] transition-colors"
>
Demo
</Link>
<Link
href="/login"
className="text-sm font-medium text-[#8A8F87] hover:text-[#2D3436] transition-colors"
>
Sign In
</Link>
</div>
</div>
</header>
{/* Mobile sidebar overlay */}
{mobileOpen && (
<div className="fixed inset-0 z-50 lg:hidden">
<div
className="absolute inset-0 bg-black/30"
onClick={() => setMobileOpen(false)}
/>
<div className="absolute left-0 top-0 bottom-0 w-72 bg-[#FAFAF7] border-r border-[#E8E3DC] p-6 overflow-y-auto">
<div className="flex items-center justify-between mb-6">
<span className="font-heading text-sm font-semibold text-[#6B8F71]">
Help Center
</span>
<button
onClick={() => setMobileOpen(false)}
className="p-1 rounded-lg hover:bg-[#F2EFE9] text-[#8A8F87]"
>
&times;
</button>
</div>
<div onClick={() => setMobileOpen(false)}>
<HelpSidebar />
</div>
</div>
</div>
)}
</>
);
}
+75
View File
@@ -0,0 +1,75 @@
import {
BookOpen,
Home,
CheckSquare,
HardHat,
FileText,
Users,
LayoutDashboard,
Bell,
CreditCard,
UserCircle,
} from "lucide-react";
export const helpNavItems = [
{
label: "Getting Started",
href: "/help/getting-started",
icon: BookOpen,
description: "Set up your account and add your first home.",
},
{
label: "Residences",
href: "/help/residences",
icon: Home,
description: "Manage your properties and household members.",
},
{
label: "Tasks",
href: "/help/tasks",
icon: CheckSquare,
description: "Track maintenance with kanban boards and schedules.",
},
{
label: "Contractors",
href: "/help/contractors",
icon: HardHat,
description: "Organize your service providers and share contacts.",
},
{
label: "Documents",
href: "/help/documents",
icon: FileText,
description: "Store warranties, manuals, and important files.",
},
{
label: "Sharing",
href: "/help/sharing",
icon: Users,
description: "Invite household members and share contractors.",
},
{
label: "Dashboard",
href: "/help/dashboard",
icon: LayoutDashboard,
description: "Understand your dashboard overview and metrics.",
},
{
label: "Notifications",
href: "/help/notifications",
icon: Bell,
description: "Configure push notifications and reminders.",
},
{
label: "Subscription",
href: "/help/subscription",
icon: CreditCard,
description: "Free vs Pro plans and managing your subscription.",
},
{
label: "Account",
href: "/help/account",
icon: UserCircle,
description: "Profile, password, theme, and account settings.",
},
];
+18
View File
@@ -0,0 +1,18 @@
interface HelpSectionProps {
id: string;
title: string;
children: React.ReactNode;
}
export function HelpSection({ id, title, children }: HelpSectionProps) {
return (
<section id={id}>
<h2 className="font-heading text-xl md:text-2xl font-bold tracking-tight text-[#2D3436] mb-4">
{title}
</h2>
<div className="space-y-4 text-[#2D3436]/80 leading-relaxed">
{children}
</div>
</section>
);
}
+33
View File
@@ -0,0 +1,33 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { helpNavItems } from "./help-nav-data";
export function HelpSidebar() {
const pathname = usePathname();
return (
<nav className="flex flex-col gap-1">
{helpNavItems.map((item) => {
const isActive = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
className={`flex items-center gap-2.5 rounded-xl px-3.5 py-2.5 text-sm font-medium transition-all duration-200 ${
isActive
? "bg-[#6B8F71]/10 text-[#6B8F71] shadow-sm"
: "text-[#8A8F87] hover:bg-[#F2EFE9] hover:text-[#2D3436]"
}`}
>
<item.icon
className={`size-4 ${isActive ? "text-[#6B8F71]" : "text-[#8A8F87]"}`}
/>
{item.label}
</Link>
);
})}
</nav>
);
}
@@ -0,0 +1,38 @@
import { ImageIcon } from "lucide-react";
interface ScreenshotPlaceholderProps {
alt: string;
caption?: string;
aspectRatio?: "video" | "square" | "wide" | "tall";
}
const aspectClasses = {
video: "aspect-video",
square: "aspect-square",
wide: "aspect-[21/9]",
tall: "aspect-[9/16]",
};
export function ScreenshotPlaceholder({
alt,
caption,
aspectRatio = "video",
}: ScreenshotPlaceholderProps) {
return (
<figure className="my-6">
<div
className={`${aspectClasses[aspectRatio]} w-full rounded-xl border-2 border-dashed border-[#E8E3DC] bg-[#FAFAF7] flex flex-col items-center justify-center gap-3`}
>
<ImageIcon className="size-8 text-[#8A8F87]/50" />
<span className="text-sm text-[#8A8F87]/70 text-center px-4">
{alt}
</span>
</div>
{caption && (
<figcaption className="mt-2 text-center text-sm text-[#8A8F87]">
{caption}
</figcaption>
)}
</figure>
);
}
+4 -4
View File
@@ -20,11 +20,11 @@ export function ResidenceCard({ data }: ResidenceCardProps) {
.join(", "); .join(", ");
return ( return (
<Link href={`${basePath}/residences/${residence.id}`} className="block group"> <Link href={`${basePath}/residences/${residence.id}`} className="block group h-full">
<div className="rounded-2xl border border-border bg-card overflow-hidden transition-all duration-200 hover:shadow-[var(--shadow-warm-md)] hover:-translate-y-0.5 hover:border-primary/30"> <div className="h-full rounded-2xl border border-border bg-card overflow-hidden transition-all duration-200 hover:shadow-[var(--shadow-warm-md)] hover:-translate-y-0.5 hover:border-primary/30 flex flex-col">
<div className="h-1 bg-gradient-to-r from-primary/60 to-primary/20" /> <div className="h-1 bg-gradient-to-r from-primary/60 to-primary/20" />
<div className="p-5"> <div className="p-5 flex flex-col flex-1">
<div className="flex items-start gap-3 mb-3"> <div className="flex items-start gap-3 mb-3 flex-1">
<div className="size-10 rounded-xl bg-primary/10 flex items-center justify-center shrink-0"> <div className="size-10 rounded-xl bg-primary/10 flex items-center justify-center shrink-0">
<Home className="size-5 text-primary" /> <Home className="size-5 text-primary" />
</div> </div>
+1 -1
View File
@@ -6,7 +6,7 @@ export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl; const { pathname } = request.nextUrl;
// Public paths that don't require auth // Public paths that don't require auth
const publicPaths = ['/', '/login', '/register', '/forgot-password', '/reset-password', '/verify-email', '/demo']; const publicPaths = ['/', '/login', '/register', '/forgot-password', '/reset-password', '/verify-email', '/demo', '/help'];
const isPublicPath = publicPaths.some(p => pathname === p || pathname.startsWith(p + '/')); const isPublicPath = publicPaths.some(p => pathname === p || pathname.startsWith(p + '/'));
const isApiPath = pathname.startsWith('/api/'); const isApiPath = pathname.startsWith('/api/');
const isStaticPath = pathname.startsWith('/_next/') || pathname.startsWith('/favicon') || pathname.match(/\.(png|jpg|jpeg|gif|svg|ico|webp|woff2?|ttf|css|js)$/); const isStaticPath = pathname.startsWith('/_next/') || pathname.startsWith('/favicon') || pathname.match(/\.(png|jpg|jpeg|gif|svg|ico|webp|woff2?|ttf|css|js)$/);