Skip to content
Merged
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
14 changes: 3 additions & 11 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
# Release Notes

## v0.3.0

### ✨ Features

- **Date Columns:** Display dates in a human readable format
- **Login Redirect:** Redirect to the dashboard if authenticated
- New features and improvements.

### 🐛 Bug Fixes

- Removed unnecessary columns
- API key last used date wasn't updating
- Zod bug fixed, downgraded to `3.24.4`
- Other bug fixes
- Various bug fixes and optimizations.

### 📚 Docs

Read the docs [here](https://docs.letterspace.app) for more information.
See the latest documentation at [https://docs.letterspace.app](https://docs.letterspace.app).
2 changes: 2 additions & 0 deletions apps/landing-page/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Metadata } from "next"
import { Geist, Geist_Mono } from "next/font/google"
import "./globals.css"
import { Analytics } from "@/components/analytics"

const geistSans = Geist({
variable: "--font-geist-sans",
Expand Down Expand Up @@ -77,6 +78,7 @@ export default function RootLayout({
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
<Analytics />
</body>
</html>
)
Expand Down
13 changes: 13 additions & 0 deletions apps/landing-page/src/components/analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Script from "next/script"

export function Analytics() {
return (
<Script
id="plausible-script"
strategy="afterInteractive"
defer
data-domain="letterspace.app"
src="https://analytics.letterspace.app/js/script.js"
/>
)
}
136 changes: 76 additions & 60 deletions apps/web/src/pages/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -298,36 +298,46 @@ export function DashboardPage() {
</CardHeader>
<CardContent>
<div className="space-y-2">
{Object.entries(dashboard.messageStats ?? {}).map(
([status, count]) => {
const item = statusConfig[
status as keyof typeof statusConfig
] || {
icon: Mail,
className: "bg-muted text-muted-foreground",
}
{Object.keys(dashboard.messageStats ?? {}).length > 0 ? (
Object.entries(dashboard.messageStats ?? {}).map(
([status, count]) => {
const item = statusConfig[
status as keyof typeof statusConfig
] || {
icon: Mail,
className: "bg-muted text-muted-foreground",
}

const Icon = item.icon
const Icon = item.icon

return (
<Card key={status} hoverEffect className="hover:bg-accent">
<CardContent className="p-3">
<div className="flex items-center justify-between">
<div
className={cn(
"flex items-center gap-2",
item.textClassName
)}
>
<Icon className="h-4 w-4" />
<p className="text-sm font-medium">{status}</p>
return (
<Card
key={status}
hoverEffect
className="hover:bg-accent"
>
<CardContent className="p-3">
<div className="flex items-center justify-between">
<div
className={cn(
"flex items-center gap-2",
item.textClassName
)}
>
<Icon className="h-4 w-4" />
<p className="text-sm font-medium">{status}</p>
</div>
<div className="font-medium">{count}</div>
</div>
<div className="font-medium">{count}</div>
</div>
</CardContent>
</Card>
)
}
</CardContent>
</Card>
)
}
)
) : (
<div className="flex h-24 items-center justify-center text-sm text-muted-foreground">
No message status data available.
</div>
)}
</div>
</CardContent>
Expand All @@ -344,43 +354,49 @@ export function DashboardPage() {
</CardHeader>
<CardContent>
<div className="flex flex-col gap-2">
{dashboard.recentCampaigns.map((campaign) => (
<Link
key={campaign.id}
to={`/dashboard/campaigns/${campaign.id}`}
>
<div className="flex items-center justify-between space-x-4 rounded-lg border p-4 hover:bg-accent duration-200">
<div className="flex flex-col space-y-1">
<p className="font-medium">{campaign.title}</p>
<p className="text-sm text-muted-foreground">
Sent to {campaign.sentMessages.toLocaleString()}{" "}
subscribers
</p>
</div>
<div className="flex items-center space-x-4 text-sm">
<div className="flex flex-col items-end space-y-1">
<p className="font-medium">
{campaign.deliveryRate.toFixed(1)}%
</p>
<p className="text-xs text-muted-foreground">
Delivery rate
{dashboard.recentCampaigns.length > 0 ? (
dashboard.recentCampaigns.map((campaign) => (
<Link
key={campaign.id}
to={`/dashboard/campaigns/${campaign.id}`}
>
<div className="flex items-center justify-between space-x-4 rounded-lg border p-4 hover:bg-accent duration-200">
<div className="flex flex-col space-y-1">
<p className="font-medium">{campaign.title}</p>
<p className="text-sm text-muted-foreground">
Sent to {campaign.sentMessages.toLocaleString()}{" "}
subscribers
</p>
</div>
<div className="flex flex-col items-end space-y-1">
<p className="font-medium">
{dayjs(campaign.completedAt).format("DD MMM YYYY")}
</p>
<p className="text-xs text-muted-foreground">
Completed date
</p>
<div className="flex items-center space-x-4 text-sm">
<div className="flex flex-col items-end space-y-1">
<p className="font-medium">
{campaign.deliveryRate.toFixed(1)}%
</p>
<p className="text-xs text-muted-foreground">
Delivery rate
</p>
</div>
<div className="flex flex-col items-end space-y-1">
<p className="font-medium">
{dayjs(campaign.completedAt).format("DD MMM YYYY")}
</p>
<p className="text-xs text-muted-foreground">
Completed date
</p>
</div>
<Button variant="ghost" size="icon">
<ArrowRight className="h-4 w-4" />
</Button>
</div>
<Button variant="ghost" size="icon">
<ArrowRight className="h-4 w-4" />
</Button>
</div>
</div>
</Link>
))}
</Link>
))
) : (
<div className="flex h-24 items-center justify-center text-sm text-muted-foreground">
No recent campaigns yet. Start one to see stats here.
</div>
)}
</div>
</CardContent>
</Card>
Expand Down