feat: complete new UI for Profiles, fix notfound error on username change
This commit is contained in:
@ -2,6 +2,9 @@
|
||||
|
||||
import { revalidatePath } from "next/cache"
|
||||
import { z } from "zod"
|
||||
import { editUserSchema } from "./schema"
|
||||
import { UserLink } from "./types"
|
||||
import { parseSocialLink } from "./utils"
|
||||
|
||||
export async function createSandbox(body: {
|
||||
type: string
|
||||
@ -123,20 +126,31 @@ const UpdateErrorSchema = z.object({
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export async function updateUser(prevState: any, formData: FormData) {
|
||||
const data = Object.fromEntries(formData)
|
||||
|
||||
const schema = z.object({
|
||||
id: z.string(),
|
||||
username: z.string(),
|
||||
oldUsername: z.string(),
|
||||
name: z.string(),
|
||||
interface FormState {
|
||||
message: string
|
||||
error?: any
|
||||
newRoute?: string
|
||||
fields?: Record<string, unknown>
|
||||
}
|
||||
export async function updateUser(
|
||||
prevState: any,
|
||||
formData: FormData
|
||||
): Promise<FormState> {
|
||||
let data = Object.fromEntries(formData)
|
||||
let links: UserLink[] = []
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (key.startsWith("link")) {
|
||||
const [_, index] = key.split(".")
|
||||
if (value) {
|
||||
links.splice(parseInt(index), 0, parseSocialLink(value as string))
|
||||
delete data[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log(data)
|
||||
|
||||
// @ts-ignore
|
||||
data.links = links
|
||||
try {
|
||||
const validatedData = schema.parse(data)
|
||||
|
||||
const validatedData = editUserSchema.parse(data)
|
||||
const changedUsername = validatedData.username !== validatedData.oldUsername
|
||||
const res = await fetch(
|
||||
`${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`,
|
||||
@ -150,6 +164,9 @@ export async function updateUser(prevState: any, formData: FormData) {
|
||||
id: validatedData.id,
|
||||
username: data.username ?? undefined,
|
||||
name: data.name ?? undefined,
|
||||
bio: data.bio ?? undefined,
|
||||
personalWebsite: data.personalWebsite ?? undefined,
|
||||
links: data.links ?? undefined,
|
||||
}),
|
||||
}
|
||||
)
|
||||
@ -160,11 +177,11 @@ export async function updateUser(prevState: any, formData: FormData) {
|
||||
const parseResult = UpdateErrorSchema.safeParse(responseData)
|
||||
|
||||
if (!parseResult.success) {
|
||||
return { error: "Unexpected error occurred" }
|
||||
}
|
||||
|
||||
if (parseResult.data.error) {
|
||||
return parseResult.data
|
||||
return {
|
||||
message: "Unexpected error occurred",
|
||||
error: parseResult.error,
|
||||
fields: validatedData,
|
||||
}
|
||||
}
|
||||
|
||||
if (changedUsername) {
|
||||
@ -175,12 +192,13 @@ export async function updateUser(prevState: any, formData: FormData) {
|
||||
return { message: "Successfully updated" }
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
console.log(error)
|
||||
return {
|
||||
error: error.errors?.[0].message,
|
||||
message: "Invalid data",
|
||||
error: error.errors,
|
||||
fields: data,
|
||||
}
|
||||
}
|
||||
|
||||
return { error: "An unexpected error occurred" }
|
||||
return { message: "An unexpected error occurred", fields: data }
|
||||
}
|
||||
}
|
||||
|
14
frontend/lib/constants/index.ts
Normal file
14
frontend/lib/constants/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export const KNOWN_PLATFORMS = [
|
||||
"github",
|
||||
"twitter",
|
||||
"instagram",
|
||||
"bluesky",
|
||||
"linkedin",
|
||||
"youtube",
|
||||
"twitch",
|
||||
"discord",
|
||||
"mastodon",
|
||||
"threads",
|
||||
"gitlab",
|
||||
"generic",
|
||||
] as const
|
@ -1,3 +1,37 @@
|
||||
import {
|
||||
AtSign,
|
||||
Github,
|
||||
GitlabIcon as GitlabLogo,
|
||||
Globe,
|
||||
Instagram,
|
||||
Link,
|
||||
Linkedin,
|
||||
MessageCircle,
|
||||
Twitch,
|
||||
Twitter,
|
||||
Youtube,
|
||||
} from "lucide-react"
|
||||
import { KnownPlatform } from "../types"
|
||||
|
||||
export const socialIcons: Record<
|
||||
KnownPlatform | "website",
|
||||
React.ComponentType<any>
|
||||
> = {
|
||||
github: Github,
|
||||
twitter: Twitter,
|
||||
instagram: Instagram,
|
||||
bluesky: AtSign,
|
||||
linkedin: Linkedin,
|
||||
youtube: Youtube,
|
||||
twitch: Twitch,
|
||||
discord: MessageCircle,
|
||||
mastodon: AtSign,
|
||||
threads: AtSign,
|
||||
gitlab: GitlabLogo,
|
||||
generic: Link,
|
||||
website: Globe,
|
||||
}
|
||||
|
||||
export const projectTemplates: {
|
||||
id: string
|
||||
name: string
|
||||
|
20
frontend/lib/schema/index.ts
Normal file
20
frontend/lib/schema/index.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { z } from "zod"
|
||||
import { KNOWN_PLATFORMS } from "../constants"
|
||||
|
||||
export const editUserSchema = z.object({
|
||||
id: z.string().trim(),
|
||||
username: z.string().trim().min(1, "Username must be at least 1 character"),
|
||||
oldUsername: z.string().trim(),
|
||||
name: z.string().trim().min(1, "Name must be at least 1 character"),
|
||||
bio: z.string().trim().optional(),
|
||||
personalWebsite: z.string().trim().optional(),
|
||||
links: z
|
||||
.array(
|
||||
z.object({
|
||||
url: z.string().trim(),
|
||||
platform: z.enum(KNOWN_PLATFORMS),
|
||||
})
|
||||
)
|
||||
.catch([]),
|
||||
})
|
||||
export type EditUserSchema = z.infer<typeof editUserSchema>
|
@ -1,5 +1,7 @@
|
||||
// DB Types
|
||||
|
||||
import { KNOWN_PLATFORMS } from "./constants"
|
||||
|
||||
export type User = {
|
||||
id: string
|
||||
name: string
|
||||
@ -8,11 +10,20 @@ export type User = {
|
||||
avatarUrl: string | null
|
||||
createdAt: Date
|
||||
generations: number
|
||||
sandbox: Sandbox[]
|
||||
usersToSandboxes: UsersToSandboxes[]
|
||||
bio: string | null
|
||||
personalWebsite: string | null
|
||||
links: UserLink[]
|
||||
tier: "FREE" | "PRO" | "ENTERPRISE"
|
||||
tierExpiresAt: Date
|
||||
lastResetDate?: number
|
||||
lastResetDate: number
|
||||
sandbox: Sandbox[]
|
||||
usersToSandboxes: UsersToSandboxes[]
|
||||
}
|
||||
|
||||
export type KnownPlatform = (typeof KNOWN_PLATFORMS)[number]
|
||||
export type UserLink = {
|
||||
url: string
|
||||
platform: KnownPlatform
|
||||
}
|
||||
|
||||
export type Sandbox = {
|
||||
|
@ -2,7 +2,7 @@ import { type ClassValue, clsx } from "clsx"
|
||||
// import { toast } from "sonner"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import fileExtToLang from "./file-extension-to-language.json"
|
||||
import { TFile, TFolder } from "./types"
|
||||
import { KnownPlatform, TFile, TFolder, UserLink } from "./types"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@ -98,3 +98,57 @@ export function sortFileExplorer(
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
export function parseSocialLink(url: string): UserLink {
|
||||
try {
|
||||
// Handle empty or invalid URLs
|
||||
if (!url) return { url: "", platform: "generic" }
|
||||
|
||||
// Remove protocol and www prefix for consistent parsing
|
||||
const cleanUrl = url
|
||||
.toLowerCase()
|
||||
.replace(/^https?:\/\//, "")
|
||||
.replace(/^www\./, "")
|
||||
.split("/")[0] // Get just the domain part
|
||||
|
||||
// Platform detection mapping
|
||||
const platformPatterns: Record<
|
||||
Exclude<KnownPlatform, "generic">,
|
||||
RegExp
|
||||
> = {
|
||||
github: /github\.com/,
|
||||
twitter: /(?:twitter\.com|x\.com|t\.co)/,
|
||||
instagram: /instagram\.com/,
|
||||
bluesky: /(?:bsky\.app|bluesky\.social)/,
|
||||
linkedin: /linkedin\.com/,
|
||||
youtube: /(?:youtube\.com|youtu\.be)/,
|
||||
twitch: /twitch\.tv/,
|
||||
discord: /discord\.(?:gg|com)/,
|
||||
mastodon: /mastodon\.(?:social|online|world)/,
|
||||
threads: /threads\.net/,
|
||||
gitlab: /gitlab\.com/,
|
||||
}
|
||||
|
||||
// Check URL against each pattern
|
||||
for (const [platform, pattern] of Object.entries(platformPatterns)) {
|
||||
if (pattern.test(cleanUrl)) {
|
||||
return {
|
||||
url,
|
||||
platform: platform as KnownPlatform,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to generic if no match found
|
||||
return {
|
||||
url,
|
||||
platform: "generic",
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing social link:", error)
|
||||
return {
|
||||
url: url || "",
|
||||
platform: "generic",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user