Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 71 additions & 2 deletions src/app/helper/reports/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ import { useCurrentHelper } from "@/hooks/useCurrentHelper"
import { useProjectSelection } from "@/contexts/project-context"
import { Sidebar } from "@/components/layout/sidebar"
import { Header } from "@/components/layout/header"
import {
MONTHLY_PREVIEW_STATS,
PAYOUT_PREVIEW_ROWS,
REPORTS_MONTHLY_PREVIEW_DISCLAIMER,
REPORTS_PAYOUTS_PREVIEW_DISCLAIMER,
} from "@/lib/helper-area-preview-copy"

interface PayoutData {
id: string
Expand Down Expand Up @@ -89,6 +95,12 @@ export default function HelperReportsPage() {
setSelectedRows(selectedRows.length === payouts.length ? [] : payouts.map((payout) => payout.id))
}

const showPayoutPreview =
!!projectId &&
!!helperId &&
!isLoading &&
payouts.length === 0

return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
Expand Down Expand Up @@ -217,6 +229,54 @@ export default function HelperReportsPage() {
Loading payouts...
</td>
</tr>
) : showPayoutPreview ? (
<>
<tr>
<td colSpan={7} className="px-6 py-3 text-sm text-gray-600 border-b border-dashed border-gray-300 bg-muted/40">
{REPORTS_PAYOUTS_PREVIEW_DISCLAIMER}
</td>
</tr>
{PAYOUT_PREVIEW_ROWS.map((payout) => (
<tr key={payout.id} role="presentation" className="bg-gray-50/50">
<td className="px-6 py-4 whitespace-nowrap">
<Checkbox disabled checked={false} />
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div className="flex items-center gap-2 flex-wrap">
<span>{payout.ticketId}</span>
<Badge variant="outline" className="text-[10px] uppercase tracking-wide">
Preview
</Badge>
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{payout.date}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{payout.ticketType}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{payout.amount}</td>
<td className="px-6 py-4 whitespace-nowrap">
<Badge
variant={payout.status === "completed" ? "default" : "secondary"}
className={
payout.status === "completed"
? "bg-green-100 text-green-800 hover:bg-green-100"
: "bg-yellow-100 text-yellow-800 hover:bg-yellow-100"
}
>
{payout.status === "completed" ? "Completed" : "Pending"}
</Badge>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" type="button" disabled>
Open
</Button>
<Button variant="outline" size="sm" type="button" disabled>
Download PDF
</Button>
</div>
</td>
</tr>
))}
</>
) : payouts.length === 0 ? (
<tr>
<td colSpan={7} className="px-6 py-8 text-center text-gray-500">
Expand Down Expand Up @@ -296,8 +356,17 @@ export default function HelperReportsPage() {

{/* Monthly Reports Tab Content */}
{activeTab === "monthly" && (
<div className="bg-white rounded-lg border border-gray-200 p-8 text-center">
<p className="text-gray-500">Monthly reports content would go here</p>
<div className="bg-white rounded-lg border border-dashed border-gray-300 p-8">
<p className="text-sm text-gray-600 mb-6 max-w-2xl">{REPORTS_MONTHLY_PREVIEW_DISCLAIMER}</p>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{MONTHLY_PREVIEW_STATS.map((row) => (
<div key={row.label} className="rounded-lg border border-gray-200 bg-gray-50/80 p-4">
<p className="text-xs font-medium uppercase tracking-wide text-gray-500 mb-1">Preview</p>
<p className="text-sm text-gray-700 mb-2">{row.label}</p>
<p className="text-2xl font-semibold text-gray-900">{row.value}</p>
</div>
))}
</div>
</div>
)}
</main>
Expand Down
115 changes: 114 additions & 1 deletion src/app/helper/support/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Card, CardContent } from "@/components/ui/card"
import { Clock, Users, Plus, Sparkles } from "lucide-react"
import { useMemo } from "react"
import { toast } from "sonner"
import {
SUPPORT_TICKET_PREVIEW_CARDS,
SUPPORT_TICKETS_PREVIEW_DISCLAIMER,
} from "@/lib/helper-area-preview-copy"
import { useProjectSelection } from "@/contexts/project-context"
import { useTicketsWithDetails } from "@/hooks/useTicketsWithDetails"
import { useProjectPaymentSettings } from "@/hooks/useProject"
Expand Down Expand Up @@ -111,6 +116,18 @@ export default function HelperSupportPage() {

const todayFormatted = formatDuration(todayMinutes)

const showSupportTicketPreview =
!!projectId &&
!ticketsLoading &&
availableTickets.length === 0 &&
activeTickets.length === 0

const previewRates = rates ?? {
startPrice: "USD 10.00",
first60: "USD 1.50/min",
after60: "USD 1.00/min",
}

return (
<div className="flex h-screen">
<Sidebar />
Expand Down Expand Up @@ -172,7 +189,103 @@ export default function HelperSupportPage() {
{/* Available Tickets */}
<div className="space-y-4">
<h2 className="text-lg font-semibold text-foreground">Available Tickets</h2>
{formattedAvailable.length === 0 ? (
{formattedAvailable.length === 0 && showSupportTicketPreview ? (
<div className="space-y-4">
<p
role="status"
className="text-sm text-muted-foreground rounded-lg border border-dashed border-border bg-muted/30 px-4 py-3"
>
{SUPPORT_TICKETS_PREVIEW_DISCLAIMER}
</p>
{SUPPORT_TICKET_PREVIEW_CARDS.map((ticket) => (
<Card
key={ticket.id}
className="border border-dashed border-border opacity-95"
role="presentation"
>
<CardContent className="p-6">
<div className="flex items-start gap-4">
<Avatar className="w-10 h-10">
<AvatarFallback className="bg-[#d9dce8] text-foreground">{ticket.avatar}</AvatarFallback>
</Avatar>
<div className="flex-1 space-y-4">
<div>
<div className="flex flex-wrap items-center gap-2 mb-2">
<h3 className="font-medium text-foreground">{ticket.title}</h3>
<Badge variant="outline" className="text-[10px] uppercase tracking-wide">
Preview
</Badge>
<span className="text-sm text-muted-foreground">• {ticket.timestamp}</span>
</div>
<p className="text-sm text-muted-foreground mb-3">{ticket.description}</p>

<div className="space-y-3">
{ticket.topics.length > 0 && (
<div>
<p className="text-sm font-medium text-foreground mb-1">Other topics</p>
<div className="flex gap-2 flex-wrap">
{ticket.topics.map((topic) => (
<Badge key={topic} variant="secondary" className="bg-muted text-muted-foreground">
{topic}
</Badge>
))}
</div>
</div>
)}

<div>
<p className="text-sm font-medium text-foreground mb-1">Type of help</p>
<Badge variant="secondary" className="bg-muted text-muted-foreground capitalize">
{ticket.helpType}
</Badge>
</div>

<div>
<p className="text-sm font-medium text-foreground mb-2">Rates</p>
<div className="grid grid-cols-3 gap-4 text-sm">
<div>
<p className="text-muted-foreground">Start price</p>
<p className="font-medium text-foreground">{previewRates.startPrice}</p>
</div>
<div>
<p className="text-muted-foreground">First 60 min</p>
<p className="font-medium text-foreground">{previewRates.first60}</p>
</div>
<div>
<p className="text-muted-foreground">After 60 min</p>
<p className="font-medium text-foreground">{previewRates.after60}</p>
</div>
</div>
</div>
</div>
</div>

<div className="flex gap-3">
<Button
type="button"
variant="secondary"
className="cursor-default"
onClick={() => toast.info("Preview only — this is not a real ticket.")}
>
Claim ticket
</Button>
<Button
type="button"
variant="outline"
className="border-brand-primary text-brand-primary cursor-default"
onClick={() => toast.info("Preview only — this is not a real ticket.")}
>
<Sparkles className="w-4 h-4" />
Rephrase with AI
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
))}
</div>
) : formattedAvailable.length === 0 ? (
<p className="text-sm text-muted-foreground">No available tickets right now.</p>
) : (
formattedAvailable.map((ticket) => (
Expand Down
87 changes: 75 additions & 12 deletions src/app/helpers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import { useCreateProjectInvite, useListProjectInvites, useRevokeProjectInvite }
import { useProjectSelection } from "@/contexts/project-context"
import { useUser } from "@/contexts/user-context"
import Link from "next/link"
import {
HELPER_LIST_PREVIEW_DISCLAIMER,
HELPER_PREVIEW_PLACEHOLDERS,
} from "@/lib/helper-preview-placeholders"

const GithubIcon = ({ className }: { className?: string }) => (
<svg
Expand Down Expand Up @@ -610,8 +614,8 @@ export default function HelpersPage() {
)
}
if (currentView === "added") {
return filteredHelpers.length > 0 ? (
filteredHelpers.map((helper, index) => (
if (filteredHelpers.length > 0) {
return filteredHelpers.map((helper, index) => (
<div key={index} className="px-6 py-4 hover:bg-[#f7f9ff]">
<div className="grid gap-4 items-center" style={{ gridTemplateColumns: '2rem repeat(11, 1fr)' }}>
<div>
Expand Down Expand Up @@ -658,16 +662,75 @@ export default function HelpersPage() {
</div>
</div>
))
) : (
<div className="px-6 py-12 text-center">
<p className="text-muted-foreground mb-2">No helpers yet.</p>
<p className="text-sm text-muted-foreground mb-4">Add helpers by inviting them via email or sharing an invite link.</p>
<Button className="h-10 rounded-xl px-5 text-[14px] font-semibold bg-brand-primary hover:bg-brand-primary/90 text-white shadow-md" onClick={() => setIsDrawerOpen(true)}>
<Plus className="w-4 h-4 mr-2" />
Add new helper
</Button>
</div>
)
}
const noRealHelpers = (helpersData?.length ?? 0) === 0
if (noRealHelpers && projectId) {
return (
<>
<div className="px-6 py-5 border-b border-dashed border-border bg-muted/15">
<p className="text-sm text-muted-foreground mb-4 max-w-3xl">
{HELPER_LIST_PREVIEW_DISCLAIMER}
</p>
<div
role="presentation"
aria-hidden
className="rounded-lg border border-dashed border-border divide-y divide-border overflow-hidden bg-background/90 opacity-90"
>
{HELPER_PREVIEW_PLACEHOLDERS.map((h) => (
<div key={h.id} className="px-6 py-4">
<div className="grid gap-4 items-center" style={{ gridTemplateColumns: '2rem repeat(11, 1fr)' }}>
<div>
<input type="checkbox" disabled className="rounded border-border opacity-50" />
</div>
<div className="col-span-3 flex items-center gap-[18px] min-w-0">
<div
className="w-8 h-8 rounded-[11px] flex items-center justify-center text-sm font-medium text-foreground shrink-0"
style={{ backgroundColor: h.color }}
>
{h.initials}
</div>
<div className="min-w-0">
<span className="text-sm font-medium text-foreground block truncate">{h.name}</span>
<span className="text-xs text-muted-foreground truncate block">{h.involvement}</span>
</div>
</div>
<div className="col-span-3 flex items-center space-x-2 min-w-0">
<GithubIcon className="w-4 h-4 text-foreground shrink-0" />
<span className="text-sm text-[#0F0F11] truncate">{h.githubHandle}</span>
</div>
<div className="col-span-2">
<Badge variant="secondary" className="bg-muted text-muted-foreground hover:bg-muted text-[13px] px-3 py-1">
{mapCategoryToLabel[h.category]}
</Badge>
</div>
<div className="col-span-3 flex items-center justify-end gap-2">
<Badge variant="outline" className="text-[11px] font-medium uppercase tracking-wide shrink-0">
Preview
</Badge>
<span className="text-sm text-muted-foreground px-2">—</span>
</div>
</div>
</div>
))}
</div>
</div>
<div className="px-6 py-12 text-center">
<p className="text-muted-foreground mb-2">No helpers yet.</p>
<p className="text-sm text-muted-foreground mb-4">Add helpers by inviting them via email or sharing an invite link.</p>
<Button className="h-10 rounded-xl px-5 text-[14px] font-semibold bg-brand-primary hover:bg-brand-primary/90 text-white shadow-md" onClick={() => setIsDrawerOpen(true)}>
<Plus className="w-4 h-4 mr-2" />
Add new helper
</Button>
</div>
</>
)
}
return (
<div className="px-6 py-12 text-center text-muted-foreground">
<p className="mb-2">No helpers match your filters.</p>
<p className="text-sm">Try clearing search or switching category to see your team.</p>
</div>
)
}
return filteredRequests.length > 0 ? (
filteredRequests.map((request, index) => (
Expand Down
Loading