Skip to content

Commit 7fe4286

Browse files
page & supabase updates
1 parent 6eee682 commit 7fe4286

File tree

13 files changed

+744
-252
lines changed

13 files changed

+744
-252
lines changed

.github/FUNDING.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# These are supported funding model platforms
2+
3+
github: gerome-elassaad

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
# CodinIT
2-
3-
![interface-codinit)](https://github.com/user-attachments/assets/8cb324ff-a924-46bc-a22b-32f89c6aed55)
1+
![opengraph](https://github.com/user-attachments/assets/de684e88-a65c-42ea-b067-d1a3bc85a420)
42

53

64
## Powered by the [E2B SDK](https://github.com/e2b-dev/code-interpreter).

app/actions/profile.ts

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
"use server"
2+
3+
import { createServerClient, type CookieOptions } from "@supabase/ssr"
4+
import { cookies } from "next/headers"
5+
import { revalidatePath } from "next/cache"
6+
7+
// Matches the ProfileFormData in app/settings/profile/page.tsx and Supabase table
8+
export interface ProfileData {
9+
id?: string // User ID from auth.users
10+
first_name: string | null
11+
last_name: string | null
12+
company: string | null
13+
job_title: string | null
14+
location: string | null
15+
timezone: string | null
16+
bio: string | null
17+
work_description: string | null
18+
preferences: string | null
19+
personalized_responses: boolean
20+
activity_status: boolean
21+
profile_visibility: "public" | "private" | "contacts"
22+
avatar_url?: string | null // For profile picture
23+
// email will be fetched from auth.user if needed, not stored directly here for update
24+
}
25+
26+
export async function getProfile(): Promise<ProfileData | null> {
27+
const cookieStorePromise = cookies()
28+
const supabase = createServerClient(
29+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
30+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
31+
{
32+
cookies: {
33+
async get(name: string) {
34+
const cookieStore = await cookieStorePromise
35+
return cookieStore.get(name)?.value
36+
},
37+
async set(name: string, value: string, options: CookieOptions) {
38+
try {
39+
const cookieStore = await cookieStorePromise
40+
cookieStore.set(name, value, options)
41+
} catch (error) {
42+
// If the set method is called from a Server Component, an error may occur.
43+
// This can be ignored if you have middleware refreshing user sessions.
44+
console.warn(`Failed to set cookie '${name}' from Server Action/Component. This might be okay if middleware handles session refresh. Error: ${error}`);
45+
}
46+
},
47+
async remove(name: string = "supabase-auth-token") {
48+
try {
49+
const cookieStore = await cookieStorePromise
50+
cookieStore.delete(name)
51+
} catch (error) {
52+
// If the delete method is called from a Server Component, an error may occur.
53+
// This can be ignored if you have middleware refreshing user sessions.
54+
console.warn(`Failed to delete cookie '${name}' from Server Action/Component. This might be okay if middleware handles session refresh. Error: ${error}`);
55+
}
56+
},
57+
},
58+
}
59+
)
60+
61+
try {
62+
const {
63+
data: { user },
64+
} = await supabase.auth.getUser()
65+
66+
if (!user) {
67+
console.log("No user found, cannot fetch profile.")
68+
return null
69+
}
70+
71+
const { data, error } = await supabase
72+
.from("profiles")
73+
.select("*")
74+
.eq("id", user.id)
75+
.single()
76+
77+
if (error) {
78+
if (error.code === 'PGRST116') { // PGRST116: "Searched for a single row, but 0 rows were found"
79+
console.log("No profile found for user, returning defaults or null.")
80+
// You might want to return a default profile structure or ensure one is created on user signup
81+
return {
82+
id: user.id,
83+
first_name: "",
84+
last_name: "",
85+
company: null,
86+
job_title: null,
87+
location: null,
88+
timezone: null,
89+
bio: null,
90+
work_description: null,
91+
preferences: null,
92+
personalized_responses: true,
93+
activity_status: true,
94+
profile_visibility: "private",
95+
avatar_url: null,
96+
}
97+
}
98+
console.error("Error fetching profile:", error)
99+
throw error
100+
}
101+
102+
return data as ProfileData
103+
} catch (error) {
104+
console.error("Error in getProfile:", error)
105+
return null
106+
}
107+
}
108+
109+
export async function updateProfile(
110+
profileData: Partial<ProfileData>
111+
): Promise<{ success: boolean; error?: any }> {
112+
const cookieStore = cookies()
113+
const supabase = createServerClient(
114+
process.env.NEXT_PUBLIC_SUPABASE_URL!,
115+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
116+
{
117+
cookies: {
118+
async get(name: string) {
119+
return (await cookieStore).get(name)?.value
120+
},
121+
async set(name: string, value: string, options: CookieOptions) {
122+
try {
123+
(await cookieStore).set(name, value, options)
124+
} catch (error) {
125+
console.warn(`Failed to set cookie '${name}' from Server Action/Component. This might be okay if middleware handles session refresh. Error: ${error}`);
126+
}
127+
},
128+
remove(name: string) {
129+
try {
130+
} catch (error) {
131+
console.warn(`Failed to delete cookie '${name}' from Server Action/Component. This might be okay if middleware handles session refresh. Error: ${error}`);
132+
}
133+
},
134+
},
135+
}
136+
)
137+
138+
try {
139+
const {
140+
data: { user },
141+
} = await supabase.auth.getUser()
142+
143+
if (!user) {
144+
console.error("User not authenticated for profile update.")
145+
return { success: false, error: "User not authenticated" }
146+
}
147+
148+
// Ensure 'id' is not part of the update payload to Supabase,
149+
// as it's used in the .eq() clause and is the primary key.
150+
// 'updated_at' will be handled by the database trigger.
151+
const { id, ...updateData } = profileData
152+
153+
const { error } = await supabase
154+
.from("profiles")
155+
.update(updateData)
156+
.eq("id", user.id)
157+
158+
if (error) {
159+
console.error("Error updating profile:", error)
160+
return { success: false, error }
161+
}
162+
163+
revalidatePath("/settings/profile") // Revalidate the profile page to show updated data
164+
revalidatePath("/") // Revalidate other paths if profile info is displayed elsewhere
165+
return { success: true }
166+
} catch (error) {
167+
console.error("Error in updateProfile:", error)
168+
return { success: false, error }
169+
}
170+
}

app/layout.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,37 @@ import { Inter } from 'next/font/google'
88
const inter = Inter({ subsets: ['latin'] })
99

1010
export const metadata: Metadata = {
11-
title: 'CodinIT - #1 Open Source AI App Builder',
11+
metadataBase: new URL('https://codingit.vercel.app'),
12+
title: '#1 Open Source AI Software Engineer',
13+
keywords: [
14+
'AI software engineer',
15+
'open source',
16+
'live code execution',
17+
'file uploads',
18+
'real-time chat',
19+
'codinit',
20+
'codingit',
21+
'lovable.dev alternative',
22+
'bolt.new alternative',
23+
'v0.dev alternative'
24+
],
1225
description: 'Open-source alternative to lovable.dev, bolt.new & v0.dev. AI software engineer — live code execution, file uploads, & real-time chat blazing-fast.',
1326
icons: [
1427
{ rel: "apple-touch-icon", sizes: "180x180", url: "/apple-touch-icon.png" },
28+
{ rel: "icon", type: "image/x-icon", url: "/favicon.ico" },
1529
{ rel: "icon", type: "image/png", sizes: "32x32", url: "/icons/favicon-32x32.png" },
1630
{ rel: "icon", type: "image/png", sizes: "16x16", url: "/icons/favicon-16x16.png" },
1731
{ rel: "icon", type: "image/png", sizes: "192x192", url: "/android-chrome-192x192.png" },
1832
{ rel: "icon", type: "image/png", sizes: "512x512", url: "/android-chrome-512x512.png" }
1933
],
20-
manifest: "/site.webmanifest"
34+
manifest: "/site.webmanifest",
35+
openGraph: {
36+
title: "CodinIT - #1 Open Source AI App Builder",
37+
description: "Open-source alternative to lovable.dev, bolt.new & v0.dev. AI software engineer — live code execution, file uploads, & real-time chat blazing-fast.",
38+
images: ["/opengraph.png"],
39+
url: "https://codinit.dev",
40+
type: "website"
41+
}
2142
}
2243

2344
export default function RootLayout({

0 commit comments

Comments
 (0)