diff --git a/app/(dashboard)/repo-settings/[repositoryId]/description/page.tsx b/app/(dashboard)/repo-settings/[repositoryId]/description/page.tsx
index fe5711e..a46a504 100644
--- a/app/(dashboard)/repo-settings/[repositoryId]/description/page.tsx
+++ b/app/(dashboard)/repo-settings/[repositoryId]/description/page.tsx
@@ -1,12 +1,25 @@
+import UpdateProjectDescription from "@/components/ui/updateProjectDescription";
+import { fetchRepoDetails, updateRepositoryDescritpion } from "@/lib/repository/service";
+
export const metadata = {
title: "Project description",
description: "Change how your project is presented to players.",
};
-export default async function DescriptionPage({ params }) {
+export default async function DescriptionPage({ params, useState }) {
+
+ const repoDetail = await fetchRepoDetails(params.repositoryId);
+
+ console.log({ repoDetail })
+
+ async function updateRepoDescritpion(description: string) {
+ "use server"
+ return updateRepositoryDescritpion(params.repositoryId, description);
+ }
+
return (
-
Make work with the current description on repository detail page.
+
);
}
diff --git a/app/[githubLogin]/page.tsx b/app/[githubLogin]/page.tsx
index ae42cc7..f2b26ef 100644
--- a/app/[githubLogin]/page.tsx
+++ b/app/[githubLogin]/page.tsx
@@ -100,15 +100,17 @@ export default async function ProfilePage({ params }) {
const user = await getUserByLogin(githubLogin);
if (user) {
+ const userEnrollments = await getEnrolledRepositories(user?.id);
+
+ const repos = userEnrollments.length > 0 ? [...userEnrollments].map((repo) => repo.name) : null; // get repo names from userEnrollments
const [githubUserData, mergedIssues, openPRs] = await Promise.all([
getGithubUserByLogin(githubLogin).then(
(data) => data || { name: "Rick", avatar_url: Rick, bio: "is never gonna give you up 🕺" }
),
- getMergedPullRequestsByGithubLogin("formbricks/formbricks", githubLogin),
+ getMergedPullRequestsByGithubLogin(repos, githubLogin),
getOpenPullRequestsByGithubLogin("formbricks/formbricks", githubLogin),
]);
- const userEnrollments = await getEnrolledRepositories(user?.id);
const arrayCurrentLevelOfUserInEnrolledRepos = await Promise.all(
userEnrollments.map(async (repo) => {
diff --git a/components/ui/tiptap-editor.tsx b/components/ui/tiptap-editor.tsx
new file mode 100644
index 0000000..c5e4e95
--- /dev/null
+++ b/components/ui/tiptap-editor.tsx
@@ -0,0 +1,261 @@
+"use client";
+
+import { Color } from "@tiptap/extension-color";
+import ListItem from "@tiptap/extension-list-item";
+import TextStyle from "@tiptap/extension-text-style";
+import { EditorProvider, useCurrentEditor } from "@tiptap/react";
+import StarterKit from "@tiptap/starter-kit";
+import React from "react";
+
+import "../../styles/editor.css";
+
+const MenuBar = () => {
+ const { editor } = useCurrentEditor();
+
+ if (!editor) {
+ return null;
+ }
+
+ editor.setOptions({
+ editorProps: {
+ attributes: {
+ class:
+ "border-none prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none",
+ },
+ },
+ });
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+const extensions = [
+ Color.configure({ types: [TextStyle.name, ListItem.name] }),
+ TextStyle.configure({ types: [ListItem.name] }),
+ StarterKit.configure({
+ bulletList: {
+ keepMarks: true,
+ keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
+ },
+ orderedList: {
+ keepMarks: true,
+ keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
+ },
+ }),
+];
+
+export default ({ content, fn }) => {
+ return } extensions={extensions} onUpdate={({editor}) => fn(editor.getHTML()) } content={content}>;
+};
diff --git a/components/ui/updateProjectDescription.tsx b/components/ui/updateProjectDescription.tsx
new file mode 100644
index 0000000..0d58a29
--- /dev/null
+++ b/components/ui/updateProjectDescription.tsx
@@ -0,0 +1,26 @@
+"use client";
+
+import Tiptap from "@/components/ui/tiptap-editor";
+import { use, useEffect, useState } from "react";
+
+export default function UpdateProjectDescription({ content, action }) {
+ const [description, setDescription] = useState(content);
+
+ useEffect(() => {
+ const fetchDescription = async () => {
+ const repo = await action(description);
+ setDescription(repo.projectDescription || description);
+ };
+ // add debounce here to avoid too many requests
+ const debounced = setTimeout(() => {
+ fetchDescription();
+ }, 2000);
+ return () => clearTimeout(debounced);
+ }, [description]);
+
+ return (
+
+
+
+ );
+}
diff --git a/lib/github/service.ts b/lib/github/service.ts
index 731bff1..85a4999 100644
--- a/lib/github/service.ts
+++ b/lib/github/service.ts
@@ -5,38 +5,67 @@ import { unstable_cache } from "next/cache";
import { GITHUB_APP_ACCESS_TOKEN, GITHUB_CACHE_REVALIDATION_INTERVAL, OSS_GG_LABEL } from "../constants";
-export const getMergedPullRequestsByGithubLogin = (repo: string, githubLogin: string) =>
- unstable_cache(
- async () => {
- const url = `https://api.github.com/search/issues?q=repo:${repo}+is:pull-request+is:merged+author:${githubLogin}&per_page=10&sort=created&order=desc`;
-
- const headers = {
- Authorization: `Bearer ${GITHUB_APP_ACCESS_TOKEN}`,
- Accept: "application/vnd.github.v3+json",
- };
-
- const response = await fetch(url, { headers });
- const data = await response.json();
-
- const validatedData = ZGithubApiResponseSchema.parse(data);
-
- // Map the GitHub API response to issue format
- const mergedPRs = validatedData.items.map((pr) => ({
- logoUrl: "https://avatars.githubusercontent.com/u/105877416?s=200&v=4",
- href: pr.html_url,
- title: pr.title,
- author: pr.user.login,
- key: pr.id.toString(),
- isIssue: false,
- }));
+export const getMergedPullRequestsByGithubLogin = async (repos: Array | null, githubLogin: string) => {
+ if (!repos || repos.length === 0) {
+ return Promise.resolve([]);
+ }
+
+ const mergedPRs: Array<{
+ logoUrl: string;
+ href: string;
+ title: string;
+ author: string;
+ key: string;
+ isIssue: boolean;
+ }> = [];
+
+ for (const repoName of repos) {
+ await unstable_cache(
+ async () => {
+ const url = `https://api.github.com/search/issues?q=repo:${repoName}+is:pull-request+is:merged+author:${githubLogin}&per_page=10&sort=created&order=desc`;
+
+ const headers = {
+ Authorization: `Bearer ${GITHUB_APP_ACCESS_TOKEN}`,
+ Accept: "application/vnd.github.v3+json",
+ };
- return mergedPRs;
- },
- [`getMergedPullRequests-${repo}-${githubLogin}`],
- {
- revalidate: GITHUB_CACHE_REVALIDATION_INTERVAL,
- }
- )();
+ const response = await fetch(url, { headers });
+ const data = await response.json();
+
+ const validatedData = ZGithubApiResponseSchema.parse(data);
+
+ mergedPRs.push(
+ ...validatedData.items.map((pr) => ({
+ logoUrl: "https://avatars.githubusercontent.com/u/105877416?s=200&v=4",
+ href: pr.html_url,
+ title: pr.title,
+ author: pr.user.login,
+ key: pr.id.toString(),
+ isIssue: false,
+ }))
+ );
+
+ // Map the GitHub API response to issue format
+ // const mergedPRs = validatedData.items.map((pr) => ({
+ // logoUrl: "https://avatars.githubusercontent.com/u/105877416?s=200&v=4",
+ // href: pr.html_url,
+ // title: pr.title,
+ // author: pr.user.login,
+ // key: pr.id.toString(),
+ // isIssue: false,
+ // }));
+
+ return mergedPRs;
+ },
+ [`getMergedPullRequests-${repos}-${githubLogin}`],
+ {
+ revalidate: GITHUB_CACHE_REVALIDATION_INTERVAL,
+ }
+ )();
+ }
+
+ return mergedPRs;
+};
export const getOpenPullRequestsByGithubLogin = (repo: string, githubLogin: string) =>
unstable_cache(
diff --git a/lib/repository/service.ts b/lib/repository/service.ts
index 1e7a6b6..614ed3b 100644
--- a/lib/repository/service.ts
+++ b/lib/repository/service.ts
@@ -99,6 +99,24 @@ export const updateRepository = async (id: string, configuredValue: boolean) =>
return updatedRepository;
};
+/**
+ * Updates a repository's description field
+ * @returns The updated repository.
+ */
+
+export const updateRepositoryDescritpion = async (id: string, description: string) => {
+ const updatedRepository = await db.repository.update({
+ where: { id },
+ data: { projectDescription: description },
+ });
+
+ if (!updatedRepository) {
+ throw new Error("Repository not found.");
+ }
+
+ return updatedRepository;
+};
+
/**
* Fetches a repository
* @returns The fetched repository.
diff --git a/package.json b/package.json
index 8ec9414..5fb9ac0 100644
--- a/package.json
+++ b/package.json
@@ -17,10 +17,6 @@
"start": "next start"
},
"dependencies": {
- "@aws-sdk/client-s3": "^3.577.0",
- "@aws-sdk/s3-presigned-post": "^3.577.0",
- "@aws-sdk/s3-request-presigner": "^3.577.0",
- "@hookform/resolvers": "^3.4.0",
"@aws-sdk/client-s3": "^3.540.0",
"@aws-sdk/s3-presigned-post": "^3.540.0",
"@aws-sdk/s3-request-presigner": "^3.540.0",
@@ -45,9 +41,20 @@
"@radix-ui/react-toast": "^1.1.5",
"@smithy/middleware-serde": "3.0.0",
"@t3-oss/env-nextjs": "^0.8.0",
+ "@tiptap/extension-blockquote": "^2.4.0",
+ "@tiptap/extension-bold": "^2.4.0",
+ "@tiptap/extension-bullet-list": "^2.4.0",
+ "@tiptap/extension-color": "^2.4.0",
"@tiptap/extension-heading": "^2.4.0",
"@tiptap/extension-highlight": "^2.4.0",
+ "@tiptap/extension-link": "^2.4.0",
+ "@tiptap/extension-list-item": "^2.4.0",
+ "@tiptap/extension-ordered-list": "^2.4.0",
+ "@tiptap/extension-paragraph": "^2.4.0",
+ "@tiptap/extension-placeholder": "^2.4.0",
+ "@tiptap/extension-text-style": "^2.4.0",
"@tiptap/extension-typography": "^2.4.0",
+ "@tiptap/extension-underline": "^2.4.0",
"@tiptap/react": "^2.4.0",
"@tiptap/starter-kit": "^2.4.0",
"@trigger.dev/nextjs": "^2.3.18",
@@ -79,6 +86,7 @@
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"tremendous": "3.0.1",
+ "trix": "^2.1.1",
"uuid": "^9.0.1",
"zod": "^3.23.8"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b9427a9..4ae1547 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -80,15 +80,48 @@ dependencies:
'@t3-oss/env-nextjs':
specifier: ^0.8.0
version: 0.8.0(typescript@5.4.5)(zod@3.23.8)
+ '@tiptap/extension-blockquote':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-bold':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-bullet-list':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-color':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)(@tiptap/extension-text-style@2.4.0)
'@tiptap/extension-heading':
specifier: ^2.4.0
version: 2.4.0(@tiptap/core@2.4.0)
'@tiptap/extension-highlight':
specifier: ^2.4.0
version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-link':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)(@tiptap/pm@2.4.0)
+ '@tiptap/extension-list-item':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-ordered-list':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-paragraph':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-placeholder':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)(@tiptap/pm@2.4.0)
+ '@tiptap/extension-text-style':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
'@tiptap/extension-typography':
specifier: ^2.4.0
version: 2.4.0(@tiptap/core@2.4.0)
+ '@tiptap/extension-underline':
+ specifier: ^2.4.0
+ version: 2.4.0(@tiptap/core@2.4.0)
'@tiptap/react':
specifier: ^2.4.0
version: 2.4.0(@tiptap/core@2.4.0)(@tiptap/pm@2.4.0)(react-dom@18.3.1)(react@18.3.1)
@@ -182,6 +215,9 @@ dependencies:
tremendous:
specifier: 3.0.1
version: 3.0.1
+ trix:
+ specifier: ^2.1.1
+ version: 2.1.1
uuid:
specifier: ^9.0.1
version: 9.0.1
@@ -3408,6 +3444,16 @@ packages:
'@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
dev: false
+ /@tiptap/extension-color@2.4.0(@tiptap/core@2.4.0)(@tiptap/extension-text-style@2.4.0):
+ resolution: {integrity: sha512-aVuqGtzTIZO93niADdu+Hx8g03X0pS7wjrJcCcYkkDEbC/siC03zlxKZIYBW1Jiabe99Z7/s2KdtLoK6DW2A2g==}
+ peerDependencies:
+ '@tiptap/core': ^2.0.0
+ '@tiptap/extension-text-style': ^2.0.0
+ dependencies:
+ '@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
+ '@tiptap/extension-text-style': 2.4.0(@tiptap/core@2.4.0)
+ dev: false
+
/@tiptap/extension-document@2.4.0(@tiptap/core@2.4.0):
resolution: {integrity: sha512-3jRodQJZDGbXlRPERaloS+IERg/VwzpC1IO6YSJR9jVIsBO6xC29P3cKTQlg1XO7p6ZH/0ksK73VC5BzzTwoHg==}
peerDependencies:
@@ -3499,6 +3545,17 @@ packages:
'@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
dev: false
+ /@tiptap/extension-link@2.4.0(@tiptap/core@2.4.0)(@tiptap/pm@2.4.0):
+ resolution: {integrity: sha512-r3PjT0bjSKAorHAEBPA0icSMOlqALbxVlWU9vAc+Q3ndzt7ht0CTPNewzFF9kjzARABVt1cblXP/2+c0qGzcsg==}
+ peerDependencies:
+ '@tiptap/core': ^2.0.0
+ '@tiptap/pm': ^2.0.0
+ dependencies:
+ '@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
+ '@tiptap/pm': 2.4.0
+ linkifyjs: 4.1.3
+ dev: false
+
/@tiptap/extension-list-item@2.4.0(@tiptap/core@2.4.0):
resolution: {integrity: sha512-reUVUx+2cI2NIAqMZhlJ9uK/+zvRzm1GTmlU2Wvzwc7AwLN4yemj6mBDsmBLEXAKPvitfLh6EkeHaruOGymQtg==}
peerDependencies:
@@ -3523,6 +3580,16 @@ packages:
'@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
dev: false
+ /@tiptap/extension-placeholder@2.4.0(@tiptap/core@2.4.0)(@tiptap/pm@2.4.0):
+ resolution: {integrity: sha512-SmWOjgWpmhFt0BPOnL65abCUH0wS5yksUJgtANn5bQoHF4HFSsyl7ETRmgf0ykxdjc7tzOg31FfpWVH4wzKSYg==}
+ peerDependencies:
+ '@tiptap/core': ^2.0.0
+ '@tiptap/pm': ^2.0.0
+ dependencies:
+ '@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
+ '@tiptap/pm': 2.4.0
+ dev: false
+
/@tiptap/extension-strike@2.4.0(@tiptap/core@2.4.0):
resolution: {integrity: sha512-pE1uN/fQPOMS3i+zxPYMmPmI3keubnR6ivwM+KdXWOMnBiHl9N4cNpJgq1n2eUUGKLurC2qrQHpnVyGAwBS6Vg==}
peerDependencies:
@@ -3531,6 +3598,14 @@ packages:
'@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
dev: false
+ /@tiptap/extension-text-style@2.4.0(@tiptap/core@2.4.0):
+ resolution: {integrity: sha512-H0uPWeZ4sXz3o836TDWnpd38qClqzEM2d6QJ9TK+cQ1vE5Gp8wQ5W4fwUV1KAHzpJKE/15+BXBjLyVYQdmXDaQ==}
+ peerDependencies:
+ '@tiptap/core': ^2.0.0
+ dependencies:
+ '@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
+ dev: false
+
/@tiptap/extension-text@2.4.0(@tiptap/core@2.4.0):
resolution: {integrity: sha512-LV0bvE+VowE8IgLca7pM8ll7quNH+AgEHRbSrsI3SHKDCYB9gTHMjWaAkgkUVaO1u0IfCrjnCLym/PqFKa+vvg==}
peerDependencies:
@@ -3547,6 +3622,14 @@ packages:
'@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
dev: false
+ /@tiptap/extension-underline@2.4.0(@tiptap/core@2.4.0):
+ resolution: {integrity: sha512-guWojb7JxUwLz4OKzwNExJwOkhZjgw/ttkXCMBT0PVe55k998MMYe1nvN0m2SeTW9IxurEPtScH4kYJ0XuSm8Q==}
+ peerDependencies:
+ '@tiptap/core': ^2.0.0
+ dependencies:
+ '@tiptap/core': 2.4.0(@tiptap/pm@2.4.0)
+ dev: false
+
/@tiptap/pm@2.4.0:
resolution: {integrity: sha512-B1HMEqGS4MzIVXnpgRZDLm30mxDWj51LkBT/if1XD+hj5gm8B9Q0c84bhvODX6KIs+c6z+zsY9VkVu8w9Yfgxg==}
dependencies:
@@ -6122,6 +6205,10 @@ packages:
uc.micro: 2.1.0
dev: false
+ /linkifyjs@4.1.3:
+ resolution: {integrity: sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==}
+ dev: false
+
/locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@@ -8354,6 +8441,10 @@ packages:
engines: {node: '>=8'}
dev: true
+ /trix@2.1.1:
+ resolution: {integrity: sha512-IljOMGOlRUPg1i5Pk/+x/Ia65ZY7Gw5JxxKCh/4caxG5ZaKuFJCKdn1+TF0efUYfdg+bqWenB/mAYCHjZu0zpQ==}
+ dev: false
+
/trough@2.2.0:
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
dev: false
diff --git a/styles/editor.css b/styles/editor.css
index 5695fc6..501fa0a 100644
--- a/styles/editor.css
+++ b/styles/editor.css
@@ -1,26 +1,16 @@
.tiptap > * + * {
margin-top: 0.75em;
}
-
-.tiptap ul,
-.tiptap ol {
+.tiptap ul, .tiptap ol {
padding: 0 1rem;
}
-
-.tiptap h1,
-.tiptap h2,
-.tiptap h3,
-.tiptap h4,
-.tiptap h5,
-.tiptap h6 {
+.tiptap h1, .tiptap h2, .tiptap h3, .tiptap h4, .tiptap h5, .tiptap h6 {
line-height: 1.1;
}
-
.tiptap code {
background-color: rgba(97, 97, 97, 0.1);
color: #616161;
}
-
.tiptap pre {
background: #0D0D0D;
color: #FFF;
@@ -28,24 +18,22 @@
padding: 0.75rem 1rem;
border-radius: 0.5rem;
}
-
.tiptap pre code {
color: inherit;
padding: 0;
background: none;
font-size: 0.8rem;
}
-
.tiptap img {
max-width: 100%;
height: auto;
}
-
-.tiptap hr {
- margin: 1rem 0;
-}
-
.tiptap blockquote {
padding-left: 1rem;
border-left: 2px solid rgba(13, 13, 13, 0.1);
-}
\ No newline at end of file
+}
+.tiptap hr {
+ border: none;
+ border-top: 2px solid rgba(13, 13, 13, 0.1);
+ margin: 2rem 0;
+}