rebrand to useSend (#210)

This commit is contained in:
KM Koushik
2025-09-03 08:21:55 +10:00
committed by GitHub
parent b1a59d2705
commit 07c53d3f58
219 changed files with 1349 additions and 2835 deletions

View File

@@ -1,4 +1,4 @@
DATABASE_URL="postgresql://unsend:password@localhost:54320/unsend"
DATABASE_URL="postgresql://usesend:password@localhost:54320/usesend"
REDIS_URL="redis://localhost:6379"
@@ -17,7 +17,7 @@ AWS_SNS_ENDPOINT="http://localhost:3003/api/sns"
NEXTAUTH_SECRET=""
FROM_EMAIL="hello@unsend.dev"
FROM_EMAIL="hello@usesend.com"
API_RATE_LIMIT=2

View File

@@ -4,9 +4,9 @@ REDIS_URL="redis://redis:6379"
# Postgres - required for docker-compose, not needed for just docker
POSTGRES_USER="postgres"
POSTGRES_PASSWORD="postgres"
POSTGRES_DB="unsend"
POSTGRES_DB="usesend"
# Postgres - required
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/unsend"
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/usesend"
# NextAuth - required
NEXTAUTH_URL="http://localhost:3000"
@@ -14,7 +14,7 @@ NEXTAUTH_SECRET=
#SMTP
SMTP_HOST=smtp.mailtrap.io # Example SMTP host
SMTP_USER= "unsend" # Example SMTP user
SMTP_USER= "usesend" # Example SMTP user
## Auth providers any one is required
# GitHub login - required

View File

@@ -2,7 +2,7 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
ignorePatterns: ["apps/**", "packages/**"],
extends: ["@unsend/eslint-config/library.js"],
extends: ["@usesend/eslint-config/library.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,

View File

@@ -26,7 +26,7 @@ body:
id: kind
attributes:
label: Self hosted or Cloud?
description: Does this happen on app.unsend.dev or on your own instance?
description: Does this happen on app.usesend.com or on your own instance?
options:
- Cloud
- Self hosted

View File

@@ -24,7 +24,7 @@ jobs:
- warp-ubuntu-latest-x64-2x
- warp-ubuntu-latest-arm64-2x
app:
- name: unsend
- name: usesend
dockerfile: ./docker/Dockerfile
context: .
- name: smtp-proxy
@@ -65,16 +65,16 @@ jobs:
docker build \
-f "$DOCKER_FILE" \
--progress=plain \
-t "unsend/$APP-$BUILD_PLATFORM:latest" \
-t "unsend/$APP-$BUILD_PLATFORM:$GIT_SHA" \
-t "unsend/$APP-$BUILD_PLATFORM:$APP_VERSION" \
-t "ghcr.io/unsend-dev/$APP-$BUILD_PLATFORM:latest" \
-t "ghcr.io/unsend-dev/$APP-$BUILD_PLATFORM:$GIT_SHA" \
-t "ghcr.io/unsend-dev/$APP-$BUILD_PLATFORM:$APP_VERSION" \
-t "usesend/$APP-$BUILD_PLATFORM:latest" \
-t "usesend/$APP-$BUILD_PLATFORM:$GIT_SHA" \
-t "usesend/$APP-$BUILD_PLATFORM:$APP_VERSION" \
-t "ghcr.io/usesend/$APP-$BUILD_PLATFORM:latest" \
-t "ghcr.io/usesend/$APP-$BUILD_PLATFORM:$GIT_SHA" \
-t "ghcr.io/usesend/$APP-$BUILD_PLATFORM:$APP_VERSION" \
"$CONTEXT"
- name: Push the docker image to DockerHub
run: docker push --all-tags "unsend/$APP-$BUILD_PLATFORM"
run: docker push --all-tags "usesend/$APP-$BUILD_PLATFORM"
env:
BUILD_PLATFORM: ${{ matrix.os == 'warp-ubuntu-latest-arm64-2x' && 'arm64' || 'amd64' }}
APP: ${{ matrix.app.name }}
@@ -82,7 +82,7 @@ jobs:
CONTEXT: ${{ matrix.app.context }}
- name: Push the docker image to GitHub Container Registry
run: docker push --all-tags "ghcr.io/unsend-dev/$APP-$BUILD_PLATFORM"
run: docker push --all-tags "ghcr.io/usesend/$APP-$BUILD_PLATFORM"
env:
BUILD_PLATFORM: ${{ matrix.os == 'warp-ubuntu-latest-arm64-2x' && 'arm64' || 'amd64' }}
APP: ${{ matrix.app.name }}
@@ -117,25 +117,25 @@ jobs:
APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')"
GIT_SHA="$(git rev-parse HEAD)"
for APP_NAME in unsend smtp-proxy; do
for APP_NAME in usesend smtp-proxy; do
docker manifest create \
unsend/$APP_NAME:latest \
--amend unsend/$APP_NAME-amd64:latest \
--amend unsend/$APP_NAME-arm64:latest
usesend/$APP_NAME:latest \
--amend usesend/$APP_NAME-amd64:latest \
--amend usesend/$APP_NAME-arm64:latest
docker manifest create \
unsend/$APP_NAME:$GIT_SHA \
--amend unsend/$APP_NAME-amd64:$GIT_SHA \
--amend unsend/$APP_NAME-arm64:$GIT_SHA
usesend/$APP_NAME:$GIT_SHA \
--amend usesend/$APP_NAME-amd64:$GIT_SHA \
--amend usesend/$APP_NAME-arm64:$GIT_SHA
docker manifest create \
unsend/$APP_NAME:$APP_VERSION \
--amend unsend/$APP_NAME-amd64:$APP_VERSION \
--amend unsend/$APP_NAME-arm64:$APP_VERSION
usesend/$APP_NAME:$APP_VERSION \
--amend usesend/$APP_NAME-amd64:$APP_VERSION \
--amend usesend/$APP_NAME-arm64:$APP_VERSION
docker manifest push unsend/$APP_NAME:latest
docker manifest push unsend/$APP_NAME:$GIT_SHA
docker manifest push unsend/$APP_NAME:$APP_VERSION
docker manifest push usesend/$APP_NAME:latest
docker manifest push usesend/$APP_NAME:$GIT_SHA
docker manifest push usesend/$APP_NAME:$APP_VERSION
done
- name: Create and push GitHub Container Registry manifest
@@ -143,23 +143,23 @@ jobs:
APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')"
GIT_SHA="$(git rev-parse HEAD)"
for APP_NAME in unsend smtp-proxy; do
for APP_NAME in usesend smtp-proxy; do
docker manifest create \
ghcr.io/unsend-dev/$APP_NAME:latest \
--amend ghcr.io/unsend-dev/$APP_NAME-amd64:latest \
--amend ghcr.io/unsend-dev/$APP_NAME-arm64:latest
ghcr.io/usesend/$APP_NAME:latest \
--amend ghcr.io/usesend/$APP_NAME-amd64:latest \
--amend ghcr.io/usesend/$APP_NAME-arm64:latest
docker manifest create \
ghcr.io/unsend-dev/$APP_NAME:$GIT_SHA \
--amend ghcr.io/unsend-dev/$APP_NAME-amd64:$GIT_SHA \
--amend ghcr.io/unsend-dev/$APP_NAME-arm64:$GIT_SHA
ghcr.io/usesend/$APP_NAME:$GIT_SHA \
--amend ghcr.io/usesend/$APP_NAME-amd64:$GIT_SHA \
--amend ghcr.io/usesend/$APP_NAME-arm64:$GIT_SHA
docker manifest create \
ghcr.io/unsend-dev/$APP_NAME:$APP_VERSION \
--amend ghcr.io/unsend-dev/$APP_NAME-amd64:$APP_VERSION \
--amend ghcr.io/unsend-dev/$APP_NAME-arm64:$APP_VERSION
ghcr.io/usesend/$APP_NAME:$APP_VERSION \
--amend ghcr.io/usesend/$APP_NAME-amd64:$APP_VERSION \
--amend ghcr.io/usesend/$APP_NAME-arm64:$APP_VERSION
docker manifest push ghcr.io/unsend-dev/$APP_NAME:latest
docker manifest push ghcr.io/unsend-dev/$APP_NAME:$GIT_SHA
docker manifest push ghcr.io/unsend-dev/$APP_NAME:$APP_VERSION
docker manifest push ghcr.io/usesend/$APP_NAME:latest
docker manifest push ghcr.io/usesend/$APP_NAME:$GIT_SHA
docker manifest push ghcr.io/usesend/$APP_NAME:$APP_VERSION
done

View File

@@ -23,7 +23,7 @@
## Coding Style & Naming Conventions
- TypeScript-first; 2-space indent; semicolons enabled by Prettier.
- Linting: `@unsend/eslint-config`; run `pnpm lint` before PRs.
- Linting: `@usesend/eslint-config`; run `pnpm lint` before PRs.
- Formatting: Prettier 3; run `pnpm format`.
- Files: React components PascalCase (e.g., `AppSideBar.tsx`); folders kebab/lowercase.
- Paths (web): use alias `~/` for src imports (e.g., `import { x } from "~/utils/x"`).

View File

@@ -1,53 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
- **Build**: `pnpm build` (specific: `pnpm build:web`, `pnpm build:editor`, `pnpm build:marketing`)
- **Lint**: `pnpm lint`
- **Dev**: `pnpm dev` (or `pnpm d` for setup + dev)
- **DB**: `pnpm db:migrate-dev`, `pnpm db:studio`, `pnpm db:push`
- **Test**: Run single test with `pnpm test --filter=web -- -t "test name"`
- **Format**: `pnpm format`
## Code Style
- **Formatting**: Prettier with tailwind plugin
- **Imports**: Group by source (internal/external), alphabetize
- **TypeScript**: Strong typing, avoid `any`, use Zod for validation
- **Naming**: camelCase for variables/functions, PascalCase for components/classes
- **React**: Functional components with hooks, group related hooks
- **Component Structure**: Props at top, hooks next, helper functions, then JSX
- **Error Handling**: Use try/catch with specific error types
- **API**: Use tRPC for internal, Hono for public API endpoints
Follow Vercel style guides with strict TypeScript. Be thoughtful, write readable code over premature optimization.
## Architecture Overview
Unsend is an open-source email sending infrastructure built as a monorepo with the following structure:
### Core Applications
- **web** (`apps/web`): Main Next.js dashboard application with tRPC API, Prisma ORM, authentication
- **marketing** (`apps/marketing`): Marketing website built with Next.js
- **smtp-server** (`apps/smtp-server`): SMTP server implementation
- **docs** (`apps/docs`): Documentation using Mintlify
### Shared Packages
- **email-editor** (`packages/email-editor`): Rich email editor using TipTap, JSX Email
- **ui** (`packages/ui`): Shared UI components using shadcn/ui and Tailwind
- **sdk** (`packages/sdk`): Client SDK for Unsend API
- **eslint-config**, **typescript-config**, **tailwind-config**: Shared configurations
### Key Technologies
- **Frontend**: Next.js 15, React 19, Tailwind CSS, shadcn/ui, Framer Motion
- **Backend**: tRPC, Prisma, PostgreSQL, Redis (BullMQ queues)
- **Email**: AWS SES, JSX Email, custom email editor
- **Auth**: NextAuth.js with GitHub/Google providers
- **API**: Hono for public REST API with OpenAPI/Swagger
- **Infrastructure**: Docker, Railway deployment ready
### Development Workflow
- Uses Turbo for monorepo builds and development
- Environment setup with `pnpm dx` (installs deps, starts Docker, runs migrations)
- Database operations prefixed with `pnpm db:`
- Each package has independent linting and building

View File

@@ -1,6 +1,6 @@
# 🤝 Contributing to Unsend
# 🤝 Contributing to useSend
Thanks for your interest in contributing to **Unsend**! Were an open-source email infrastructure platform, and wed love your help to make it even better. This guide will walk you through how to get started, set up the project locally, and submit contributions.
Thanks for your interest in contributing to **useSend**! Were an open-source email infrastructure platform, and wed love your help to make it even better. This guide will walk you through how to get started, set up the project locally, and submit contributions.
---
@@ -9,7 +9,7 @@ Thanks for your interest in contributing to **Unsend**! Were an open-source e
All contributions begin with setting up the project locally. Follow the steps below to get started.
📖 **Refer to the full setup guide:**
[https://docs.unsend.dev/get-started/local](https://docs.unsend.dev/get-started/local)
[https://docs.usesend.com/get-started/local](https://docs.usesend.com/get-started/local)
### ⚙️ Prerequisites
@@ -28,8 +28,8 @@ Youll need:
### 1. Fork & Clone
```bash
git clone https://github.com/YOUR-USERNAME/unsend.git
cd unsend
git clone https://github.com/YOUR-USERNAME/usesend.git
cd usesend
```
### 2. Install Dependencies
@@ -58,6 +58,7 @@ openssl rand -base64 32
### 4. GitHub OAuth (Optional for Dev)
Set up a GitHub OAuth App:
- Homepage: `http://localhost:3000/login`
- Callback: `http://localhost:3000/api/auth/callback/github`
@@ -147,7 +148,7 @@ apps/
packages/
├── eslint-config # Shared ESLint rules
├── sdk # TypeScript SDK for Unsend REST API
├── sdk # TypeScript SDK for useSend REST API
├── tailwind-config # Shared Tailwind setup
├── typescript-config # Shared tsconfig
├── ui # Shared UI components (buttons, modals, etc.)
@@ -164,6 +165,7 @@ git checkout -b feat/your-feature
```
2. **Make Your Changes**
- Follow the existing project structure.
- Write clean, modular, and reusable code.
- Formatting is enforced with Prettier.
@@ -189,8 +191,8 @@ git push origin feat/your-feature
## 💬 Community and Support
- **Discord**: [Join our server](https://discord.gg/BU8n8pJv8S)
- **GitHub Discussions**: [Start a discussion](https://github.com/unsend-dev/unsend/discussions)
- **GitHub Issues**: [Report issues or bugs](https://github.com/unsend-dev/unsend/issues)
- **GitHub Discussions**: [Start a discussion](https://github.com/usesend/usesend/discussions)
- **GitHub Issues**: [Report issues or bugs](https://github.com/usesend/usesend/issues)
---
@@ -199,6 +201,6 @@ git push origin feat/your-feature
Need help or unsure where to begin? Just ask!
- Chat with us on [Discord](https://discord.gg/BU8n8pJv8S)
- Open an [Issue](https://github.com/unsend-dev/unsend/issues)
- Open an [Issue](https://github.com/usesend/usesend/issues)
Were excited to see your ideas and contributions! 💌

View File

@@ -1,36 +1,36 @@
<p align="center">
<img style="width: 200px;height: 200px; margin: auto;" src="https://github.com/unsend-dev/unsend/assets/24666922/76268b21-0786-4f89-aa0f-e003fd0a6d60" alt="Unsend Logo">
<img style="width: 200px;height: 200px; margin: auto;" src="https://github.com/usesend/usesend/assets/24666922/76268b21-0786-4f89-aa0f-e003fd0a6d60" alt="useSend Logo">
</p>
<p align="center" style="margin-top: 20px">
<p align="center">
The Open Source sending infrastructure.
<br>
<a href="https://unsend.dev"><strong>Learn more »</strong></a>
<a href="https://usesend.com"><strong>Learn more »</strong></a>
<br />
<br />
<a href="https://discord.gg/BU8n8pJv8S">Discord</a>
.
<a href="https://unsend.dev">Website</a>
<a href="https://usesend.com">Website</a>
·
<a href="https://github.com/unsend-dev/unsend/issues">Issues</a>
<a href="https://github.com/usesend/usesend/issues">Issues</a>
</p>
</p>
<p align="center">
<a href="https://discord.gg/BU8n8pJv8S"><img src="https://img.shields.io/badge/Discord-unsend-%235865F2" alt="Join Unsend on Discord"></a>
<a href="https://github.com/unsend-dev/unsend/stargazers"><img src="https://img.shields.io/github/stars/unsend-dev%2Funsend" alt="GitHub Stars"></a>
<a href="https://github.com/unsend-dev/unsend/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License"></a>
<a href="https://hub.docker.com/r/unsend/unsend"><img alt="Docker Automated build" src="https://img.shields.io/docker/pulls/unsend/unsend"></a>
<a href="https://discord.gg/BU8n8pJv8S"><img src="https://img.shields.io/badge/Discord-usesend-%235865F2" alt="Join useSend on Discord"></a>
<a href="https://github.com/usesend/usesend/stargazers"><img src="https://img.shields.io/github/stars/usesend%2Fusesend" alt="GitHub Stars"></a>
<a href="https://github.com/usesend/usesend/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License"></a>
<a href="https://hub.docker.com/r/usesend/usesend"><img alt="Docker Automated build" src="https://img.shields.io/docker/pulls/usesend/usesend"></a>
</p>
<div align="center">
<img src="https://github.com/unsend-dev/unsend/assets/24666922/68c41a6f-8fd1-4a3e-8d9b-987dda105c22" style="width: 100%;" />
<img src="https://github.com/usesend/usesend/assets/24666922/68c41a6f-8fd1-4a3e-8d9b-987dda105c22" style="width: 100%;" />
</div>
## About this project
As most of email products out there, Unsend also uses Amazon SES under the hood to send emails. We provide an open and alternative way to send emails reliably and cheaply with a great dashboard. You can also use Unsend manage contacts and send bulk emails(newsletter, product updates etc). We will take care of the subscriptions.
As most of email products out there, useSend also uses Amazon SES under the hood to send emails. We provide an open and alternative way to send emails reliably and cheaply with a great dashboard. You can also use useSend manage contacts and send bulk emails(newsletter, product updates etc). We will take care of the subscriptions.
Currently we only support emails, but we plan to expand to other sending protocols like SMS, push notification and even whatsapp.
@@ -50,14 +50,14 @@ We are currently in beta and trying to rollout to public slowly. If you're inter
## Community and Next Steps 🎯
We're currently working on opening unsend for public beta.
We're currently working on opening useSend for public beta.
- Check out the first source code release in this repository and test it.
- Tell us what you think in the [Discussions](https://github.com/unsend-dev/unsend/discussions).
- Tell us what you think in the [Discussions](https://github.com/usesend/usesend/discussions).
- Join the [Discord server](https://discord.gg/BU8n8pJv8S) for any questions and getting to know to other community members.
- ⭐ the repository to help us raise awareness.
- Spread the word on Twitter.
- Fix or create [issues](https://github.com/unsend/unsend/issues), that are needed for the first production release.
- Fix or create [issues](https://github.com/usesend/usesend/issues), that are needed for the first production release.
## Tech Stack
@@ -72,25 +72,25 @@ We're currently working on opening unsend for public beta.
### Email editor
Check out the editor code for [here](https://github.com/unsend-dev/unsend/tree/main/packages/email-editor). Editor is possible only because of the amazing tools and libraries.
Check out the editor code for [here](https://github.com/usesend/usesend/tree/main/packages/email-editor). Editor is possible only because of the amazing tools and libraries.
- [jsx-email](https://jsx.email/) - converts editor content to html
- [maily.to](https://maily.to/) - unsend email editor is greatly inspired from maily.to
- [maily.to](https://maily.to/) - useSend email editor is greatly inspired from maily.to
- [tiptap](https://tiptap.dev/) - editor core
## Local Development
Follow our detailed guide to run Unsend locally
Follow our detailed guide to run useSend locally
[https://docs.unsend.dev/get-started/local](https://docs.unsend.dev/get-started/local)
[https://usesend.com/docs/get-started/local](https://usesend.com/docs/get-started/local)
## Docker
We provide a Docker container for Unsend, which is published on both DockerHub and GitHub Container Registry.
We provide a Docker container for useSend, which is published on both DockerHub and GitHub Container Registry.
DockerHub: [https://hub.docker.com/r/unsend/unsend](https://hub.docker.com/r/unsend/unsend)
DockerHub: [https://hub.docker.com/r/usesend/usesend](https://hub.docker.com/r/usesend/usesend)
GitHub Container Registry: [https://ghcr.io/unsend-dev/unsend](https://ghcr.io/unsend-dev/unsend)
GitHub Container Registry: [https://ghcr.io/usesend/usesend](https://ghcr.io/usesend/usesend)
You can pull the Docker image from either of these registries and run it with your preferred container hosting provider.
@@ -100,24 +100,22 @@ For detailed instructions on how to configure and run the Docker container, plea
## Self Hosting
Checkout the [Self hosting](https://docs.unsend.dev/get-started/self-hosting) guide to learn how to self-host Unsend.
Checkout the [Self hosting](https://usesend.com/docs/get-started/self-hosting) guide to learn how to self-host useSend.
Also
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/QbMnwX?referralCode=oaAwvp)
## Star History
<a href="https://star-history.com/#unsend-dev/unsend&Date">
<a href="https://star-history.com/#usesend/usesend&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=unsend-dev/unsend&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=unsend-dev/unsend&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=unsend-dev/unsend&type=Date" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=usesend/usesend&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=usesend/usesend&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=usesend/usesend&type=Date" />
</picture>
</a>
## Sponsors
We are grateful for the support of our sponsors.

View File

@@ -5,12 +5,12 @@ description: "Fundamental concepts of Usend's API."
## Base URL
Unsend's API is built on REST principles and is served over HTTPS. To ensure data privacy, unencrypted HTTP is not supported.
useSend's API is built on REST principles and is served over HTTPS. To ensure data privacy, unencrypted HTTP is not supported.
The Base URL for all API endpoints is:
```sh Terminal
https://app.unsend.dev/api/
https://app.usesend.com/api/
```
## Authentication
@@ -21,4 +21,4 @@ Authentication to Usend's API is performed via the Authorization header with a B
Authorization: Bearer us_12345
```
You can create a new token/API key under your Unsend [Developer Settings](https://app.unsend.dev/dev-settings/api-keys).
You can create a new token/API key under your useSend [Developer Settings](https://app.usesend.com/dev-settings/api-keys).

View File

@@ -2,11 +2,11 @@
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "Unsend API"
"title": "useSend API"
},
"servers": [
{
"url": "https://app.unsend.dev/api"
"url": "https://app.usesend.com/api"
}
],
"components": {

View File

@@ -1,6 +1,6 @@
---
title: Create AWS credentials
description: Step by step guide to create AWS credentials to self-host Unsend.
description: Step by step guide to create AWS credentials to self-host useSend.
---
<Steps>

View File

@@ -1,12 +1,12 @@
---
title: Local Development
description: "A guide on how to run Unsends codebase locally"
description: "A guide on how to run useSends codebase locally"
icon: code
---
## Introduction
Unsend's codebase is fully [open-source on github](https://github.com/unsend-dev/unsend)
useSend's codebase is fully [open-source on github](https://github.com/usesend/usesend)
Here is the codebase structure
@@ -27,7 +27,7 @@ The `apps` directory contains the code for:
- `web`: Code for our dashboard and email infra
- `marketing`: The code for the landing page of Unsend
- `marketing`: The code for the landing page of useSend
- `docs`: The documentation that you are currently reading.
@@ -35,7 +35,7 @@ The `packages` directory contains the code for:
- `eslint-config` package contains shared ESLint configuration settings
- `sdk` package contains typescript sdk for unsend rest api
- `sdk` package contains TypeScript SDK for useSend REST API
- `tailwind-config` This package contains a shared Tailwind CSS configuration.
@@ -43,9 +43,9 @@ The `packages` directory contains the code for:
- `ui` This package is a collection of reusable UI components like buttons, badges, etc
## Running Unsend locally
## Running useSend locally
To run Unsend, locally you will need to setup the following:
To run useSend locally, you will need to setup the following:
- [AWS](https://aws.amazon.com/) Free tier account will work.
@@ -57,13 +57,13 @@ To run Unsend, locally you will need to setup the following:
<Steps>
<Step title="Fork the repo">
Click on the fork button on [GitHub](https://github.com/unsend-dev/unsend) to fork the repo
Click on the fork button on [GitHub](https://github.com/usesend/usesend) to fork the repo
</Step>
<Step title="Clone the repo">
Once the repo is forked you can clone it on your local machine using:
```bash
git clone https://github.com/your-username/unsend.git
git clone https://github.com/your-username/usesend.git
```
</Step>
@@ -107,7 +107,7 @@ openssl rand -base64 32
variables. for development email link will logged in the console.
</Note>
Next, [create a new GitHub App](https://github.com/settings/applications/new). This will allow you to sign in to Unsend with your GitHub account.4
Next, [create a new GitHub App](https://github.com/settings/applications/new). This will allow you to sign in to useSend with your GitHub account.
Add the homepage as:
@@ -131,7 +131,7 @@ Once the app is added you can add the Client ID under `GITHUB_ID`and CLIENT SECR
will not be sent out.
</Note>
Next, we need to add in the [AWS credentials](https://docs.unsend.dev/get-started/create-aws-credentials). Follow the detailed guide to get the AWS credentials with accurate permissions and add them in:
Next, we need to add in the [AWS credentials](https://docs.usesend.com/get-started/create-aws-credentials). Follow the detailed guide to get the AWS credentials with accurate permissions and add them in:
```
AWS_ACCESS_KEY=<access-key-id>
@@ -141,7 +141,7 @@ AWS_SECRET_KEY=<secret-access-key>
</Step>
</Steps>
## Running Unsend locally
## Running useSend locally
We are using a local Postgresql server and a local Redis server. But if you don't have docker you can also manually set these up.

View File

@@ -1,13 +1,13 @@
---
title: NodeJS
description: "Send your mail using unsend in NodeJS"
description: "Send your mail using useSend in NodeJS"
icon: node-js
---
## Prerequisites
- [Unsend API key](https://app.unsend.dev/dev-settings/api-keys)
- [Verified domain](https://app.unsend.dev/domains)
- [useSend API key](https://app.usesend.com/dev-settings/api-keys)
- [Verified domain](https://app.usesend.com/domains)
## Using SDK
@@ -15,48 +15,48 @@ icon: node-js
<Step title="Install SDK">
<CodeGroup>
```bash npm
npm install unsend
npm install usesend
```
```bash yarn
yarn add unsend
yarn add usesend
```
```bash pnpm
pnpm add unsend
pnpm add usesend
```
```bash bun
bun add unsend
bun add usesend
```
</CodeGroup>
</Step>
<Step title="Initialize SDK">
Get the API key from the [Unsend dashboard](https://app.unsend.dev/dev-settings/api-keys) and initialize the SDK
Get the API key from the [useSend dashboard](https://app.usesend.com/dev-settings/api-keys) and initialize the SDK
```javascript
import { Unsend } from "unsend";
import { UseSend } from "usesend";
const unsend = new Unsend("us_12345");
const usesend = new UseSend("us_12345");
```
If you are running a self-hosted version of Unsend, pass the base URL as the
If you are running a self-hosted version of useSend, pass the base URL as the
second argument:
```javascript
const unsend = new Unsend("us_12345", "https://my-unsend-instance.com");
const usesend = new UseSend("us_12345", "https://app.usesend.com");
```
</Step>
<Step title="Send Email">
```javascript
unsend.emails.send({
usesend.emails.send({
to: "hello@acme.com",
from: "hello@company.com",
subject: "Unsend email",
html: "<p>Unsend is the best open source product to send emails</p>",
text: "Unsend is the best open source product to send emails",
subject: "useSend email",
html: "<p>useSend is the best open source product to send emails</p>",
text: "useSend is the best open source product to send emails",
});
```
</Step>
@@ -66,12 +66,12 @@ icon: node-js
<Steps>
<Step title="Get the contact book id">
Get the contact book id from the [Unsend dashboard](https://app.unsend.dev/contacts/). Copy the contact book id
Get the contact book id from the [useSend dashboard](https://app.usesend.com/contacts/). Copy the contact book id
</Step>
<Step title="Add contacts">
```javascript
unsend.contacts
usesend.contacts
.create("clzeydgeygff", {
email: "hey@koushik.dev",
firstName: "Koushik",
@@ -83,7 +83,7 @@ icon: node-js
<Step title="Update contact">
```javascript
unsend.contacts.update("clzeydgeygff", contactId, {
usesend.contacts.update("clzeydgeygff", contactId, {
firstName: "Koushik",
lastName: "KM",
});

View File

@@ -1,6 +1,6 @@
---
title: Self hosting Unsend
description: "An end-to-end guide on how to self-host Unsend. An opensource sending infrastructure for developers."
title: Self hosting useSend
description: "An end-to-end guide on how to self-host useSend. An open-source sending infrastructure for developers."
icon: server
---
@@ -13,7 +13,7 @@ If you have any questions join [#self-host](https://discord.gg/gbsvjb9MqV) on di
## Step 1: Environment variables
Unsend depends on AWS ses to send emails and SNS to receive email status. Along with that it also depends on Postgres as a database and Redis for queue. Copy the `.env.selfhost.example` file to `.env` and fill in the values.
useSend depends on AWS SES to send emails and SNS to receive email status. Along with that it also depends on Postgres as a database and Redis for queue. Copy the `.env.selfhost.example` file to `.env` and fill in the values.
<Steps>
<Step title="AWS credentials">
@@ -33,11 +33,11 @@ Add the following environment variables.
</Step>
<Step title="GitHub app credentials for login">
Usend uses github authentication for login.
useSend uses GitHub authentication for login.
Use this link to [create an github app](https://docs.github.com/en/apps/creating-github-apps/about-creating-github-apps/about-creating-github-apps)
Callback URL : `https://<your-unsend-instance>/api/auth/callback/github`
Callback URL : `https://<your-usesend-instance>/api/auth/callback/github`
![github app](/images/github-callback.png)
@@ -48,10 +48,10 @@ GITHUB_ID="<your-github-client-id>"
GITHUB_SECRET="<your-github-client-secret>"
```
<Info>If you want email/password login, please help us out with the [code](https://github.com/unsend-dev/unsend) </Info>
<Info>If you want email/password login, please help us out with the [code](https://github.com/usesend/usesend) </Info>
</Step>
<Step title="Database & Redis">
Unsend uses Postgres as a database and Redis as a queue. You need to create a new database and add the following environment variables.
useSend uses Postgres as a database and Redis as a queue. You need to create a new database and add the following environment variables.
If you're using docker-compose or our railway template, it's all automatically done for you.
@@ -71,8 +71,8 @@ openssl rand -base64 32
Add the following environment variables.
```env
NEXTAUTH_URL="https://<your-unsend-instance>"
NEXTAUTH_SECRET="<your-unsend-secret>"
NEXTAUTH_URL="https://<your-usesend-instance>"
NEXTAUTH_SECRET="<your-usesend-secret>"
```
</Step>
@@ -86,7 +86,7 @@ You can use any platforms that supports docker. You can also use the railway tem
Follow this guide to setup your docker instance: [Set up docker](/get-started/set-up-docker)
[![Docker image](https://img.shields.io/badge/dockerhub-images-important.svg?logo=Docker)](https://hub.docker.com/r/unsend/unsend)
[![Docker image](https://img.shields.io/badge/dockerhub-images-important.svg?logo=Docker)](https://hub.docker.com/r/usesend/usesend)
### Railway
@@ -96,15 +96,15 @@ Updating image is easy, click on the 3 dots and redeploy. This will pull the lat
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/QbMnwX?referralCode=oaAwvp)
Your unsend instance is now live now.
Your useSend instance is now live now.
## Step 3: Setting up a region
In order to send emails, you need to select an region in aws. Use a region where your users are located / where unsend is hosted. If you're confused just use `us-east-1`.
In order to send emails, you need to select a region in AWS. Use a region where your users are located / where useSend is hosted. If you're confused just use `us-east-1`.
You can check available regions [here](https://docs.aws.amazon.com/general/latest/gr/ses.html)
Once you logged in to unsend, it will prompt you add ses configuration.
Once you log in to useSend, it will prompt you add SES configuration.
- Add the region
- Add the callback url, which is basically the app url. Note this should be accesible from internet. This is how you get the delivery status of the emails.
@@ -120,10 +120,10 @@ Once you logged in to unsend, it will prompt you add ses configuration.
## Step 5: SMTP Proxy Server (Optional)
The SMTP proxy server is an optional component that allows applications to send emails through Unsend using standard SMTP protocol instead of the REST API. This is useful for legacy applications, email clients, or any software that needs to send emails via SMTP.
The SMTP proxy server is an optional component that allows applications to send emails through useSend using standard SMTP protocol instead of the REST API. This is useful for legacy applications, email clients, or any software that needs to send emails via SMTP.
<Tip>
The complete source code for the SMTP proxy server is available at: [unsend-dev/unsend/tree/main/apps/smtp-server](https://github.com/unsend-dev/unsend/tree/main/apps/smtp-server)
The complete source code for the SMTP proxy server is available at: [usesend/usesend/tree/main/apps/smtp-server](https://github.com/usesend/usesend/tree/main/apps/smtp-server)
</Tip>
### When to use the SMTP proxy:
@@ -137,19 +137,19 @@ The complete source code for the SMTP proxy server is available at: [unsend-dev/
Create a `docker-compose.yml` file for the SMTP server:
```yaml
name: unsend-smtp-server
name: usesend-smtp-server
services:
smtp-server:
container_name: unsend-smtp-server
image: unsend/smtp-proxy:latest
container_name: usesend-smtp-server
image: usesend/smtp-proxy:latest
environment:
SMTP_AUTH_USERNAME: "unsend" # Username for SMTP authentication
UNSEND_BASE_URL: "https://your-unsend-instance.com" # Your Unsend instance URL
SMTP_AUTH_USERNAME: "usesend" # Username for SMTP authentication
USESEND_BASE_URL: "https://your-usesend-instance.com" # Your useSend instance URL
# Optional: SSL certificate paths for secure connections
# UNSEND_API_KEY_PATH: "/certs/server.key"
# UNSEND_API_CERT_PATH: "/certs/server.crt"
# USESEND_API_KEY_PATH: "/certs/server.key"
# USESEND_API_CERT_PATH: "/certs/server.crt"
# Optional: Mount SSL certificates
# volumes:
@@ -178,12 +178,12 @@ To send emails through the proxy, configure your application with these SMTP set
- **Host**: Your server's IP address or domain
- **Ports**: 25, 587 (STARTTLS), 465 (SSL/TLS), 2587, or 2465
- **Username**: `unsend` (or your custom `SMTP_AUTH_USERNAME`)
- **Password**: Your Unsend API key
- **Username**: `usesend` (or your custom `SMTP_AUTH_USERNAME`)
- **Password**: Your useSend API key
- **Encryption**: STARTTLS (ports 25, 587, 2587) or SSL/TLS (ports 465, 2465)
<Tip>
The SMTP proxy forwards all emails to your Unsend instance, so make sure your main Unsend application is running and accessible.
The SMTP proxy forwards all emails to your useSend instance, so make sure your main useSend application is running and accessible.
</Tip>
<Warning>
@@ -201,5 +201,5 @@ You're all set up now.
If you have any questions, please join [#self-host](https://discord.gg/gbsvjb9MqV) on discord.
<Tip>
A community member shared a short write-up on hosting Unsend with [Coolify](https://mattstein.com/thoughts/coolify-unsend/). Give it a read if you need another reference.
A community member shared a short write-up on hosting useSend with [Coolify](https://mattstein.com/thoughts/coolify-unsend/). Give it a read if you need another reference.
</Tip>

View File

@@ -1,6 +1,6 @@
---
title: Docker setup for unsend
description: The following guide will walk you through setting up Unsend using Docker. You can choose between a production setup using Docker Compose or a standalone container.
title: Docker setup for useSend
description: The following guide will walk you through setting up useSend using Docker. You can choose between a production setup using Docker Compose or a standalone container.
---
## Prerequisites
@@ -12,9 +12,9 @@ Before you begin, ensure that you have the following installed:
## Option 1: Production Docker Compose Setup
This setup includes PostgreSQL, Redis and the Unsend application.
This setup includes PostgreSQL, Redis and the useSend application.
1. Download the Docker Compose file from the Unsend repository: [compose.yml](https://github.com/unsend-dev/unsend/blob/main/docker/prod/compose.yml)
1. Download the Docker Compose file from the useSend repository: [compose.yml](https://github.com/usesend/usesend/blob/main/docker/prod/compose.yml)
2. Navigate to the directory containing the `compose.yml` file.
3. Create a `.env` file in the same directory. Copy the contents of `.env.selfhost.example`
4. Run the following command to start the containers:
@@ -23,24 +23,24 @@ This setup includes PostgreSQL, Redis and the Unsend application.
docker-compose --env-file ./.env up -d
```
This will start the PostgreSQL database, Redis and the Unsend application containers.
This will start the PostgreSQL database, Redis and the useSend application containers.
5. Access the Unsend application by visiting `http://localhost:3000` in your web browser.
5. Access the useSend application by visiting `http://localhost:3000` in your web browser.
## Option 2: Standalone Docker Container
If you prefer to host the Unsend application on your container provider of choice, you can use the pre-built Docker image from DockerHub or GitHub's Package Registry. Note that you will need to provide your own database and SMTP host.
If you prefer to host the useSend application on your container provider of choice, you can use the pre-built Docker image from DockerHub or GitHub's Package Registry. Note that you will need to provide your own database and SMTP host.
1. Pull the Unsend Docker image:
1. Pull the useSend Docker image:
```
docker pull unsend/unsend
docker pull usesend/usesend
```
Or, if using GitHub's Package Registry:
```
docker pull ghcr.io/unsend-dev/unsend
docker pull ghcr.io/usesend/usesend
```
2. Run the Docker container, providing the necessary environment variables for your database and SMTP host:
@@ -57,13 +57,13 @@ docker run -d \
-e AWS_DEFAULT_REGION="<your-next-private-aws-region>"
-e GITHUB_ID="<your-next-private-github-id>"
-e GITHUB_SECRET="<your-next-private-github-secret>"
unsend/unsend
usesend/usesend
```
Replace the placeholders with your actual database and aws details.
1. Access the Unsend application by visiting the URL you provided in the `NEXTAUTH_URL` environment variable in your web browser.
1. Access the useSend application by visiting the URL you provided in the `NEXTAUTH_URL` environment variable in your web browser.
## Success
You have now successfully set up Unsend using Docker. You can start sending emails efficiently. If you encounter any issues or have further questions, please refer to the official Unsend documentation or seek assistance from the community.
You have now successfully set up useSend using Docker. You can start sending emails efficiently. If you encounter any issues or have further questions, please refer to the official useSend documentation or seek assistance from the community.

View File

@@ -1,6 +1,6 @@
---
title: SMTP support
description: "A guide to integrate Unsend with SMTP"
description: "A guide to integrate useSend with SMTP"
icon: envelope
---
@@ -8,31 +8,31 @@ icon: envelope
You will need an API key and a verified domain to get the most out of this guide:
- [API Key](https://app.unsend.dev/dev-settings/api-keys)
- [Verified Domain](https://app.unsend.dev/domains)
- [API Key](https://app.usesend.com/dev-settings/api-keys)
- [Verified Domain](https://app.usesend.com/domains)
## SMTP credentials
To set up your SMTP integration, you'll need to provide the following credentials:
- **Host:** ```smtp.unsend.dev```
- **Host:** ```smtp.usesend.com```
- **Port:** ```465```, ```587```, ```2465```, or ```2587```
- **Username:** ```unsend```
- **Username:** ```usesend```
- **Password:** ```YOUR-API-KEY```
## Example with Nodemailer
Following example with Nodemailer shows how you can send mails with SMTP support from Unsend and Nodemailer.
Following example with Nodemailer shows how you can send mails with SMTP support from useSend and Nodemailer.
```javascript
const nodemailer = require("nodemailer");
const transporter = nodemailer.createTransport({
host: "smtp.unsend.dev",
host: "smtp.usesend.com",
port: 465,
secure: false,
auth: {
user: "unsend",
user: "usesend",
pass: "us_123",
},
tls: {
@@ -44,8 +44,8 @@ const mailOptions = {
to: "sender@example.com",
from: "hello@example.com",
subject: "Testing SMTP",
html: "<strong>THIS IS USING SMTP,</strong><p>Unsend is the best open source sending platform<p><p>check out <a href='https://unsend.dev'>unsend.dev</a>",
text: "hello,\n\nUnsend is the best open source sending platform",
html: "<strong>THIS IS USING SMTP,</strong><p>useSend is the best open source sending platform<p><p>check out <a href='https://usesend.com'>usesend.com</a>",
text: "hello,\n\nuseSend is the best open source sending platform",
};
transporter.sendMail(mailOptions, (error, info) => {

View File

@@ -1,29 +1,29 @@
---
title: Use with React Email
description: "A guide on how to use Unsend with React Email"
description: "A guide on how to use useSend with React Email"
---
## Introduction
[React Email](https://react.email/docs/introduction) is a library for building emails with React. In this guide, we will show you how to use Unsend with React Email.
[React Email](https://react.email/docs/introduction) is a library for building emails with React. In this guide, we will show you how to use useSend with React Email.
## Install dependencies
<CodeGroup>
```sh npm
npm install unsend @react-email/render
npm install usesend @react-email/render
```
```sh yarn
yarn add unsend @react-email/render
yarn add usesend @react-email/render
```
```sh pnpm
pnpm add unsend @react-email/render
pnpm add usesend @react-email/render
```
```sh bun
bun add unsend @react-email/render
bun add usesend @react-email/render
```
</CodeGroup>
@@ -46,21 +46,21 @@ export function Email(props) {
}
```
## Send an email using Unsend
## Send an email using useSend
```ts
import { Unsend } from "unsend";
import { UseSend } from "usesend";
import { render } from "@react-email/render";
import { Email } from "./email";
const unsend = new Unsend("us_your_unsend_api_key");
const usesend = new UseSend("us_your_usesend_api_key");
const html = await render(<Email url="https://unsend.dev" />);
const html = await render(<Email url="https://usesend.com" />);
const response = await unsend.emails.send({
to: "hello@unsend.dev",
from: "hello@unsend.dev",
subject: "Unsend email",
const response = await usesend.emails.send({
to: "hello@usesend.com",
from: "hello@usesend.com",
subject: "useSend email",
html,
});
```
@@ -77,7 +77,7 @@ If you're using nodejs, importing `email.jsx` might fail. make sure to add these
}
```
Checkout this [example](https://github.com/unsend-dev/unsend-js-examples/tree/main/react-email-js)
Checkout this [example](https://github.com/usesend/unsend-js-examples/tree/main/react-email-js)
### TypeScript
@@ -89,4 +89,4 @@ Just add `jsx` to your `tsconfig.json`
}
```
Checkout this [example](https://github.com/unsend-dev/unsend-js-examples/tree/main/react-email-ts)
Checkout this [example](https://github.com/usesend/unsend-js-examples/tree/main/react-email-ts)

View File

@@ -1,6 +1,6 @@
---
title: Introduction
description: "Unsend is Open source alternative to Resend, Sendgrid, Mailgun and Postmark etc."
description: "useSend is open source alternative to Resend, Sendgrid, Mailgun and Postmark etc."
icon: rocket
---
@@ -12,7 +12,7 @@ Quicklinks to set up your account and get started
<Card
title="Add domain"
icon="globe"
href="https://app.unsend.dev/domains"
href="https://app.usesend.com/domains"
>
Add domains to send emails
</Card>
@@ -20,7 +20,7 @@ Quicklinks to set up your account and get started
<Card
title="Create API key"
icon="key"
href="https://app.unsend.dev/dev-settings/api-keys"
href="https://app.usesend.com/dev-settings/api-keys"
>
Generate API key to send emails from your app.
</Card>

View File

@@ -1,6 +1,6 @@
{
"$schema": "https://mintlify.com/schema.json",
"name": "Unsend",
"name": "useSend",
"logo": {
"dark": "/logo/Logo-wordmark-dark.png",
"light": "/logo/Logo-wordmark.png"
@@ -18,12 +18,12 @@
"topbarLinks": [
{
"name": "Support",
"url": "mailto:hello@unsend.dev"
"url": "mailto:hey@usesend.com"
}
],
"topbarCtaButton": {
"name": "Dashboard",
"url": "https://app.unsend.dev"
"url": "https://app.usesend.com"
},
"tabs": [
{
@@ -35,7 +35,7 @@
{
"name": "GitHub",
"icon": "github",
"url": "https://github.com/unsend-dev/unsend"
"url": "https://github.com/usesend/usesend"
},
{
"name": "Community",

View File

@@ -1,9 +1,10 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ["@unsend/eslint-config/next.js"],
extends: ["@usesend/eslint-config/next.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
};

View File

@@ -1,36 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -1,36 +0,0 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

5
apps/marketing/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@@ -0,0 +1,12 @@
/** @type {import("next").NextConfig} */
const config = {
// Static export for marketing site
output: "export",
images: {
// Required for static export if using images
unoptimized: true,
},
};
export default config;

View File

@@ -1,6 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
};
export default nextConfig;

View File

@@ -2,33 +2,36 @@
"name": "marketing",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev -p 3001",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "eslint . --max-warnings 0"
},
"dependencies": {
"@heroicons/react": "^2.2.0",
"@unsend/email-editor": "workspace:*",
"@unsend/ui": "workspace:*",
"date-fns": "^4.1.0",
"framer-motion": "^12.9.2",
"lucide-react": "^0.503.0",
"next": "15.3.1",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"next": "^15.3.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"@usesend/ui": "workspace:*",
"@usesend/email-editor": "workspace:*"
},
"devDependencies": {
"@next/eslint-plugin-next": "^15.3.1",
"@types/eslint": "^9.6.1",
"@types/node": "^22.15.2",
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.2",
"@unsend/eslint-config": "workspace:*",
"@unsend/tailwind-config": "workspace:*",
"autoprefixer": "^10.4.21",
"eslint": "^9.25.1",
"eslint-config-next": "15.3.1",
"@typescript-eslint/eslint-plugin": "^8.31.0",
"@typescript-eslint/parser": "^8.31.0",
"@usesend/eslint-config": "workspace:*",
"@usesend/tailwind-config": "workspace:*",
"@usesend/typescript-config": "workspace:*",
"eslint": "^8.57.1",
"eslint-config-next": "^15.3.1",
"postcss": "^8.5.3",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.11",
"tailwindcss": "^3.4.1",
"typescript": "^5.8.3"
}

View File

@@ -0,0 +1,8 @@
const config = {
plugins: {
tailwindcss: {},
},
};
module.exports = config;

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -1,13 +0,0 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_11_17" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="512" height="512">
<rect width="512" height="512" fill="black"/>
</mask>
<g mask="url(#mask0_11_17)">
<path d="M375.776 63.6078C376.949 58.409 384.357 58.409 385.53 63.6078L435.035 282.926C436.338 288.703 428.294 291.55 425.673 286.239L385.137 204.087C383.303 200.37 378.003 200.37 376.169 204.087L335.632 286.239C333.012 291.55 324.967 288.703 326.271 282.926L375.776 63.6078Z" fill="#D9D9D9"/>
<path d="M400 312C400 338.264 394.827 364.272 384.776 388.537C374.725 412.802 359.993 434.85 341.421 453.421C322.85 471.993 300.802 486.725 276.537 496.776C252.272 506.827 226.264 512 200 512C173.736 512 147.728 506.827 123.463 496.776C99.1982 486.725 77.1504 471.993 58.5786 453.421C40.0069 434.85 25.275 412.802 15.2241 388.537C5.17315 364.272 -2.2961e-06 338.264 0 312L40.7078 312C40.7078 332.919 44.828 353.632 52.8332 372.958C60.8383 392.285 72.5717 409.845 87.3634 424.637C102.155 439.428 119.715 451.162 139.041 459.167C158.368 467.172 179.081 471.292 200 471.292C220.919 471.292 241.632 467.172 260.958 459.167C280.285 451.162 297.845 439.428 312.637 424.637C327.428 409.845 339.162 392.285 347.167 372.959C355.172 353.632 359.292 332.919 359.292 312H400Z" fill="#D9D9D9"/>
<path d="M0 20.5C0 9.17816 9.17816 0 20.5 0V0C31.8218 0 41 9.17816 41 20.5V310.5C41 321.822 31.8218 331 20.5 331V331C9.17816 331 0 321.822 0 310.5V20.5Z" fill="#D9D9D9"/>
<path d="M359 20.5C359 9.17816 368.178 0 379.5 0V0C390.822 0 400 9.17816 400 20.5V310.5C400 321.822 390.822 331 379.5 331V331C368.178 331 359 321.822 359 310.5V20.5Z" fill="#D9D9D9"/>
<path d="M362.06 10.2806C363.767 5.02805 369.408 2.15354 374.661 3.86019V3.86019C379.913 5.56684 382.788 11.2084 381.081 16.4609L284.977 312.239C283.27 317.492 277.629 320.367 272.376 318.66V318.66C267.123 316.953 264.249 311.312 265.956 306.059L362.06 10.2806Z" fill="#D9D9D9"/>
<path d="M378.96 17.9405C377.254 12.688 380.128 7.04642 385.381 5.33976V5.33976C390.633 3.63311 396.275 6.50762 397.981 11.7602L494.086 307.539C495.792 312.791 492.918 318.433 487.665 320.139V320.139C482.413 321.846 476.771 318.972 475.064 313.719L378.96 17.9405Z" fill="#D9D9D9"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

View File

@@ -1,120 +0,0 @@
"use client";
import { Code } from "@unsend/ui/src/code";
const jsCode = `const requestOptions = {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer us_1a2b3c4d5e6f7f8g"
},
body: JSON.stringify({
"to": "test@company.com",
"from": "hello@unsend.dev",
"subject": "Unsend email",
"html": "<p>Unsend is the best open source product to send emails</p>"
}),
};
fetch("http://unsend.dev/api/v1/emails", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.error(error));
`;
const pythonCode = `import requests
import json
url = "http://unsend.dev/api/v1/emails"
payload = json.dumps({
"to": "test@company.com",
"from": "hello@unsend.dev",
"subject": "Unsend email",
"html": "<p>Unsend is the best open source product to send emails</p>"
})
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer us_1a2b3c4d5e6f7f8g'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)`;
const rubyCode = `require 'uri'
require 'net/http'
require 'json'
url = URI("http://unsend.dev/api/v1/emails")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Accept"] = 'application/json'
request["Content-Type"] = 'application/json'
request["Authorization"] = 'Bearer us_1a2b3c4d5e6f7f8g'
request.body = JSON.dump({
"to" => "test@company.com",
"from" => "hello@unsend.dev",
"subject" => "Unsend email",
"html" => "<p>Unsend is the best open source product to send emails</p>"
})
response = http.request(request)
puts response.read_body`;
const phpCode = `$url = "http://unsend.dev/api/v1/emails";
$payload = json_encode(array(
"to" => "test@company.com",
"from" => "hello@unsend.dev",
"subject" => "Unsend email",
"html" => "<p>Unsend is the best open source product to send emails</p>"
));
$headers = array(
"Accept: application/json",
"Content-Type: application/json",
"Authorization: Bearer us_1a2b3c4d5e6f7f8g"
);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
} else {
echo $response;
}`;
const cUrl = `curl --location 'https://unsend.dev/v1/emails' \\
--header 'Accept: application/json' \\
--header 'Content-Type: application/json' \\
--header 'Authorization: Bearer us_44c1071bd30058322f89a09805522d7341a47b5e' \\
--data-raw '{
"to": "test@company.com",
"from": "hello@unsend.dev",
"subject": "Unsend email",
"html": "<p>Unsend is the best open source product to send emails</p>",
}'`;
export default function IntegrationCode() {
return (
<Code
codeBlocks={[
{ language: "js", code: jsCode },
{ language: "ruby", code: rubyCode },
{ language: "php", code: phpCode },
{ language: "python", code: pythonCode },
{ language: "curl", code: cUrl },
]}
codeClassName="h-[500px] "
/>
);
}

View File

@@ -1,38 +0,0 @@
"use client";
import { Editor } from "@unsend/email-editor";
import { Button } from "@unsend/ui/src/button";
import { useState } from "react";
export default function EditorPage() {
const [json, setJson] = useState<Record<string, any>>({
type: "doc",
content: [],
});
const onConvertToHtml = async () => {
console.log(json)
const resp = await fetch("http://localhost:3000/api/to-html", {
method: "POST",
body: JSON.stringify(json),
});
const respJson = await resp.json();
console.log(respJson);
};
return (
<div className=" max-w-2xl mx-auto">
<h1 className="text-center text-2xl py-8">
Try out unsend's email editor
</h1>
<div className="flex flex-col gap-4">
<Button className="w-[200px]" onClick={onConvertToHtml}>
Convert to HTML
</Button>
<Editor onUpdate={(editor) => setJson(editor.getJSON())} />
</div>
</div>
);
}

View File

@@ -1,33 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}

View File

@@ -1,149 +1,38 @@
import "@unsend/ui/styles/globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { ThemeProvider } from "@unsend/ui";
import Script from "next/script";
import Link from "next/link";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
import Image from "next/image";
import { DocumentChartBarIcon } from "@heroicons/react/24/solid";
import { Book } from "lucide-react";
import { Separator } from "@unsend/ui/src/separator";
import "@usesend/ui/styles/globals.css";
const inter = Inter({ subsets: ["latin"] });
import { Inter } from "next/font/google";
import { JetBrains_Mono } from "next/font/google";
import type { Metadata } from "next";
import { ThemeProvider } from "@usesend/ui";
const inter = Inter({
subsets: ["latin"],
variable: "--font-sans",
});
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-mono",
});
export const metadata: Metadata = {
title: "Unsend",
description: "Open source sending infrastructure for developers",
title: "useSend - Open source email platform",
description: "Open source email platform for everyone",
icons: [{ rel: "icon", url: "/favicon.ico" }],
twitter: {
card: "summary_large_image",
site: "https://unsend.dev",
title: "Unsend",
description: "Open source sending infrastructure for developers",
images: "https://unsend.dev/og_banner.png",
creator: "@KM_Koushik_",
},
openGraph: {
type: "website",
title: "Unsend",
description: "Open source sending infrastructure for developers",
siteName: "Unsend",
url: "https://unsend.dev",
images: "https://unsend.dev/og_banner.png",
},
};
export default function RootLayout({
children,
}: Readonly<{
}: {
children: React.ReactNode;
}>) {
}) {
return (
<html lang="en">
{process.env.NODE_ENV === "production" && (
<Script src="https://scripts.simpleanalyticscdn.com/latest.js" />
)}
<body className={inter.className}>
<ThemeProvider attribute="class" defaultTheme="dark">
<div className="bg-[#0c0e12] pb-20 h-full">
<div className=" mx-auto w-full lg:max-w-6xl relative flex flex-col ">
<nav className="p-4 flex justify-between">
<div className="text-2xl font-semibold">
<Link href="/">Unsend</Link>
</div>
<div className="flex gap-8 items-center">
<Link href="https://docs.unsend.dev" target="_blank" className="flex items-center gap-1 bg-border/70 text-white px-3 py-1 rounded-full">
<Book className="h-6 w-6 fill-white stroke-border" /> Docs
</Link>
<Link
href="https://github.com/unsend-dev/unsend"
target="_blank"
<html lang="en" suppressHydrationWarning className="bg-sidebar-background">
<body
className={`font-sans ${inter.variable} ${jetbrainsMono.variable} bg-sidebar-background`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
className="h-6 w-6 stroke-white fill-white"
>
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
</svg>
</Link>
<Link href="https://twitter.com/unsend_dev" target="_blank">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="h-6 w-6 stroke-white fill-white"
target="_blank"
>
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
</svg>
</Link>
<Link
href="https://discord.gg/BU8n8pJv8S"
target="_blank"
className="flex gap-2 items-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
className="h-6 w-6 stroke-white fill-white"
>
<path d="M524.5 69.8a1.5 1.5 0 0 0 -.8-.7A485.1 485.1 0 0 0 404.1 32a1.8 1.8 0 0 0 -1.9 .9 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.1-30.6 1.9 1.9 0 0 0 -1.9-.9A483.7 483.7 0 0 0 116.1 69.1a1.7 1.7 0 0 0 -.8 .7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 0 0 .8 1.4A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.1-.7A348.2 348.2 0 0 0 208.1 430.4a1.9 1.9 0 0 0 -1-2.6 321.2 321.2 0 0 1 -45.9-21.9 1.9 1.9 0 0 1 -.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8 0 0 1 1.9-.3c96.2 43.9 200.4 43.9 295.5 0a1.8 1.8 0 0 1 1.9 .2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9 0 0 1 -.2 3.1 301.4 301.4 0 0 1 -45.9 21.8 1.9 1.9 0 0 0 -1 2.6 391.1 391.1 0 0 0 30 48.8 1.9 1.9 0 0 0 2.1 .7A486 486 0 0 0 610.7 405.7a1.9 1.9 0 0 0 .8-1.4C623.7 277.6 590.9 167.5 524.5 69.8zM222.5 337.6c-29 0-52.8-26.6-52.8-59.2S193.1 219.1 222.5 219.1c29.7 0 53.3 26.8 52.8 59.2C275.3 311 251.9 337.6 222.5 337.6zm195.4 0c-29 0-52.8-26.6-52.8-59.2S388.4 219.1 417.9 219.1c29.7 0 53.3 26.8 52.8 59.2C470.7 311 447.5 337.6 417.9 337.6z" />
</svg>
</Link>
{/* <Link href="https://github.com/unsendhq">GitHub</Link> */}
</div>
</nav>
</div>
<div className="max-w-6xl mx-auto px-4">{children}</div>
<div className="flex justify-between mt-20 max-w-6xl mx-auto px-4 md:px-0 pb-10">
<div className="flex gap-2 items-center">
<TextWithCopyButton value="hello@unsend.dev" />
</div>
<div className="flex gap-8 items-center">
<Link href="https://docs.unsend.dev">docs</Link>
<Separator orientation="vertical" />
<Link href="/terms">terms</Link>
<Link href="/privacy">privacy</Link>
<Link
href="https://github.com/unsend-dev/unsend"
target="_blank"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
className="h-6 w-6 stroke-white fill-white"
>
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
</svg>
</Link>
<Link href="https://twitter.com/unsend_dev" target="_blank">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="h-6 w-6 stroke-white fill-white"
target="_blank"
>
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" />
</svg>
</Link>
<Link
href="https://discord.gg/BU8n8pJv8S"
target="_blank"
className="flex gap-2 items-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
className="h-6 w-6 stroke-white fill-white"
>
<path d="M524.5 69.8a1.5 1.5 0 0 0 -.8-.7A485.1 485.1 0 0 0 404.1 32a1.8 1.8 0 0 0 -1.9 .9 337.5 337.5 0 0 0 -14.9 30.6 447.8 447.8 0 0 0 -134.4 0 309.5 309.5 0 0 0 -15.1-30.6 1.9 1.9 0 0 0 -1.9-.9A483.7 483.7 0 0 0 116.1 69.1a1.7 1.7 0 0 0 -.8 .7C39.1 183.7 18.2 294.7 28.4 404.4a2 2 0 0 0 .8 1.4A487.7 487.7 0 0 0 176 479.9a1.9 1.9 0 0 0 2.1-.7A348.2 348.2 0 0 0 208.1 430.4a1.9 1.9 0 0 0 -1-2.6 321.2 321.2 0 0 1 -45.9-21.9 1.9 1.9 0 0 1 -.2-3.1c3.1-2.3 6.2-4.7 9.1-7.1a1.8 1.8 0 0 1 1.9-.3c96.2 43.9 200.4 43.9 295.5 0a1.8 1.8 0 0 1 1.9 .2c2.9 2.4 6 4.9 9.1 7.2a1.9 1.9 0 0 1 -.2 3.1 301.4 301.4 0 0 1 -45.9 21.8 1.9 1.9 0 0 0 -1 2.6 391.1 391.1 0 0 0 30 48.8 1.9 1.9 0 0 0 2.1 .7A486 486 0 0 0 610.7 405.7a1.9 1.9 0 0 0 .8-1.4C623.7 277.6 590.9 167.5 524.5 69.8zM222.5 337.6c-29 0-52.8-26.6-52.8-59.2S193.1 219.1 222.5 219.1c29.7 0 53.3 26.8 52.8 59.2C275.3 311 251.9 337.6 222.5 337.6zm195.4 0c-29 0-52.8-26.6-52.8-59.2S388.4 219.1 417.9 219.1c29.7 0 53.3 26.8 52.8 59.2C470.7 311 447.5 337.6 417.9 337.6z" />
</svg>
</Link>
{/* <Link href="https://github.com/unsendhq">GitHub</Link> */}
</div>
</div>
</div>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
{children}
</ThemeProvider>
</body>
</html>

View File

@@ -1,189 +1,27 @@
import { EnvelopeIcon, MegaphoneIcon } from "@heroicons/react/24/solid";
import {
Heading1,
Heading2,
Heading3,
AlignLeft,
AlignRight,
AlignCenter,
Bold,
Italic,
ListOrdered,
} from "lucide-react";
import { formatDate } from "date-fns";
import Image from "next/image";
import { GitHubStarsButton } from "~/components/GitHubStarsButton";
import IntegrationCode from "./IntegrationCode";
import {
GitHubStarButton,
HeroImage,
JoinWaitlist,
} from "~/components/landind-page";
export default function Home() {
export default function Page() {
return (
<div className="pb-20">
<div className=" mx-auto w-full lg:max-w-6xl relative flex flex-col ">
<div className="p-4 mt-20">
<h1 className="relative z-10 text-neutral-100 text-2xl lg:max-w-4xl mx-auto md:text-6xl md:leading-[4.5rem] text-center font-sans font-bold">
Open source sending infrastructure for{" "}
<span className="bg-clip-text text-transparent bg-gradient-to-r from-[#06b6d4] to-[#10b981]">
developers
</span>
</h1>
<p></p>
<p className="text-neutral-100 text-lg max-w-lg mx-auto my-4 text-center relative z-10">
Send transactional, marketing emails, SMSes and push notifications
effortlessly.
<main className="min-h-screen flex items-center justify-center p-6">
<div className="max-w-2xl text-center space-y-4">
<div className="flex justify-center">
<Image
src="/logo-squircle.png"
alt="useSend logo"
width={64}
height={64}
priority
/>
</div>
<h1 className="text-xl font-mono font-medium text-blue">useSend</h1>
<p className="text-muted-foreground">
Open source email platform for everyone
</p>
<div className="flex flex-col items-center lg:flex-row justify-center mt-16 gap-8">
<JoinWaitlist />
<GitHubStarButton />
</div>
<HeroImage />
</div>
{/* <BackgroundBeams /> */}
</div>
<div className="w-full lg:max-w-6xl mx-auto flex flex-col gap-40 mt-40 md:px-6 px-0">
<div className="space-y-12">
<div>
<p className="text-center font-semibold text-3xl lg:text-6xl">
Reach your users</p>
</div>
<div className="flex gap-10 flex-col lg:flex-row md:p-8 p-3 bg-[#0c0e12] rounded-lg">
<div className="lg:w-1/2">
<div className="flex flex-col gap-2">
<EnvelopeIcon className="h-10 w-10 text-fuchsia-500" />
<p className="text-3xl font-semibold">Transactional Mail</p>
</div>
<ul className="flex flex-col gap-4 mt-8">
<li>Simple to use! No wasted time on configuration.</li>
<li>Send emails that reach the inbox, not spam.</li>
<li>Get notified of email bounces and complaints.</li>
</ul>
</div>
<div className="lg:w-1/2 flex flex-col bg-[#0e1217] border rounded-lg p-8">
<div className=" border-l border-dashed flex flex-col gap-8">
<div className="flex flex-col gap-4">
<div className="flex gap-5 items-start">
<div className=" -ml-2.5">
<div
className={`flex justify-center items-center p-1.5 bg-gray-600/50 rounded-full`}
>
<div className={`h-2 w-2 rounded-full bg-gray-600`}></div>
</div>
</div>
<div className="-mt-1 ">
<div className=" capitalize font-medium">
<div
className={` text-center w-[130px] rounded capitalize py-1 text-xs bg-gray-400/10 text-gray-400 border-gray-400/10`}
>
Sent
</div>
</div>
<div
className="text-xs text-muted-foreground mt-2"
suppressHydrationWarning
>
{formatDate(Date.now() - 100000, "MMM dd, hh:mm a")}
</div>
<div className="mt-1 text-primary/80">
We received your request and sent the email to recipient's
server.
</div>
</div>
</div>
</div>
<div>
<div className="flex gap-5 items-start">
<div className="-ml-2.5">
<div
className={`flex justify-center items-center p-1.5 bg-emerald-500/50 rounded-full`}
>
<div
className={`h-2 w-2 rounded-full bg-emerald-500`}
></div>
</div>
</div>
<div className="-mt-1">
<div className=" capitalize font-medium">
<div
className={` text-center w-[130px] rounded capitalize py-1 text-xs bg-emerald-500/10 text-emerald-500 border-emerald-600/10`}
>
Delivered
</div>
</div>
<div
className="text-xs text-muted-foreground mt-2"
suppressHydrationWarning
>
{formatDate(new Date(), "MMM dd, hh:mm a")}
</div>
<div className="mt-1 text-primary/80">
Mail is successfully delivered to the recipient.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="flex gap-10 flex-col lg:flex-row md:p-8 p-3 bg-[#0c0e12] rounded-lg">
<div className="lg:w-1/2">
<div className="flex flex-col gap-2">
<MegaphoneIcon className="h-10 w-10 text-indigo-500" />
<p className="text-3xl font-semibold">Marketing Mail</p>
</div>
<ul className="flex flex-col gap-4 mt-8">
<li>Manage newsletters, changelogs, and broadcasts easily.</li>
<li>
Use our no-code email builder and templates that works on all
email clients.
</li>
<li>Measure engagement using click and open tracking.</li>
<li>
Focus on the content and we will handle the subscription for
you.
</li>
</ul>
</div>
<div className="lg:w-1/2">
<div className="w-full rounded-lg border bg-[#0e1217]">
<div className="flex gap-4 justify-between border-b p-4 overflow-auto">
<Heading1 />
<Heading2 />
<Heading3 />
<AlignLeft />
<AlignCenter />
<AlignRight />
<Bold />
<Italic />
<ListOrdered />
</div>
<div className="h-[200px] p-4">
<div className="">
<div className="text-xl text-center">Welcome to unsend!</div>
<p className="text-center mt-8">
Finally an open source alternative for Resend, Mailgun,
Sendgrid and postmark.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="mt-20 rounded-lg md:p-8 p-3">
<p className="text-center font-semibold text-3xl lg:text-6xl ">
Integrate in minutes
</p>
<div className="mt-10">
<IntegrationCode />
</div>
</div>
<div className="mt-6 flex items-center justify-center gap-3">
<GitHubStarsButton />
</div>
</div>
</main>
);
}

View File

@@ -1,154 +0,0 @@
import React from "react";
const PrivacyPolicy = () => {
return (
<div className="mx-auto mt-20">
<div className="flex flex-col gap-12">
<div>
<h1 className="text-2xl font-semibold">Privacy Policy</h1>
<p className="mb-4 text-muted-foreground">
Last Updated: Aug 22, 2024
</p>
<p className="mb-4 text-primary">
Unsend is committed to protecting your privacy. This Privacy Policy
outlines how we collect, use, and disclose your information when you
use Unsend.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">
1. Information We Collect
</h2>
<h3 className="text-lg font-semibold mt-4 mb-2">
Personal Information
</h3>
<p className="mb-4 opacity-90 ">
When you create an account, we collect your email address and name.
</p>
<h3 className="text-lg font-semibold mt-4 mb-2">Usage Data</h3>
<p className="mb-4 opacity-90 ">
We automatically collect information about how you interact with the
Service, such as pages visited and features used.
</p>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">
2. How We Use Your Information
</h2>
<p className="mb-4 opacity-90 ">
We use your information for the following purposes:
</p>
<ul className="list-disc list-inside mb-4 opacity-90">
<li>To provide and maintain the Service</li>
<li>To improve and personalize your experience with the Service</li>
<li>
To communicate with you about updates, promotions, and customer
support
</li>
</ul>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">
3. Sharing Your Information
</h2>
<p className="mb-4 opacity-90">
We do not sell, rent or your personal information with third
parties.
</p>
<p className="mb-4 opacity-90">
We are using following third party services to run this service.
</p>
<ul className="list-disc list-inside mb-4 opacity-90 gap-2 flex flex-col mt-2">
<li>
<a
href="https://railway.app/"
className=" underline"
target="_blank"
rel="noopener noreferrer"
>
Railway
</a>
: this is where unsend is hosted. currently in US region
</li>
<li>
<a
href="https://aws.amazon.com/ses/"
className=" underline"
target="_blank"
rel="noopener noreferrer"
>
AWS
</a>
: unsend uses AWS SES to process your mails
</li>
</ul>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">4. Data Security</h2>
<p className="mb-4 opacity-90">
We take reasonable steps to protect your information from
unauthorized access, use, or disclosure. However, no method of
transmission or storage is completely secure, and we cannot
guarantee the absolute security of your information.
</p>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">5. Data Retention</h2>
<p className="mb-4 opacity-90">
We retain your personal information for as long as necessary to
provide the Service, comply with legal obligations, resolve
disputes, and enforce our agreements.
</p>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">6. Your Rights</h2>
<p className="mb-4 opacity-90">
You may access, update, or request the deletion of your personal
information by contacting us at hello@unsend.dev.
</p>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">7. Children's Privacy</h2>
<p className="mb-4 opacity-90">
The Service is not intended for users under 13 years old. We do not
knowingly collect personal information from children under 13. If
you are a parent or guardian and believe your child has provided us
with personal information, please contact us at hello@unsend.dev.
</p>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">
8. Changes to This Policy
</h2>
<p className="mb-4 opacity-90">
We may update this Policy from time to time. We will notify you of
any changes by posting the updated Policy on this page. By
continuing to use the Service, you agree to be bound by the updated
Policy.
</p>
</div>
<div>
<h2 className="text-2xl font-semibold mb-4">9. Contact</h2>
<p className="mb-4 opacity-90">
If you have any questions or concerns regarding this Privacy Policy,
please contact us at hello@unsend.dev.
</p>
</div>
</div>
</div>
);
};
export default PrivacyPolicy;

View File

@@ -1,97 +0,0 @@
import React from "react";
const TermsOfService = () => {
return (
<div className="mx-auto mt-20">
<div className="flex flex-col gap-12">
<div>
<h1 className="text-2xl font-semibold">Terms of Service</h1>
<p className="mb-4 text-muted-foreground">
Last Updated: Apr 22, 2024
</p>
<p className="mb-4 text-primary">
By using Unsend, you agree to these Terms of Service. Unsend
reserves the right to modify these Terms at any time. By continuing
to use the Service, you agree to the updated Terms.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">1. Agreement</h2>
<p className="mb-4 opacity-90">
By using Unsend, you agree to these Terms of Service. Unsend
reserves the right to modify these Terms at any time. By continuing
to use the Service, you agree to the updated Terms.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">2. Eligibility</h2>
<p className="mb-4 opacity-90">
You must be at least 13 years old to use the Service. By using the
Service, you represent that you meet this age requirement.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">3. Acceptable Use</h2>
<p className="mb-4 opacity-90">
You agree not to use the Service for any illegal or harmful
activities. We reserve the right to terminate your access to the
Service if you violate this provision.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">4. Termination</h2>
<p className="mb-4 opacity-90">
We reserve the right to suspend or terminate your access to the
Service at any time, with or without notice, for any reason.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">
5. Disclaimers and Limitation of Liability
</h2>
<p className="mb-4 opacity-90">
The Service is provided "as is" and "as available," without
warranties of any kind. We disclaim all liability for any damages or
losses arising from your use of the Service.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">6. Governing Law</h2>
<p className="mb-4 opacity-90">
These Terms shall be governed by the laws of US. Any disputes
arising from these Terms shall be resolved in the courts located in
US.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">7. Privacy</h2>
<p className="mb-4 opacity-90">
Please read our{" "}
<a className="underline" href="/privacy">
privacy policy
</a>
.
</p>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">8. Contact</h2>
<p className="mb-4 opacity-90">
If you have any questions or concerns regarding these Terms, please
contact us at hello@unsend.dev.
</p>
</div>
</div>
</div>
);
};
export default TermsOfService;

View File

@@ -0,0 +1,73 @@
"use client";
import { useEffect, useState } from "react";
import { Button } from "@usesend/ui/src/button";
const REPO = "unsend-dev/unsend";
const REPO_URL = `https://github.com/${REPO}`;
const API_URL = `https://api.github.com/repos/${REPO}`;
export function GitHubStarsButton() {
const [stars, setStars] = useState<number | null>(null);
useEffect(() => {
let cancelled = false;
async function load() {
try {
const res = await fetch(API_URL, {
headers: {
Accept: "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
},
cache: "no-store",
});
if (!res.ok) return;
const json = await res.json();
if (!cancelled && typeof json.stargazers_count === "number") {
setStars(json.stargazers_count);
}
} catch (err) {
// ignore network errors; keep placeholder
}
}
load();
return () => {
cancelled = true;
};
}, []);
const formatted = stars?.toLocaleString() ?? "—";
return (
<Button variant="outline" size="lg" className="px-4 gap-2">
<a
href={REPO_URL}
target="_blank"
rel="noopener noreferrer"
aria-label="Star this repo on GitHub"
className="flex items-center gap-2"
>
<GitHubIcon className="h-4 w-4" />
<span>Star on GitHub</span>
</a>
</Button>
);
}
function GitHubIcon({ className = "" }: { className?: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
className={className}
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M12 2C6.477 2 2 6.486 2 12.021c0 4.424 2.865 8.175 6.839 9.5.5.093.682-.217.682-.483 0-.237-.009-.864-.014-1.696-2.782.605-3.369-1.343-3.369-1.343-.454-1.155-1.11-1.463-1.11-1.463-.907-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.833.091-.647.35-1.088.636-1.338-2.221-.253-4.555-1.113-4.555-4.952 0-1.093.39-1.987 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.027A9.564 9.564 0 0 1 12 6.844a9.56 9.56 0 0 1 2.504.337c1.909-1.297 2.748-1.027 2.748-1.027.545 1.378.202 2.397.1 2.65.64.701 1.028 1.595 1.028 2.688 0 3.848-2.337 4.697-4.565 4.945.36.31.68.921.68 1.856 0 1.339-.012 2.418-.012 2.747 0 .268.18.581.688.482C19.138 20.193 22 16.443 22 12.02 22 6.486 17.523 2 12 2z"
clipRule="evenodd"
/>
</svg>
);
}

View File

@@ -1,68 +0,0 @@
"use client";
import { DocumentIcon } from "@heroicons/react/24/solid";
import { RocketLaunchIcon } from "@heroicons/react/24/solid";
import { motion } from "framer-motion";
import Image from "next/image";
export function JoinWaitlist() {
return (
<motion.a
className="bg-white text-black py-2 px-6 rounded-full cursor-pointer flex justify-center gap-2 w-[220px]"
whileHover={{ scale: 1.05 }}
transition={{ type: "spring", stiffness: 400, damping: 10 }}
href="https://app.youform.io/forms/caja89vr"
target="_blank"
>
<RocketLaunchIcon className="h-6 w-6" />
Join the waitlist
</motion.a>
);
}
export function GitHubStarButton() {
return (
<motion.a
className="border-white border py-2 px-6 rounded-full justify-center cursor-pointer flex gap-2 w-[220px]"
whileHover={{ scale: 1.05 }}
transition={{ type: "spring", stiffness: 400, damping: 10 }}
href="https://github.com/unsend-dev/unsend"
target="_blank"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
className="h-6 w-6 stroke-white fill-white"
>
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
</svg>
Star us on github
</motion.a>
);
}
export function HeroImage() {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{
duration: 0.3,
damping: 15,
stiffness: 100,
type: "spring",
}}
viewport={{ once: true }}
className="p-3 bg-[#0c0e12] mt-24 rounded-xl mx-2 shadow-[#1e293b]/70 shadow-md border-2 border-border/70"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/app.webp"
alt="App"
width={1200}
height={800}
className="rounded-lg relative"
/>
</motion.div>
);
}

View File

@@ -1,149 +0,0 @@
"use client";
import React from "react";
import { motion } from "framer-motion";
import { cn } from "@unsend/ui/lib/utils";
export const BackgroundBeams = React.memo(
({ className }: { className?: string }) => {
const paths = [
"M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875",
"M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867",
"M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859",
"M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851",
"M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843",
"M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835",
"M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827",
"M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819",
"M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811",
"M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803",
"M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795",
"M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787",
"M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779",
"M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771",
"M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763",
"M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755",
"M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747",
"M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739",
"M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731",
"M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723",
"M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715",
"M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707",
"M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699",
"M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691",
"M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683",
"M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675",
"M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667",
"M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659",
"M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651",
"M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643",
"M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635",
"M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627",
"M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619",
"M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611",
"M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603",
"M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595",
"M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587",
"M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579",
"M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571",
"M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563",
"M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555",
"M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547",
"M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539",
"M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531",
"M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523",
"M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515",
"M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507",
"M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499",
"M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491",
"M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483",
];
return (
<div
className={cn(
"absolute h-full w-full inset-0 [mask-size:40px] [mask-repeat:no-repeat] flex items-center justify-center",
className
)}
>
<svg
className=" z-0 h-full w-full pointer-events-none absolute "
width="100%"
height="100%"
viewBox="0 0 696 316"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M-380 -189C-380 -189 -312 216 152 343C616 470 684 875 684 875M-373 -197C-373 -197 -305 208 159 335C623 462 691 867 691 867M-366 -205C-366 -205 -298 200 166 327C630 454 698 859 698 859M-359 -213C-359 -213 -291 192 173 319C637 446 705 851 705 851M-352 -221C-352 -221 -284 184 180 311C644 438 712 843 712 843M-345 -229C-345 -229 -277 176 187 303C651 430 719 835 719 835M-338 -237C-338 -237 -270 168 194 295C658 422 726 827 726 827M-331 -245C-331 -245 -263 160 201 287C665 414 733 819 733 819M-324 -253C-324 -253 -256 152 208 279C672 406 740 811 740 811M-317 -261C-317 -261 -249 144 215 271C679 398 747 803 747 803M-310 -269C-310 -269 -242 136 222 263C686 390 754 795 754 795M-303 -277C-303 -277 -235 128 229 255C693 382 761 787 761 787M-296 -285C-296 -285 -228 120 236 247C700 374 768 779 768 779M-289 -293C-289 -293 -221 112 243 239C707 366 775 771 775 771M-282 -301C-282 -301 -214 104 250 231C714 358 782 763 782 763M-275 -309C-275 -309 -207 96 257 223C721 350 789 755 789 755M-268 -317C-268 -317 -200 88 264 215C728 342 796 747 796 747M-261 -325C-261 -325 -193 80 271 207C735 334 803 739 803 739M-254 -333C-254 -333 -186 72 278 199C742 326 810 731 810 731M-247 -341C-247 -341 -179 64 285 191C749 318 817 723 817 723M-240 -349C-240 -349 -172 56 292 183C756 310 824 715 824 715M-233 -357C-233 -357 -165 48 299 175C763 302 831 707 831 707M-226 -365C-226 -365 -158 40 306 167C770 294 838 699 838 699M-219 -373C-219 -373 -151 32 313 159C777 286 845 691 845 691M-212 -381C-212 -381 -144 24 320 151C784 278 852 683 852 683M-205 -389C-205 -389 -137 16 327 143C791 270 859 675 859 675M-198 -397C-198 -397 -130 8 334 135C798 262 866 667 866 667M-191 -405C-191 -405 -123 0 341 127C805 254 873 659 873 659M-184 -413C-184 -413 -116 -8 348 119C812 246 880 651 880 651M-177 -421C-177 -421 -109 -16 355 111C819 238 887 643 887 643M-170 -429C-170 -429 -102 -24 362 103C826 230 894 635 894 635M-163 -437C-163 -437 -95 -32 369 95C833 222 901 627 901 627M-156 -445C-156 -445 -88 -40 376 87C840 214 908 619 908 619M-149 -453C-149 -453 -81 -48 383 79C847 206 915 611 915 611M-142 -461C-142 -461 -74 -56 390 71C854 198 922 603 922 603M-135 -469C-135 -469 -67 -64 397 63C861 190 929 595 929 595M-128 -477C-128 -477 -60 -72 404 55C868 182 936 587 936 587M-121 -485C-121 -485 -53 -80 411 47C875 174 943 579 943 579M-114 -493C-114 -493 -46 -88 418 39C882 166 950 571 950 571M-107 -501C-107 -501 -39 -96 425 31C889 158 957 563 957 563M-100 -509C-100 -509 -32 -104 432 23C896 150 964 555 964 555M-93 -517C-93 -517 -25 -112 439 15C903 142 971 547 971 547M-86 -525C-86 -525 -18 -120 446 7C910 134 978 539 978 539M-79 -533C-79 -533 -11 -128 453 -1C917 126 985 531 985 531M-72 -541C-72 -541 -4 -136 460 -9C924 118 992 523 992 523M-65 -549C-65 -549 3 -144 467 -17C931 110 999 515 999 515M-58 -557C-58 -557 10 -152 474 -25C938 102 1006 507 1006 507M-51 -565C-51 -565 17 -160 481 -33C945 94 1013 499 1013 499M-44 -573C-44 -573 24 -168 488 -41C952 86 1020 491 1020 491M-37 -581C-37 -581 31 -176 495 -49C959 78 1027 483 1027 483M-30 -589C-30 -589 38 -184 502 -57C966 70 1034 475 1034 475M-23 -597C-23 -597 45 -192 509 -65C973 62 1041 467 1041 467M-16 -605C-16 -605 52 -200 516 -73C980 54 1048 459 1048 459M-9 -613C-9 -613 59 -208 523 -81C987 46 1055 451 1055 451M-2 -621C-2 -621 66 -216 530 -89C994 38 1062 443 1062 443M5 -629C5 -629 73 -224 537 -97C1001 30 1069 435 1069 435M12 -637C12 -637 80 -232 544 -105C1008 22 1076 427 1076 427M19 -645C19 -645 87 -240 551 -113C1015 14 1083 419 1083 419"
stroke="url(#paint0_radial_242_278)"
strokeOpacity="0.05"
strokeWidth="0.5"
></path>
{paths.map((path, index) => (
<motion.path
key={`path-` + index}
d={path}
stroke={`url(#linearGradient-${index})`}
strokeOpacity="0.4"
strokeWidth="0.5"
></motion.path>
))}
<defs>
{paths.map((path, index) => (
<motion.linearGradient
id={`linearGradient-${index}`}
key={`gradient-${index}`}
initial={{
x1: "0%",
x2: "0%",
y1: "0%",
y2: "0%",
}}
animate={{
x1: ["0%", "100%"],
x2: ["0%", "95%"],
y1: ["0%", "100%"],
y2: ["0%", `${93 + Math.random() * 8}%`],
}}
transition={{
duration: Math.random() * 10 + 10,
ease: "easeInOut",
repeat: Infinity,
delay: Math.random() * 10,
}}
>
<stop stopColor="#18CCFC" stopOpacity="0"></stop>
<stop stopColor="#18CCFC"></stop>
<stop offset="32.5%" stopColor="#06b6d4"></stop>
<stop offset="100%" stopColor="#10b981" stopOpacity="0"></stop>
</motion.linearGradient>
))}
<radialGradient
id="paint0_radial_242_278"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(352 34) rotate(90) scale(555 1560.62)"
>
<stop
offset="0.066667"
stopColor="#18CCFC"
stopOpacity="1"
></stop>
<stop
offset="0.243243"
stopColor="#18CCFC"
stopOpacity="0.5"
></stop>
<stop offset="0.43594" stopColor="white" stopOpacity="0.1"></stop>
</radialGradient>
</defs>
</svg>
</div>
);
}
);
BackgroundBeams.displayName = "BackgroundBeams";

View File

@@ -1,61 +0,0 @@
// Input component extends from shadcnui - https://ui.shadcn.com/docs/components/input
"use client";
import * as React from "react";
import { cn } from "@unsend/ui/lib/utils";
import { useMotionTemplate, useMotionValue, motion } from "framer-motion";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const StyledInput = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
const radius = 100; // change this to increase the rdaius of the hover effect
const [visible, setVisible] = React.useState(false);
let mouseX = useMotionValue(0);
let mouseY = useMotionValue(0);
function handleMouseMove({ currentTarget, clientX, clientY }: any) {
let { left, top } = currentTarget.getBoundingClientRect();
mouseX.set(clientX - left);
mouseY.set(clientY - top);
}
return (
<motion.div
style={{
background: useMotionTemplate`
radial-gradient(
${visible ? radius + "px" : "0px"} circle at ${mouseX}px ${mouseY}px,
#06b6d4,
transparent 80%
)
`,
}}
onMouseMove={handleMouseMove}
onMouseEnter={() => setVisible(true)}
onMouseLeave={() => setVisible(false)}
className="p-[2px] rounded-lg transition duration-300 group/input"
>
<input
type={type}
className={cn(
`flex h-10 w-full border-none bg-gray-50 dark:bg-zinc-800 text-black dark:text-white shadow-input rounded-md px-3 py-2 text-sm file:border-0 file:bg-transparent
file:text-sm file:font-medium placeholder:text-neutral-400 dark:placeholder-text-neutral-600
focus-visible:outline-none focus-visible:ring-[2px] focus-visible:ring-neutral-400 dark:focus-visible:ring-neutral-600
disabled:cursor-not-allowed disabled:opacity-50
dark:shadow-[0px_0px_1px_1px_var(--neutral-700)]
group-hover/input:shadow-none transition duration-400
`,
className
)}
ref={ref}
{...props}
/>
</motion.div>
);
}
);
StyledInput.displayName = "Input";
export { StyledInput };

View File

@@ -1,11 +1,13 @@
import { type Config } from "tailwindcss";
import sharedConfig from "@unsend/tailwind-config/tailwind.config";
import sharedConfig from "@usesend/tailwind-config/tailwind.config";
import path from "path";
export default {
...sharedConfig,
content: [
"./src/**/*.tsx",
`${path.join(require.resolve("@unsend/ui"), "..")}/**/*.{ts,tsx}`,
`${path.join(require.resolve("@usesend/ui"), "..")}/**/*.{ts,tsx}`,
`${path.join(require.resolve("@usesend/email-editor"), "..")}/**/*.{ts,tsx}`,
],
} satisfies Config;

View File

@@ -1,5 +1,5 @@
{
"extends": "@unsend/typescript-config/nextjs.json",
"extends": "@usesend/typescript-config/nextjs.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
@@ -18,7 +18,9 @@
"**/*.tsx",
"**/*.cjs",
"**/*.js",
".next/types/**/*.ts"
".next/types/**/*.ts",
".eslintrc.cjs"
],
"exclude": ["node_modules"]
}

View File

@@ -1,17 +1,17 @@
name: unsend-smtp-server
name: usesend-smtp-server
services:
smtp-server:
container_name: unsend-smtp-server
image: unsend/smtp-proxy:latest
container_name: usesend-smtp-server
image: usesend/smtp-proxy:latest
# Pass necessary environment variables
environment:
SMTP_AUTH_USERNAME: "unsend" # can be anything, just use the same while sending emails
UNSEND_BASE_URL: "https://app.unsend.dev" # your self hosted unsend instance url
SMTP_AUTH_USERNAME: "usesend" # can be anything, just use the same while sending emails
USESEND_BASE_URL: "https://app.usesend.com" # your self hosted useSend instance url
# Uncomment this if you have SSL certificates. port 465 and 2465 will be using SSL
# UNSEND_API_KEY_PATH: "/certs/server.key"
# UNSEND_API_CERT_PATH: "/certs/server.crt"
# USESEND_API_KEY_PATH: "/certs/server.key"
# USESEND_API_CERT_PATH: "/certs/server.crt"
# If you have SSL certificates, mount them here (read-only recommended)
# volumes:

View File

@@ -6,16 +6,21 @@ import { readFileSync, watch, FSWatcher } from "fs";
dotenv.config();
const AUTH_USERNAME = process.env.SMTP_AUTH_USERNAME ?? "unsend";
const UNSEND_BASE_URL = process.env.UNSEND_BASE_URL ?? "https://app.unsend.dev";
const SSL_KEY_PATH = process.env.UNSEND_API_KEY_PATH;
const SSL_CERT_PATH = process.env.UNSEND_API_CERT_PATH;
const AUTH_USERNAME = process.env.SMTP_AUTH_USERNAME ?? "usesend";
const BASE_URL =
process.env.USESEND_BASE_URL ??
process.env.UNSEND_BASE_URL ??
"https://app.usesend.com";
const SSL_KEY_PATH =
process.env.USESEND_API_KEY_PATH ?? process.env.UNSEND_API_KEY_PATH;
const SSL_CERT_PATH =
process.env.USESEND_API_CERT_PATH ?? process.env.UNSEND_API_CERT_PATH;
async function sendEmailToUnsend(emailData: any, apiKey: string) {
async function sendEmailToUseSend(emailData: any, apiKey: string) {
try {
const apiEndpoint = "/api/v1/emails";
const url = new URL(apiEndpoint, UNSEND_BASE_URL); // Combine base URL with endpoint
console.log("Sending email to Unsend API at:", url.href); // Debug statement
const url = new URL(apiEndpoint, BASE_URL); // Combine base URL with endpoint
console.log("Sending email to useSend API at:", url.href); // Debug statement
const emailDataText = JSON.stringify(emailData);
@@ -31,17 +36,17 @@ async function sendEmailToUnsend(emailData: any, apiKey: string) {
if (!response.ok) {
const errorData = await response.text();
console.error(
"Unsend API error response: error:",
"useSend API error response: error:",
JSON.stringify(errorData, null, 4),
`\nemail data: ${emailDataText}`
`\nemail data: ${emailDataText}`,
);
throw new Error(
`Failed to send email: ${errorData || "Unknown error from server"}`
`Failed to send email: ${errorData || "Unknown error from server"}`,
);
}
const responseData = await response.json();
console.log("Unsend API response:", responseData);
console.log("useSend API response:", responseData);
} catch (error) {
if (error instanceof Error) {
console.error("Error message:", error.message);
@@ -69,7 +74,7 @@ const serverOptions: SMTPServerOptions = {
onData(
stream: Readable,
session: SMTPServerSession,
callback: (error?: Error) => void
callback: (error?: Error) => void,
) {
console.log("Receiving email data..."); // Debug statement
simpleParser(stream, (err, parsed) => {
@@ -96,7 +101,7 @@ const serverOptions: SMTPServerOptions = {
replyTo: parsed.replyTo?.text,
};
sendEmailToUnsend(emailObject, session.user)
sendEmailToUseSend(emailObject, session.user)
.then(() => callback())
.then(() => console.log("Email sent successfully to: ", emailObject.to))
.catch((error) => {
@@ -128,7 +133,7 @@ function startServers() {
server.listen(port, () => {
console.log(
`Implicit SSL/TLS SMTP server is listening on port ${port}`
`Implicit SSL/TLS SMTP server is listening on port ${port}`,
);
});

View File

@@ -5,7 +5,7 @@ const transporter = nodemailer.createTransport({
port: 25,
secure: false,
auth: {
user: "unsend",
user: "usesend",
pass: "us_123",
},
tls: {
@@ -17,8 +17,8 @@ const mailOptions = {
to: "sender@example.com",
from: "hello@example.com",
subject: "Testing SMTP",
html: "<strong>THIS IS USING SMTP,</strong><p>Unsend is the best open source sending platform<p><p>check out <a href='https://unsend.dev'>unsend.dev</a>",
text: "hello,\n\nUnsend is the best open source sending platform",
html: "<strong>THIS IS USING SMTP,</strong><p>useSend is the best open source sending platform<p><p>check out <a href='https://usesend.com'>usesend.com</a>",
text: "hello,\n\nuseSend is the best open source sending platform",
};
transporter.sendMail(mailOptions, (error, info) => {

View File

@@ -1,7 +1,7 @@
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: ["@unsend/eslint-config/next.js"],
extends: ["@usesend/eslint-config/next.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,

View File

@@ -34,8 +34,8 @@
"@trpc/next": "^11.1.1",
"@trpc/react-query": "^11.1.1",
"@trpc/server": "^11.1.1",
"@unsend/email-editor": "workspace:*",
"@unsend/ui": "workspace:*",
"@usesend/email-editor": "workspace:*",
"@usesend/ui": "workspace:*",
"bullmq": "^5.51.1",
"chrono-node": "^2.8.0",
"date-fns": "^4.1.0",
@@ -65,7 +65,7 @@
"superjson": "^2.2.2",
"tldts": "^7.0.4",
"ua-parser-js": "^2.0.3",
"unsend": "workspace:*",
"usesend": "workspace:*",
"use-debounce": "^10.0.4",
"zod": "^3.24.3",
"zustand": "^5.0.8"
@@ -82,10 +82,10 @@
"@types/ua-parser-js": "^0.7.39",
"@typescript-eslint/eslint-plugin": "^8.31.0",
"@typescript-eslint/parser": "^8.31.0",
"@unsend/eslint-config": "workspace:*",
"@unsend/tailwind-config": "workspace:*",
"@unsend/typescript-config": "workspace:*",
"eslint": "^9.25.1",
"@usesend/eslint-config": "workspace:*",
"@usesend/tailwind-config": "workspace:*",
"@usesend/typescript-config": "workspace:*",
"eslint": "^8.57.1",
"eslint-config-next": "^15.3.1",
"postcss": "^8.5.3",
"prettier": "^3.5.3",

View File

@@ -0,0 +1,9 @@
-- Add dkimSelector column (nullable) with default 'usesend'
ALTER TABLE "Domain" ADD COLUMN "dkimSelector" TEXT;
-- Set existing rows to 'unsend' to preserve current selector
UPDATE "Domain" SET "dkimSelector" = 'unsend' WHERE "dkimSelector" IS NULL;
-- Set default for new rows to 'usesend'
ALTER TABLE "Domain" ALTER COLUMN "dkimSelector" SET DEFAULT 'usesend';

View File

@@ -181,6 +181,7 @@ model Domain {
clickTracking Boolean @default(false)
openTracking Boolean @default(false)
publicKey String
dkimSelector String? @default("usesend")
dkimStatus String?
spfDetails String?
dmarcAdded Boolean @default(false)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 B

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,14 +0,0 @@
<svg width="650" height="650" viewBox="0 0 650 650" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_43_2" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="650" height="650">
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="black"/>
</mask>
<g mask="url(#mask0_43_2)">
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="white"/>
<path d="M431.817 203.253C435.698 188.755 437.639 181.506 441.477 181.506C445.314 181.506 447.255 188.755 451.136 203.253L481.757 317.625C488.908 344.332 492.483 357.686 488.449 359.37C484.415 361.055 477.433 349.124 463.467 325.262L450.107 302.435C446.265 295.87 444.344 292.588 441.477 292.588C438.61 292.588 436.688 295.87 432.846 302.435L419.486 325.262C405.521 349.124 398.538 361.055 394.504 359.37C390.47 357.686 394.046 344.332 401.196 317.625L431.817 203.253Z" fill="black"/>
<path d="M467.077 364.883C467.077 385.571 463.002 406.057 455.085 425.17C447.168 444.284 435.564 461.651 420.935 476.28C406.306 490.908 388.939 502.513 369.826 510.43C350.712 518.347 330.227 522.422 309.538 522.422C288.85 522.422 268.365 518.347 249.251 510.43C230.138 502.513 212.771 490.908 198.142 476.28C183.513 461.651 171.909 444.284 163.992 425.17C156.075 406.057 152 385.571 152 364.883L202.528 364.883C202.528 378.936 205.296 392.851 210.674 405.834C216.052 418.817 223.934 430.614 233.871 440.551C243.808 450.488 255.604 458.37 268.587 463.748C281.571 469.125 295.486 471.893 309.538 471.893C323.591 471.893 337.506 469.125 350.489 463.748C363.473 458.37 375.269 450.488 385.206 440.551C395.143 430.614 403.025 418.817 408.403 405.834C413.781 392.851 416.549 378.936 416.549 364.883H467.077Z" fill="black"/>
<path d="M152 156.145C152 142.224 163.285 130.938 177.206 130.938V130.938C191.127 130.938 202.412 142.224 202.412 156.145V366.458C202.412 380.379 191.127 391.665 177.206 391.665V391.665C163.285 391.665 152 380.379 152 366.458V156.145Z" fill="black"/>
<path d="M416.665 152.206C416.665 138.285 427.95 127 441.871 127V127C455.792 127 467.077 138.285 467.077 152.206V362.52C467.077 376.441 455.792 387.726 441.871 387.726V387.726C427.95 387.726 416.665 376.441 416.665 362.52V152.206Z" fill="black"/>
<path d="M419.676 139.67C421.558 133.877 427.78 130.708 433.572 132.59V132.59C439.364 134.472 442.534 140.693 440.652 146.485L366.899 373.475C365.017 379.267 358.795 382.437 353.003 380.555V380.555C347.211 378.673 344.041 372.452 345.923 366.659L419.676 139.67Z" fill="black"/>
<path d="M442.916 147.452C441.034 141.66 444.203 135.439 449.996 133.556V133.556C455.788 131.674 462.009 134.844 463.891 140.637L537.645 367.626C539.527 373.418 536.357 379.64 530.565 381.522V381.522C524.772 383.404 518.551 380.234 516.669 374.442L442.916 147.452Z" fill="black"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -1,14 +0,0 @@
<svg width="650" height="650" viewBox="0 0 650 650" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_43_13" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="650" height="650">
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="black"/>
</mask>
<g mask="url(#mask0_43_13)">
<path d="M0 200C0 105.719 0 58.5786 29.2893 29.2893C58.5786 0 105.719 0 200 0H450C544.281 0 591.421 0 620.711 29.2893C650 58.5786 650 105.719 650 200V450C650 544.281 650 591.421 620.711 620.711C591.421 650 544.281 650 450 650H200C105.719 650 58.5786 650 29.2893 620.711C0 591.421 0 544.281 0 450V200Z" fill="black"/>
<path d="M431.817 203.253C435.698 188.755 437.639 181.506 441.477 181.506C445.314 181.506 447.255 188.755 451.136 203.253L481.757 317.625C488.908 344.332 492.483 357.686 488.449 359.37C484.415 361.055 477.433 349.124 463.467 325.262L450.107 302.435C446.265 295.87 444.344 292.588 441.477 292.588C438.61 292.588 436.688 295.87 432.846 302.435L419.486 325.262C405.521 349.124 398.538 361.055 394.504 359.37C390.47 357.686 394.046 344.332 401.196 317.625L431.817 203.253Z" fill="white"/>
<path d="M467.077 364.883C467.077 385.571 463.002 406.057 455.085 425.17C447.168 444.284 435.564 461.651 420.935 476.28C406.306 490.908 388.939 502.513 369.826 510.43C350.712 518.347 330.227 522.422 309.538 522.422C288.85 522.422 268.365 518.347 249.251 510.43C230.138 502.513 212.771 490.908 198.142 476.28C183.513 461.651 171.909 444.284 163.992 425.17C156.075 406.057 152 385.571 152 364.883L202.528 364.883C202.528 378.936 205.296 392.851 210.674 405.834C216.052 418.817 223.934 430.614 233.871 440.551C243.808 450.488 255.604 458.37 268.587 463.748C281.571 469.125 295.486 471.893 309.538 471.893C323.591 471.893 337.506 469.125 350.489 463.748C363.473 458.37 375.269 450.488 385.206 440.551C395.143 430.614 403.025 418.817 408.403 405.834C413.781 392.851 416.549 378.936 416.549 364.883H467.077Z" fill="white"/>
<path d="M152 156.145C152 142.224 163.285 130.938 177.206 130.938V130.938C191.127 130.938 202.412 142.224 202.412 156.145V366.458C202.412 380.379 191.127 391.665 177.206 391.665V391.665C163.285 391.665 152 380.379 152 366.458V156.145Z" fill="white"/>
<path d="M416.665 152.206C416.665 138.285 427.95 127 441.871 127V127C455.792 127 467.077 138.285 467.077 152.206V362.52C467.077 376.441 455.792 387.726 441.871 387.726V387.726C427.95 387.726 416.665 376.441 416.665 362.52V152.206Z" fill="white"/>
<path d="M419.676 139.67C421.558 133.877 427.78 130.708 433.572 132.59V132.59C439.364 134.472 442.534 140.693 440.652 146.485L366.899 373.475C365.017 379.267 358.795 382.437 353.003 380.555V380.555C347.211 378.673 344.041 372.452 345.923 366.659L419.676 139.67Z" fill="white"/>
<path d="M442.916 147.452C441.034 141.66 444.203 135.439 449.996 133.556V133.556C455.788 131.674 462.009 134.844 463.891 140.637L537.645 367.626C539.527 373.418 536.357 379.64 530.565 381.522V381.522C524.772 383.404 518.551 380.234 516.669 374.442L442.916 147.452Z" fill="white"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { Plus } from "lucide-react";
import { useState } from "react";

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { Edit } from "lucide-react";
import { useState } from "react";
@@ -20,13 +20,13 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { api } from "~/trpc/react";
import { Input } from "@unsend/ui/src/input";
import { toast } from "@unsend/ui/src/toaster";
import Spinner from "@unsend/ui/src/spinner";
import { Input } from "@usesend/ui/src/input";
import { toast } from "@usesend/ui/src/toaster";
import Spinner from "@usesend/ui/src/spinner";
import { SesSetting } from "@prisma/client";
const FormSchema = z.object({

View File

@@ -2,12 +2,13 @@
import AddSesConfiguration from "./add-ses-configuration";
import SesConfigurations from "./ses-configurations";
import { H1 } from "@usesend/ui";
export default function ApiKeysPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">Admin</h1>
<H1>Admin</H1>
<AddSesConfiguration />
</div>
<div className="mt-10">

View File

@@ -7,12 +7,12 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { formatDistanceToNow } from "date-fns";
import { api } from "~/trpc/react";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
import EditSesConfiguration from "./edit-ses-configuration";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
export default function SesConfigurations() {
const sesSettingsQuery = api.admin.getSesSettings.useQuery();

View File

@@ -1,10 +1,10 @@
"use client";
import { api } from "~/trpc/react";
import { Spinner } from "@unsend/ui/src/spinner";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Editor } from "@unsend/email-editor";
import { Spinner } from "@usesend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import { Editor } from "@usesend/email-editor";
import { use, useState } from "react";
import { Campaign } from "@prisma/client";
import {
@@ -12,7 +12,7 @@ import {
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
import {
Dialog,
DialogContent,
@@ -20,7 +20,7 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -31,8 +31,8 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
import { toast } from "@unsend/ui/src/toaster";
} from "@usesend/ui/src/form";
import { toast } from "@usesend/ui/src/toaster";
import { useDebouncedCallback } from "use-debounce";
import { formatDistanceToNow } from "date-fns";
import {
@@ -40,7 +40,7 @@ import {
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@unsend/ui/src/accordion";
} from "@usesend/ui/src/accordion";
const sendSchema = z.object({
confirmation: z.string(),
@@ -63,7 +63,7 @@ export default function EditCampaignPage({
{ campaignId },
{
enabled: !!campaignId,
}
},
);
if (isLoading) {
@@ -98,7 +98,7 @@ function CampaignEditor({
const utils = api.useUtils();
const [json, setJson] = useState<Record<string, any> | undefined>(
campaign.content ? JSON.parse(campaign.content) : undefined
campaign.content ? JSON.parse(campaign.content) : undefined,
);
const [isSaving, setIsSaving] = useState(false);
const [name, setName] = useState(campaign.name);
@@ -106,10 +106,10 @@ function CampaignEditor({
const [from, setFrom] = useState(campaign.from);
const [contactBookId, setContactBookId] = useState(campaign.contactBookId);
const [replyTo, setReplyTo] = useState<string | undefined>(
campaign.replyTo[0]
campaign.replyTo[0],
);
const [previewText, setPreviewText] = useState<string | null>(
campaign.previewText
campaign.previewText,
);
const [openSendDialog, setOpenSendDialog] = useState(false);
@@ -135,7 +135,7 @@ function CampaignEditor({
const deboucedUpdateCampaign = useDebouncedCallback(
updateEditorContent,
1000
1000,
);
async function onSendCampaign(values: z.infer<typeof sendSchema>) {
@@ -160,14 +160,14 @@ function CampaignEditor({
onError: (error) => {
toast.error(`Failed to send campaign: ${error.message}`);
},
}
},
);
}
const handleFileChange = async (file: File) => {
if (file.size > IMAGE_SIZE_LIMIT) {
throw new Error(
`File should be less than ${IMAGE_SIZE_LIMIT / 1024 / 1024}MB`
`File should be less than ${IMAGE_SIZE_LIMIT / 1024 / 1024}MB`,
);
}
@@ -194,7 +194,7 @@ function CampaignEditor({
const confirmation = sendForm.watch("confirmation");
const contactBook = contactBooksQuery.data?.find(
(book) => book.id === contactBookId
(book) => book.id === contactBookId,
);
return (
@@ -220,7 +220,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setName(campaign.name);
},
}
},
);
}}
/>
@@ -315,7 +315,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setSubject(campaign.subject);
},
}
},
);
}}
className="mt-1 py-1 text-sm block w-full outline-none border-b border-transparent focus:border-border bg-transparent"
@@ -350,7 +350,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setFrom(campaign.from);
},
}
},
);
}}
/>
@@ -381,7 +381,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setReplyTo(campaign.replyTo[0]);
},
}
},
);
}}
/>
@@ -414,7 +414,7 @@ function CampaignEditor({
toast.error(`${e.message}. Reverting changes.`);
setPreviewText(campaign.previewText ?? "");
},
}
},
);
}}
className="mt-1 py-1 text-sm block w-full outline-none border-b border-transparent bg-transparent focus:border-border"
@@ -440,7 +440,7 @@ function CampaignEditor({
onError: () => {
setContactBookId(campaign.contactBookId);
},
}
},
);
setContactBookId(val);
}}

View File

@@ -7,10 +7,11 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@unsend/ui/src/breadcrumb";
} from "@usesend/ui/src/breadcrumb";
import Link from "next/link";
import { H2 } from "@usesend/ui";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
import { api } from "~/trpc/react";
import { use } from "react";
@@ -80,7 +81,7 @@ export default function CampaignDetailsPage({
</BreadcrumbList>
</Breadcrumb>
<div className="mt-10">
<h2 className="text-xl font-semibold mb-4"> Statistics</h2>
<H2 className="mb-4"> Statistics</H2>
<div className="flex gap-4">
{statusCards.map((card) => (
<div
@@ -110,7 +111,7 @@ export default function CampaignDetailsPage({
{campaign.html && (
<div className=" rounded-lg mt-16">
<h2 className="text-xl font-semibold mb-4">Email</h2>
<H2 className="mb-4">Email</H2>
<div className="p-2 rounded-lg border shadow flex flex-col gap-4 w-full">
<div className="flex flex-col gap-3 px-4 py-1">

View File

@@ -7,11 +7,11 @@ import {
TableHead,
TableBody,
TableCell,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { api } from "~/trpc/react";
import { useUrlState } from "~/hooks/useUrlState";
import { Button } from "@unsend/ui/src/button";
import Spinner from "@unsend/ui/src/spinner";
import { Button } from "@usesend/ui/src/button";
import Spinner from "@usesend/ui/src/spinner";
import { formatDistanceToNow } from "date-fns";
import { CampaignStatus } from "@prisma/client";
import DeleteCampaign from "./delete-campaign";
@@ -22,7 +22,7 @@ import {
SelectTrigger,
SelectContent,
SelectItem,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
export default function CampaignList() {
const [page, setPage] = useUrlState("page", "1");

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -16,7 +16,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -24,9 +24,9 @@ import { Plus } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { useRouter } from "next/navigation";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
const campaignSchema = z.object({
name: z.string({ required_error: "Name is required" }).min(1, {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { Campaign } from "@prisma/client";
const campaignSchema = z.object({

View File

@@ -1,6 +1,6 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Dialog,
DialogContent,
@@ -8,10 +8,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Copy } from "lucide-react";
import { Campaign } from "@prisma/client";

View File

@@ -2,12 +2,13 @@
import CampaignList from "./campaign-list";
import CreateCampaign from "./create-campaign";
import { H1 } from "@usesend/ui";
export default function ContactsPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-bold text-lg">Campaigns</h1>
<H1>Campaigns</H1>
<CreateCampaign />
</div>
<CampaignList />

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Textarea } from "@unsend/ui/src/textarea";
import { Button } from "@usesend/ui/src/button";
import { Textarea } from "@usesend/ui/src/textarea";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -17,7 +17,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -26,7 +26,7 @@ import { useRouter } from "next/navigation";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
const contactsSchema = z.object({
contacts: z.string({ required_error: "Contacts are required" }).min(1, {

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Button } from "@usesend/ui/src/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
import Spinner from "@unsend/ui/src/spinner";
} from "@usesend/ui/src/select";
import Spinner from "@usesend/ui/src/spinner";
import {
Table,
TableBody,
@@ -15,7 +15,7 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { formatDistanceToNow } from "date-fns";
import Image from "next/image";
import { useUrlState } from "~/hooks/useUrlState";
@@ -23,14 +23,14 @@ import { api } from "~/trpc/react";
import { getGravatarUrl } from "~/utils/gravatar-utils";
import DeleteContact from "./delete-contact";
import EditContact from "./edit-contact";
import { Input } from "@unsend/ui/src/input";
import { Input } from "@usesend/ui/src/input";
import { useDebouncedCallback } from "use-debounce";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@unsend/ui/src/tooltip";
} from "@usesend/ui/src/tooltip";
import { UnsubscribeReason } from "@prisma/client";
function getUnsubscribeReason(reason: UnsubscribeReason) {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { Contact } from "@prisma/client";
const contactSchema = z.object({

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -17,7 +17,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
@@ -26,8 +26,8 @@ import { useRouter } from "next/navigation";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { Switch } from "@unsend/ui/src/switch";
import { toast } from "@usesend/ui/src/toaster";
import { Switch } from "@usesend/ui/src/switch";
import { Contact } from "@prisma/client";
const contactSchema = z.object({

View File

@@ -8,20 +8,20 @@ import {
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@unsend/ui/src/breadcrumb";
} from "@usesend/ui/src/breadcrumb";
import Link from "next/link";
import AddContact from "./add-contact";
import ContactList from "./contact-list";
import { TextWithCopyButton } from "@unsend/ui/src/text-with-copy";
import { TextWithCopyButton } from "@usesend/ui/src/text-with-copy";
import { formatDistanceToNow } from "date-fns";
import EmojiPicker, { Theme } from "emoji-picker-react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@unsend/ui/src/popover";
import { Button } from "@unsend/ui/src/button";
import { useTheme } from "@unsend/ui";
} from "@usesend/ui/src/popover";
import { Button } from "@usesend/ui/src/button";
import { useTheme } from "@usesend/ui";
import { use } from "react";
export default function ContactsPage({
@@ -51,7 +51,7 @@ export default function ContactsPage({
...old,
...data,
};
}
},
);
},
onSettled: () => {

View File

@@ -1,19 +1,19 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import { useState } from "react";
import { Plus } from "lucide-react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { useUpgradeModalStore } from "~/store/upgradeModalStore";
import { LimitReason } from "~/lib/constants/plans";

View File

@@ -8,7 +8,7 @@ import EditContactBook from "./edit-contact-book";
import { useRouter } from "next/navigation";
import { motion } from "framer-motion";
import { useUrlState } from "~/hooks/useUrlState";
import { Input } from "@unsend/ui/src/input";
import { Input } from "@usesend/ui/src/input";
import { useDebouncedCallback } from "use-debounce";
export default function ContactBooksList() {

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,10 +9,10 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -25,7 +25,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { ContactBook } from "@prisma/client";
const contactBookSchema = z.object({

View File

@@ -1,14 +1,14 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import {
Form,
FormControl,
@@ -16,14 +16,14 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
import { api } from "~/trpc/react";
import { useState } from "react";
import { Edit } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
const contactBookSchema = z.object({
name: z.string().min(1, { message: "Name is required" }),

View File

@@ -2,12 +2,13 @@
import AddContactBook from "./add-contact-book";
import ContactBooksList from "./contact-books-list";
import { H1 } from "@usesend/ui";
export default function ContactsPage() {
return (
<div>
<div className="flex justify-between items-center">
<h1 className="font-semibold text-xl">Contact books</h1>
<H1>Contact books</H1>
<AddContactBook />
</div>
<ContactBooksList />

View File

@@ -1,9 +1,9 @@
"use client";
import { AppSidebar } from "~/components/AppSideBar";
import { SidebarInset, SidebarTrigger } from "@unsend/ui/src/sidebar";
import { SidebarProvider } from "@unsend/ui/src/sidebar";
import { useIsMobile } from "@unsend/ui/src/hooks/use-mobile";
import { SidebarInset, SidebarTrigger } from "@usesend/ui/src/sidebar";
import { SidebarProvider } from "@usesend/ui/src/sidebar";
import { useIsMobile } from "@usesend/ui/src/hooks/use-mobile";
import { UpgradeModal } from "~/components/payments/UpgradeModal";
export function DashboardLayout({ children }: { children: React.ReactNode }) {

View File

@@ -1,12 +1,12 @@
import React from "react";
import { Tabs, TabsList, TabsTrigger } from "@unsend/ui/src/tabs";
import { Tabs, TabsList, TabsTrigger } from "@usesend/ui/src/tabs";
import { useUrlState } from "~/hooks/useUrlState";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
} from "@unsend/ui/src/select";
} from "@usesend/ui/src/select";
import { api } from "~/trpc/react";
interface DashboardFiltersProps {

View File

@@ -13,8 +13,8 @@ import {
import { EmailStatusIcon } from "../emails/email-status-badge";
import { EmailStatus } from "@prisma/client";
import { api } from "~/trpc/react";
import Spinner from "@unsend/ui/src/spinner";
import { useTheme } from "@unsend/ui";
import Spinner from "@usesend/ui/src/spinner";
import { useTheme } from "@usesend/ui";
import { useColors } from "./hooks/useColors";
interface EmailChartProps {

View File

@@ -1,4 +1,4 @@
import { useTheme } from "@unsend/ui";
import { useTheme } from "@usesend/ui";
export function useColors() {
const { resolvedTheme } = useTheme();

View File

@@ -2,6 +2,7 @@
import EmailChart from "./email-chart";
import DashboardFilters from "./dashboard-filters";
import { H1 } from "@usesend/ui";
import { useUrlState } from "~/hooks/useUrlState";
import { ReputationMetrics } from "./reputation-metrics";
@@ -13,7 +14,7 @@ export default function Dashboard() {
<div>
<div className="w-full">
<div className="flex justify-between items-center mb-10">
<h1 className="font-semibold text-xl">Analytics</h1>
<H1>Analytics</H1>
<DashboardFilters
days={days ?? "7"}
setDays={setDays}

View File

@@ -3,7 +3,7 @@ import {
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@unsend/ui/src/tooltip";
} from "@usesend/ui/src/tooltip";
import {
CheckCircle2,
CheckCircle2Icon,

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,12 +9,12 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import { useState } from "react";
import { CheckIcon, ClipboardCopy, Eye, EyeOff, Plus } from "lucide-react";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
@@ -26,7 +26,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
const apiKeySchema = z.object({
name: z.string({ required_error: "Name is required" }).min(1, {

View File

@@ -7,11 +7,11 @@ import {
TableHead,
TableHeader,
TableRow,
} from "@unsend/ui/src/table";
} from "@usesend/ui/src/table";
import { formatDistanceToNow } from "date-fns";
import { api } from "~/trpc/react";
import DeleteApiKey from "./delete-api-key";
import Spinner from "@unsend/ui/src/spinner";
import Spinner from "@usesend/ui/src/spinner";
export default function ApiList() {
const apiKeysQuery = api.apiKey.getApiKeys.useQuery();

View File

@@ -1,7 +1,7 @@
"use client";
import { Button } from "@unsend/ui/src/button";
import { Input } from "@unsend/ui/src/input";
import { Button } from "@usesend/ui/src/button";
import { Input } from "@usesend/ui/src/input";
import {
Dialog,
DialogContent,
@@ -9,11 +9,11 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@unsend/ui/src/dialog";
} from "@usesend/ui/src/dialog";
import { api } from "~/trpc/react";
import React, { useState } from "react";
import { ApiKey } from "@prisma/client";
import { toast } from "@unsend/ui/src/toaster";
import { toast } from "@usesend/ui/src/toaster";
import { Trash2 } from "lucide-react";
import { z } from "zod";
import { useForm } from "react-hook-form";
@@ -26,7 +26,7 @@ import {
FormItem,
FormLabel,
FormMessage,
} from "@unsend/ui/src/form";
} from "@usesend/ui/src/form";
const apiKeySchema = z.object({
name: z.string(),

Some files were not shown because too many files have changed in this diff Show More