Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/stripe #45

Merged
merged 37 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6eb5675
feat: setup stripe
ItaloMedici Feb 6, 2025
15cbaab
feat: create loggout page
ItaloMedici Feb 6, 2025
a0398e6
feat: create home dialog messages
ItaloMedici Feb 6, 2025
3949300
refactor: remove nickname dialog
ItaloMedici Feb 6, 2025
1bb5b70
feat(stripe): add board rounds limit
ItaloMedici Feb 6, 2025
e8b5f53
feat: create pricing page
ItaloMedici Feb 6, 2025
294939c
feat(stripe): create customer when new user signIn
ItaloMedici Feb 6, 2025
9daa01a
refactor: adjust libs
ItaloMedici Feb 6, 2025
58b906d
refactor: rename use cases
ItaloMedici Feb 6, 2025
e1ee63a
feat: create room is full page
ItaloMedici Feb 6, 2025
f4584d2
style: prettier fix
ItaloMedici Feb 6, 2025
e78ef05
feat(stripe): create plan, subscription and user tables
ItaloMedici Feb 6, 2025
16dbd1a
ci: add stripe env
ItaloMedici Feb 6, 2025
2551cac
Merge remote-tracking branch 'origin/main' into feature/stripe
ItaloMedici Feb 8, 2025
baaff2a
ci: rename env
ItaloMedici Feb 8, 2025
cd0147f
fix: change router path
ItaloMedici Feb 8, 2025
ad7907b
refactor: remove unused font
ItaloMedici Feb 8, 2025
929602a
fix(webhook): rename route
ItaloMedici Feb 8, 2025
74419db
refactor: remove unused prop
ItaloMedici Feb 8, 2025
ee8b5b9
feat: add plan name on jwt
ItaloMedici Feb 8, 2025
5c176a1
feat(stripe): add webhook events handling
ItaloMedici Feb 8, 2025
3defd1f
feat(marketing): improve homepage
ItaloMedici Feb 8, 2025
8a4459a
perf(marketing): change hero image png to webp
ItaloMedici Feb 8, 2025
5afeeed
refactor: adjust join board validation
ItaloMedici Feb 8, 2025
9b9c667
feat: authorize webhook url
ItaloMedici Feb 8, 2025
06e9333
refactor: update exemple .env
ItaloMedici Feb 8, 2025
faea8bd
refactor: add layout suspense boundary
ItaloMedici Feb 10, 2025
bce81dd
refactor: change site url env
ItaloMedici Feb 10, 2025
45267d7
ci: adjust env name
ItaloMedici Feb 10, 2025
ab5d360
refactor: remove console.log
ItaloMedici Feb 10, 2025
54eeb9a
chore: add license
ItaloMedici Feb 10, 2025
9f60faf
chore: add readme
ItaloMedici Feb 10, 2025
deff567
chore: change exemple env
ItaloMedici Feb 10, 2025
86a0aab
chore: adjust exemple env
ItaloMedici Feb 10, 2025
b480b07
refactor: force dynamic
ItaloMedici Feb 10, 2025
b4091eb
ci: adjust deploy pipeline
ItaloMedici Feb 10, 2025
eab10c6
chore(release): v0.7.0
ItaloMedici Feb 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=SOME_RANDOM_SECRET

DATABASE_URL="DATABASE_URL"
DATABASE_URL="postgres://user:password@host:5432/dbname?sslmode=require"

NEXT_PUBLIC_SITE_URL=http://localhost:3000
SITE_URL=http://localhost:3000

GOOGLE_CLIENT_ID=GOOGLE_CLIENT_ID
GOOGLE_CLIENT_SECRET=GOOGLE_CLIENT_SECRET

MAXIMUM_PLAYERS_PER_BOARD=99999
STRIPE_SECRET_KEY=STRIPE_SECRET_KEY
STRIPE_PUBLISH_KEY=STRIPE_PUBLISH_KEY
STRIPE_WEBHOOK_SECRET=STRIPE_WEBHOOK_SECRET
FREE_PLAN_PRICE_ID=FREE_PLAN_PRICE_ID
12 changes: 8 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ jobs:
- name: Deploy to EC2
run: |
ssh -o StrictHostKeyChecking=no ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF'
sudo docker pull ${{ secrets.DOCKER_IMAGE }}
sudo systemctl start docker

sudo docker stop pontim
sudo docker remove pontim

sudo docker stop pontim || true
sudo docker rm pontim || true
sudo docker pull ${{ secrets.DOCKER_IMAGE }}

sudo docker run --platform linux/arm64 -d --name pontim -p 80:3000 ${{ secrets.DOCKER_IMAGE }}
sudo docker run --platform linux/arm64 --name pontim -p 80:3000 ${{ secrets.DOCKER_IMAGE }}

sudo docker ps

sudo docker logs pontim

sudo docker image prune -f
EOF
20 changes: 11 additions & 9 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ name: Create and publish a Docker image

on:
push:
branches:
- 'main'
tags:
- 'v*'
branches:
- "main"
tags:
- "v*"
workflow_dispatch:


env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
Expand Down Expand Up @@ -50,11 +49,14 @@ jobs:
"NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL }}"
"NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }}"
"DATABASE_URL=${{ secrets.DATABASE_URL }}"
"NEXT_PUBLIC_SITE_URL=${{ secrets.NEXT_PUBLIC_SITE_URL }}"
"SITE_URL=${{ secrets.SITE_URL }}"
"GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }}"
"GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }}"
"MAXIMUM_PLAYERS_PER_BOARD=${{ secrets.MAXIMUM_PLAYERS_PER_BOARD }}"

STRIPE_SECRET_KEY=${{ secrets.STRIPE_SECRET_KEY }}
STRIPE_PUBLISH_KEY=${{ secrets.STRIPE_PUBLISH_KEY }}
STRIPE_WEBHOOK_SECRET=${{ secrets.STRIPE_WEBHOOK_SECRET }}
FREE_PLAN_PRICE_ID=${{ secrets.FREE_PLAN_PRICE_ID }}

- name: Generate artifact attestation
uses: actions/attest-build-provenance@v1
with:
Expand All @@ -64,4 +66,4 @@ jobs:
deploy:
uses: ./.github/workflows/deploy.yml
needs: build-and-push-image
secrets: inherit
secrets: inherit
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Changelog

# [0.7.0](https://github.com/ItaloMedici/pontim/compare/v0.6.3...v0.7.0) (2025-02-10)


### Bug Fixes

* change router path ([cd0147f](https://github.com/ItaloMedici/pontim/commit/cd0147f229c4f238e8a9be129b22b10d58a89752))
* **webhook:** rename route ([929602a](https://github.com/ItaloMedici/pontim/commit/929602a9e1de3872695317f4aadb443a480af2d7))


### Features

* add animations to the component how does this work ([afdf970](https://github.com/ItaloMedici/pontim/commit/afdf970fd277d5afc257730b0a1af310533e4e06))
* add aria label to arrow icon ([a671696](https://github.com/ItaloMedici/pontim/commit/a6716966c37d4fcf04ee4a2d2b3e29ce639aa8e6))
* add plan name on jwt ([ee8b5b9](https://github.com/ItaloMedici/pontim/commit/ee8b5b943897eca147f1f63f66ddc922d24091de))
* authorize webhook url ([9b9c667](https://github.com/ItaloMedici/pontim/commit/9b9c66739276938e8c09e6e471a2e7e95dbbd727))
* create footer component ([02d1ff8](https://github.com/ItaloMedici/pontim/commit/02d1ff8072d035aa223b10897be43ee4176e2f16))
* create home dialog messages ([a0398e6](https://github.com/ItaloMedici/pontim/commit/a0398e6d8d2f295e26a956d10804729e24fd31df))
* create how it works component ([919b624](https://github.com/ItaloMedici/pontim/commit/919b6240ac15c0c3578136c760801770358f8fa0))
* create loggout page ([15cbaab](https://github.com/ItaloMedici/pontim/commit/15cbaabbe43e9cdbc564a978795f7a1e222d95e3))
* create pricing page ([e8b5f53](https://github.com/ItaloMedici/pontim/commit/e8b5f53e06827a54563dec0ebeac432e4b24e5d0))
* create room is full page ([e1ee63a](https://github.com/ItaloMedici/pontim/commit/e1ee63a2287aa0098f78f145c486ee7930095b3d))
* **marketing:** improve homepage ([3defd1f](https://github.com/ItaloMedici/pontim/commit/3defd1f1359b1a544e7c6ea225a0a2014f7971e5))
* setup stripe ([6eb5675](https://github.com/ItaloMedici/pontim/commit/6eb567590502e66f3f52b92cd6d16866c1b55a36))
* **stripe:** add board rounds limit ([1bb5b70](https://github.com/ItaloMedici/pontim/commit/1bb5b70a6d84344caeff19a119c0f92dbf366a08))
* **stripe:** add webhook events handling ([5c176a1](https://github.com/ItaloMedici/pontim/commit/5c176a102d823ae62ff170aba469398e4c479009))
* **stripe:** create customer when new user signIn ([294939c](https://github.com/ItaloMedici/pontim/commit/294939c65d11636cb929ff08d5ca01729b8e086b))
* **stripe:** create plan, subscription and user tables ([e78ef05](https://github.com/ItaloMedici/pontim/commit/e78ef05d359674e5373c378f04e00bb00cbd5f4a))


### Performance Improvements

* **marketing:** change hero image png to webp ([8a4459a](https://github.com/ItaloMedici/pontim/commit/8a4459a618e00ec18f912e16f73b11a9f8974828))

## [0.6.3](https://github.com/ItaloMedici/pontim/compare/v0.6.2...v0.6.3) (2025-01-27)

## [0.6.2](https://github.com/ItaloMedici/pontim/compare/v0.6.1...v0.6.2) (2025-01-26)
Expand Down
14 changes: 10 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,23 @@ COPY . .
RUN --mount=type=secret,id=NEXTAUTH_URL \
--mount=type=secret,id=NEXTAUTH_SECRET \
--mount=type=secret,id=DATABASE_URL \
--mount=type=secret,id=NEXT_PUBLIC_SITE_URL \
--mount=type=secret,id=SITE_URL \
--mount=type=secret,id=GOOGLE_CLIENT_ID \
--mount=type=secret,id=GOOGLE_CLIENT_SECRET \
--mount=type=secret,id=MAXIMUM_PLAYERS_PER_BOARD \
--mount=type=secret,id=STRIPE_SECRET_KEY \
--mount=type=secret,id=STRIPE_PUBLISH_KEY \
--mount=type=secret,id=STRIPE_WEBHOOK_SECRET \
--mount=type=secret,id=FREE_PLAN_PRICE_ID \
sh -c 'echo "NEXTAUTH_URL=$(cat /run/secrets/NEXTAUTH_URL)" > .env && \
echo "NEXTAUTH_SECRET=$(cat /run/secrets/NEXTAUTH_SECRET)" >> .env && \
echo "DATABASE_URL=$(cat /run/secrets/DATABASE_URL)" >> .env && \
echo "NEXT_PUBLIC_SITE_URL=$(cat /run/secrets/NEXT_PUBLIC_SITE_URL)" >> .env && \
echo "SITE_URL=$(cat /run/secrets/SITE_URL)" >> .env && \
echo "GOOGLE_CLIENT_ID=$(cat /run/secrets/GOOGLE_CLIENT_ID)" >> .env && \
echo "GOOGLE_CLIENT_SECRET=$(cat /run/secrets/GOOGLE_CLIENT_SECRET)" >> .env && \
echo "MAXIMUM_PLAYERS_PER_BOARD=$(cat /run/secrets/MAXIMUM_PLAYERS_PER_BOARD)" >> .env'
echo "STRIPE_SECRET_KEY=$(cat /run/secrets/STRIPE_SECRET_KEY)" >> .env && \
echo "STRIPE_PUBLISH_KEY=$(cat /run/secrets/STRIPE_PUBLISH_KEY)" >> .env && \
echo "STRIPE_WEBHOOK_SECRET=$(cat /run/secrets/STRIPE_WEBHOOK_SECRET)" >> .env && \
echo "FREE_PLAN_PRICE_ID=$(cat /run/secrets/FREE_PLAN_PRICE_ID)" >> .env'

RUN npm run db:generate

Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Ítalo Médici

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
94 changes: 73 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,88 @@
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).
<div align="center">
<img src="https://i.imgur.com/IZBsCgh.png" alt="Pontim Logo" width="250"/>

# 🃏 Pontim - The Open-Source Scrum Poker Platform

[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Version](https://img.shields.io/github/v/release/ItaloMedici/pontim)](https://github.com/ItaloMedici/pontim/releases) [![Contributors](https://img.shields.io/github/contributors/ItaloMedici/pontim)](https://github.com/ItaloMedici/pontim/graphs/contributors) [![Issues](https://img.shields.io/github/issues/ItaloMedici/pontim)](https://github.com/ItaloMedici/pontim/issues)

## Getting Started
</div>

First, run the development server:
**Pontim** is an open-source, interactive Scrum Poker platform that helps agile teams estimate story points collaboratively and in real time. 🚀
It offers an engaging and efficient way to vote on story complexity using **Fibonacci numbers**, with a unique interactive experience where players can grab attention using sounds. 🔊

```bash
## ✨ Features

✔️ **Live Voting** – Players can vote in real-time and see the average selection.
✔️ **Interactive Notifications** – Grab teammates' attention with sound alerts.
✔️ **Customizable Rooms** – Create rooms with different limits based on your plan.
✔️ **Google OAuth Authentication** – Quick and easy login with Google.
✔️ **Stripe Integration** – Subscription-based plans for premium features.

## 🛠️ Tech Stack

Pontim is built with modern technologies to ensure a seamless and scalable experience:

| Tech | Purpose |
|---------------------|-----------------------------------------------|
| **Next.js** | Frontend framework for server-side rendering |
| **React** | UI library for interactive components |
| **Prisma** | ORM for PostgreSQL database |
| **PostgreSQL** | Relational database for persistent storage |
| **NextAuth** | Authentication provider using Google OAuth |
| **Stripe** | Payment gateway for subscriptions |
| **Docker** | Containerization for deployment |

## 🚀 Getting Started

### 1 - **Clone the Repository**
```sh
git clone https://github.com/ItaloMedici/pontim.git
cd pontim
```

### 2 - **Install Dependencies**
```sh
npm install
```

### 3 - **Set Up Environment Variables**
Create a `.env.exemple` file in the root folder and add the necessary variables:
```ini
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret
DATABASE_URL=your-db-url
...
```

### 4 - **Run the Project Locally**
```sh
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Visit `http://localhost:3000` to start using Pontim! 🎉

---

## 🤝 Contributing

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
We welcome contributions from the community! To contribute:

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
1. **Fork the repository** and create a new branch.
2. Implement your feature or fix.
3. Submit a **Pull Request (PR)** with a detailed description.

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
## 🐝 License

To learn more about Next.js, take a look at the following resources:
Pontim is open-source and released under the **MIT License**. See [LICENSE](LICENSE) for details.

- [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!
## 📚 Learning Purpose

## Deploy on Vercel
This project is designed as a **learning experience** for building **SaaS applications**, working with **real-time interactions**, and implementing **scalable architectures**. Feel free to explore, contribute, and improve Pontim! 🚀

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.
👨‍💻 **Developed by:** [Ítalo Médici](https://github.com/ItaloMedici)
🌟 **Star this project** if you find it useful! 🚀
10 changes: 10 additions & 0 deletions app/(auth)/logout/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use client";

import { LoadingLogo } from "@/components/loading-logo/loading";
import { Suspense } from "react";

const LogoutLayout = ({ children }) => {
return <Suspense fallback={<LoadingLogo />}>{children}</Suspense>;
};

export default LogoutLayout;
21 changes: 21 additions & 0 deletions app/(auth)/logout/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";

import { signOut } from "next-auth/react";
import { useSearchParams } from "next/navigation";
import { useEffect } from "react";

const LogoutPage = () => {
const searchParams = useSearchParams();
const callbackUrl = searchParams.get("callbackUrl") || "/";

useEffect(() => {
signOut({
redirect: true,
callbackUrl,
});
});

return null;
};

export default LogoutPage;
40 changes: 38 additions & 2 deletions app/(dashboard)/home/_components/create-room-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { toast } from "@/components/toast";
import { Button } from "@/components/ui/button";
import {
Dialog,
Expand All @@ -8,12 +9,47 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { useAction } from "@/hooks/use-action";
import { SearchParams } from "@/lib/consts";
import { ValidationState } from "@/messages/state";
import { canAddMoreRoom } from "@/use-cases/plan/can-add-more-room";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { CreateRoomForm } from "./create-room-form";

export function CreateRoomDialog({ trigger }: { trigger?: React.ReactNode }) {
const [open, setOpen] = useState(false);
const { mutation, isPending } = useAction(canAddMoreRoom);
const router = useRouter();

const onOpenChange = async (_open: boolean) => {
if (!_open) {
setOpen(false);
return;
}

try {
const allowed = await mutation({});

if (!allowed) {
router.replace(
`/home?${SearchParams.STATE}=${ValidationState.USER_REACH_MAX_ROOMS}`,
);
return;
}

setOpen(true);
} catch (error) {
console.error(error);
toast.error("Algo deu errado ao tentar criar a sala", {
icon: "😢",
});
}
};

return (
<Dialog>
<DialogTrigger asChild>
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogTrigger asChild disabled={isPending}>
{trigger ?? <Button size="lg">Criar nova sala</Button>}
</DialogTrigger>
<DialogContent>
Expand Down
18 changes: 18 additions & 0 deletions app/(dashboard)/home/_components/messages/errors-messages.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"use client";

import { toast } from "@/components/toast";
import { useSearchParams } from "next/navigation";
import { useEffect } from "react";

export const ErrorsMessages = () => {
const searchParams = useSearchParams();
const error = searchParams.get("error");

useEffect(() => {
if (typeof error === "string") {
toast.error(error);
}
}, [error]);

return null;
};
Loading