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:
@@ -2,7 +2,8 @@
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
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 {
|
||||
Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,
|
||||
@@ -111,14 +112,14 @@ export default function ContractorsPage() {
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-11 px-6 text-base rounded-xl"
|
||||
onClick={() => {
|
||||
setImportError(null);
|
||||
setImportOpen(true);
|
||||
}}
|
||||
>
|
||||
<Upload className="size-4 mr-2" />
|
||||
Import .honeydue
|
||||
Import
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
@@ -174,7 +175,7 @@ export default function ContractorsPage() {
|
||||
<DialogHeader>
|
||||
<DialogTitle>Import Contractor</DialogTitle>
|
||||
<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>
|
||||
</DialogHeader>
|
||||
<HoneyDueFileImport onImport={handleContractorImport} />
|
||||
@@ -184,6 +185,14 @@ export default function ContractorsPage() {
|
||||
{createContractor.isPending && (
|
||||
<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>
|
||||
</Dialog>
|
||||
</div>
|
||||
|
||||
@@ -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'll
|
||||
need to enter your current password for verification.
|
||||
</p>
|
||||
<HelpCallout type="note">
|
||||
If you signed up with Apple Sign In, you don'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's most comfortable for you.
|
||||
</p>
|
||||
<ScreenshotPlaceholder alt="Theme picker showing light, dark, and system options" />
|
||||
<p>
|
||||
The theme affects the app's appearance across all screens. Your
|
||||
preference syncs across devices when you'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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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'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'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 "Needs Attention" 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>
|
||||
);
|
||||
}
|
||||
@@ -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'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>
|
||||
);
|
||||
}
|
||||
@@ -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'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'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., "Our House") 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 "Change HVAC
|
||||
filter" to "Schedule annual inspection."
|
||||
</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'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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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'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't open. On the mobile app, you'll be prompted to allow
|
||||
notifications on first launch.
|
||||
</p>
|
||||
<HelpCallout type="tip">
|
||||
Make sure notifications are enabled in your device's system settings.
|
||||
If you'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's disabled everywhere.
|
||||
</p>
|
||||
<HelpCallout type="note">
|
||||
Check your spam folder if you're not seeing honeyDue emails. Adding
|
||||
our email to your contacts helps ensure delivery.
|
||||
</HelpCallout>
|
||||
</HelpSection>
|
||||
</HelpArticle>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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'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'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>
|
||||
);
|
||||
}
|
||||
@@ -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'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'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'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'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>
|
||||
);
|
||||
}
|
||||
@@ -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'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'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>
|
||||
);
|
||||
}
|
||||
@@ -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., "Replace HVAC filter")</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'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>
|
||||
);
|
||||
}
|
||||
@@ -441,6 +441,11 @@ export default function HomePage() {
|
||||
How It Works
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/help" className="hover:text-white transition-colors">
|
||||
Help Center
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -16,80 +16,80 @@ interface ContractorCardProps {
|
||||
export function ContractorCard({ contractor, onToggleFavorite }: ContractorCardProps) {
|
||||
const { basePath } = useDataProvider();
|
||||
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">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-start gap-3 min-w-0 flex-1">
|
||||
<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()}
|
||||
<Link href={`${basePath}/contractors/${contractor.id}`} className="block h-full group">
|
||||
<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 justify-between mb-3">
|
||||
<div className="flex items-start gap-3 min-w-0 flex-1">
|
||||
<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 className="min-w-0">
|
||||
<Link
|
||||
href={`${basePath}/contractors/${contractor.id}`}
|
||||
className="font-heading font-bold text-base leading-tight hover:text-primary transition-colors line-clamp-1"
|
||||
<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();
|
||||
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}
|
||||
</Link>
|
||||
{contractor.company && (
|
||||
<p className="text-sm text-muted-foreground mt-0.5 truncate">{contractor.company}</p>
|
||||
)}
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
{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>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { LookupSelect } from "@/components/shared/lookup-select";
|
||||
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";
|
||||
|
||||
interface ContractorFiltersProps {
|
||||
@@ -40,13 +40,26 @@ export function ContractorFilters({
|
||||
</div>
|
||||
|
||||
{/* Specialty filter */}
|
||||
<div className="w-full sm:w-48">
|
||||
<LookupSelect
|
||||
items={specialties}
|
||||
value={specialtyId}
|
||||
onValueChange={onSpecialtyChange}
|
||||
placeholder="All specialties"
|
||||
/>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="w-full sm:w-48">
|
||||
<LookupSelect
|
||||
items={specialties}
|
||||
value={specialtyId}
|
||||
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>
|
||||
|
||||
{/* Favorites toggle */}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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">
|
||||
© {new Date().getFullYear()} honeyDue. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
@@ -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]"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
<div onClick={() => setMobileOpen(false)}>
|
||||
<HelpSidebar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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.",
|
||||
},
|
||||
];
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -20,11 +20,11 @@ export function ResidenceCard({ data }: ResidenceCardProps) {
|
||||
.join(", ");
|
||||
|
||||
return (
|
||||
<Link href={`${basePath}/residences/${residence.id}`} className="block group">
|
||||
<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">
|
||||
<Link href={`${basePath}/residences/${residence.id}`} className="block group h-full">
|
||||
<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="p-5">
|
||||
<div className="flex items-start gap-3 mb-3">
|
||||
<div className="p-5 flex flex-col flex-1">
|
||||
<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">
|
||||
<Home className="size-5 text-primary" />
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ export function middleware(request: NextRequest) {
|
||||
const { pathname } = request.nextUrl;
|
||||
|
||||
// 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 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)$/);
|
||||
|
||||
Reference in New Issue
Block a user