From 0bab680d1bfb956f3616cc9b6aab3a7f16a1cf6f Mon Sep 17 00:00:00 2001 From: picsoritdidnthappen <152807131+picsoritdidnthappen@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:49:34 -0600 Subject: [PATCH 1/4] fix: issue #332 --- .env.example | 3 +- src/app/api/generateBounty/route.ts | 48 ++++++++++++++++++ src/components/global/FormBounty.tsx | 75 ++++++++++++++++++++++++++-- src/utils/types.ts | 6 +++ 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 src/app/api/generateBounty/route.ts diff --git a/.env.example b/.env.example index 7bf10e33..b8374150 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ DATABASE_URL="postgresql://…" -ADMINS="0x…,0x…,0x…" \ No newline at end of file +ADMINS="0x…,0x…,0x…" +OPENAI_API_KEY="sk-…" \ No newline at end of file diff --git a/src/app/api/generateBounty/route.ts b/src/app/api/generateBounty/route.ts new file mode 100644 index 00000000..4a652a59 --- /dev/null +++ b/src/app/api/generateBounty/route.ts @@ -0,0 +1,48 @@ +export async function POST(): Promise { + try { + const OPENAI_API_KEY = process.env.OPENAI_API_KEY; + if (!OPENAI_API_KEY) { + throw new Error('Missing OpenAI API key'); + } + + const response = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${OPENAI_API_KEY}`, + }, + body: JSON.stringify({ + model: 'gpt-4-turbo', + messages: [ + { + role: 'system', + content: `Generate unique, creative, and fun bounty ideas for the "Pics or It Didn't Happen" (poidh) website. Each bounty should encourage users to engage in amusing, interesting, or surprising activities that can be easily documented with a photo, screenshot, or video. + Ensure the ideas are diverse, spanning different themes such as real-life actions, contributions, playful tasks, or simple creative(could be developer) projects. + Ideas must remain achievable and enjoyable for users of all skill levels. A user should share result either in video or in photo. Include: + Title: A short, catchy description of the bounty (max 50 characters). + Description: A clear and engaging explanation of what the user must do to complete the bounty (max 350 characters). + Return the ideas in JSON format like this: + { 'title': '...', 'description': '...' }.`, + }, + { + role: 'user', + content: 'Generate a bounty idea for a person to do.', + }, + ], + max_tokens: 100, + temperature: 1, + response_format: { type: 'json_object' }, + }), + }); + + if (!response.ok) { + console.log('Error in response from OpenAI API.'); + } + + const data = await response.json(); + return Response.json(data.choices[0].message.content); + } catch (error) { + console.error('Error communicating with OpenAI API:', error); + return Response.error(); + } +} diff --git a/src/components/global/FormBounty.tsx b/src/components/global/FormBounty.tsx index 912fafcd..1a6104f4 100644 --- a/src/components/global/FormBounty.tsx +++ b/src/components/global/FormBounty.tsx @@ -20,6 +20,7 @@ import { trpcClient } from '@/trpc/client'; import GameButton from '@/components/global/GameButton'; import ButtonCTA from '@/components/ui/ButtonCTA'; import { InfoIcon } from '@/components/global/Icons'; +import { Bounty } from '@/utils/types'; export default function FormBounty({ open, @@ -107,6 +108,35 @@ export default function FormBounty({ }, }); + const generateBountyMutation = useMutation({ + mutationFn: async () => { + setName('Generating…'); + setDescription('Generating…'); + const res = await fetch('/api/generateBounty', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!res.ok) { + throw new Error(`Error: ${res.status}`); + } + + return JSON.parse(await res.json()); + }, + onSuccess: (bounty: Bounty) => { + setName(bounty.title); + setDescription(bounty.description); + toast.success('Bounty generated successfully'); + }, + onError: (error) => { + setName(''); + setDescription(''); + toast.error('Failed to generate bounty: ' + error.message); + }, + }); + return ( <> @@ -129,19 +159,43 @@ export default function FormBounty({ > - title + + title + setName(e.target.value)} - className='border bg-transparent border-[#D1ECFF] py-2 px-2 rounded-md mb-4' + className={cn( + 'border py-2 px-2 rounded-md mb-4 bg-transparent border-[#D1ECFF]', + generateBountyMutation.isPending + ? 'cursor-not-allowed animate-pulse' + : '' + )} /> - description + + description + reward @@ -198,6 +252,19 @@ export default function FormBounty({ create bounty +
+ need a bouty idea? click the + +
); diff --git a/src/utils/types.ts b/src/utils/types.ts index 4cdacf8b..9ce0f3f0 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -19,3 +19,9 @@ export type Wallet = { ens: string | null; degenName: string | null; }; + +export type Bounty = { + title: string; + description: string; + isGenerating?: boolean; +}; From 8f5f4c88bc183a61a7b78385a0429752f720ac74 Mon Sep 17 00:00:00 2001 From: Alina Lytovchenko Date: Mon, 9 Dec 2024 22:34:18 -0800 Subject: [PATCH 2/4] fix: resolve PR comments --- src/app/api/generateBounty/route.ts | 75 +++++++++++++--------------- src/components/global/FormBounty.tsx | 29 +++++------ src/utils/types.ts | 6 --- 3 files changed, 49 insertions(+), 61 deletions(-) diff --git a/src/app/api/generateBounty/route.ts b/src/app/api/generateBounty/route.ts index 4a652a59..ab66e776 100644 --- a/src/app/api/generateBounty/route.ts +++ b/src/app/api/generateBounty/route.ts @@ -1,48 +1,45 @@ -export async function POST(): Promise { - try { - const OPENAI_API_KEY = process.env.OPENAI_API_KEY; - if (!OPENAI_API_KEY) { - throw new Error('Missing OpenAI API key'); - } - - const response = await fetch('https://api.openai.com/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${OPENAI_API_KEY}`, - }, - body: JSON.stringify({ - model: 'gpt-4-turbo', - messages: [ - { - role: 'system', - content: `Generate unique, creative, and fun bounty ideas for the "Pics or It Didn't Happen" (poidh) website. Each bounty should encourage users to engage in amusing, interesting, or surprising activities that can be easily documented with a photo, screenshot, or video. +const prompt = `Generate unique, creative, and fun bounty ideas for the "Pics or It Didn't Happen" (poidh) website. Each bounty should encourage users to engage in amusing, interesting, or surprising activities that can be easily documented with a photo, screenshot, or video. Ensure the ideas are diverse, spanning different themes such as real-life actions, contributions, playful tasks, or simple creative(could be developer) projects. Ideas must remain achievable and enjoyable for users of all skill levels. A user should share result either in video or in photo. Include: Title: A short, catchy description of the bounty (max 50 characters). Description: A clear and engaging explanation of what the user must do to complete the bounty (max 350 characters). Return the ideas in JSON format like this: - { 'title': '...', 'description': '...' }.`, - }, - { - role: 'user', - content: 'Generate a bounty idea for a person to do.', - }, - ], - max_tokens: 100, - temperature: 1, - response_format: { type: 'json_object' }, - }), - }); + { 'title': '...', 'description': '...' }.`; + +export async function POST(): Promise { + const OPENAI_API_KEY = process.env.OPENAI_API_KEY; + if (!OPENAI_API_KEY) { + throw new Error('Missing OpenAI API key'); + } - if (!response.ok) { - console.log('Error in response from OpenAI API.'); - } + const response = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${OPENAI_API_KEY}`, + }, + body: JSON.stringify({ + model: 'gpt-4-turbo', + messages: [ + { + role: 'system', + content: prompt, + }, + { + role: 'user', + content: 'Generate a bounty idea for a person to do.', + }, + ], + max_tokens: 100, + temperature: 1, + response_format: { type: 'json_object' }, + }), + }); - const data = await response.json(); - return Response.json(data.choices[0].message.content); - } catch (error) { - console.error('Error communicating with OpenAI API:', error); - return Response.error(); + if (!response.ok) { + throw new Error(`Error: ${response.status}`); } + + const data = await response.json(); + return Response.json(data.choices[0].message.content); } diff --git a/src/components/global/FormBounty.tsx b/src/components/global/FormBounty.tsx index 1a6104f4..64f80abe 100644 --- a/src/components/global/FormBounty.tsx +++ b/src/components/global/FormBounty.tsx @@ -20,7 +20,11 @@ import { trpcClient } from '@/trpc/client'; import GameButton from '@/components/global/GameButton'; import ButtonCTA from '@/components/ui/ButtonCTA'; import { InfoIcon } from '@/components/global/Icons'; -import { Bounty } from '@/utils/types'; + +type Bounty = { + title: string; + description: string; +}; export default function FormBounty({ open, @@ -118,12 +122,7 @@ export default function FormBounty({ 'Content-Type': 'application/json', }, }); - - if (!res.ok) { - throw new Error(`Error: ${res.status}`); - } - - return JSON.parse(await res.json()); + return JSON.parse(await res.json()) as Bounty; }, onSuccess: (bounty: Bounty) => { setName(bounty.title); @@ -161,7 +160,7 @@ export default function FormBounty({ title @@ -173,14 +172,13 @@ export default function FormBounty({ onChange={(e) => setName(e.target.value)} className={cn( 'border py-2 px-2 rounded-md mb-4 bg-transparent border-[#D1ECFF]', - generateBountyMutation.isPending - ? 'cursor-not-allowed animate-pulse' - : '' + generateBountyMutation.isPending && + 'cursor-not-allowed animate-pulse' )} /> description @@ -192,9 +190,8 @@ export default function FormBounty({ onChange={(e) => setDescription(e.target.value)} className={cn( 'border py-2 px-2 rounded-md mb-4 max-h-28 bg-transparent border-[#D1ECFF]', - generateBountyMutation.isPending - ? 'cursor-not-allowed animate-pulse' - : '' + generateBountyMutation.isPending && + 'cursor-not-allowed animate-pulse' )} > @@ -257,7 +254,7 @@ export default function FormBounty({