diff --git a/src/app/dashboard/settings/page.tsx b/src/app/dashboard/settings/page.tsx index 9207cbab..2f4be9b2 100644 --- a/src/app/dashboard/settings/page.tsx +++ b/src/app/dashboard/settings/page.tsx @@ -1,5 +1,6 @@ "use client"; +import ThemePresetPicker from "@/components/ThemePresetPicker"; import { Suspense, useEffect, useMemo, useState } from "react"; import { useSession } from "next-auth/react"; import { redirect, useSearchParams } from "next/navigation"; @@ -17,11 +18,7 @@ interface UserSettings { bio: string; is_public: boolean; leaderboard_opt_in: boolean; - weekly_digest_opt_in: boolean; has_wakatime_key?: boolean; - discord_webhook_url?: string; - timezone?: string; - pinned_repos?: string[]; } interface LinkedAccount { @@ -133,19 +130,10 @@ function SettingsPageContent() { const [showBioPreview, setShowBioPreview] = useState(false); const [savingBio, setSavingBio] = useState(false); const [savingWakatime, setSavingWakatime] = useState(false); - const [discordWebhook, setDiscordWebhook] = useState(""); - const [timezone, setTimezone] = useState(""); - const [savingDiscord, setSavingDiscord] = useState(false); - const [testingDiscord, setTestingDiscord] = useState(false); const [isDirty, setIsDirty] = useState(false); const [showConfirmModal, setShowConfirmModal] = useState(false); const [pendingPath, setPendingPath] = useState(null); - // Spotlight Repos States - const [userRepos, setUserRepos] = useState([]); - const [loadingRepos, setLoadingRepos] = useState(false); - const [repoSearchQuery, setRepoSearchQuery] = useState(""); - const statusMessage = useMemo( () => getStatusMessage(searchParams.get("success"), searchParams.get("error")), @@ -237,12 +225,10 @@ function SettingsPageContent() { try { const res = await fetch("/api/user/settings"); if (res.ok) { - const data = await res.json(); - setSettings(data); - setBioDraft(data.bio ?? ""); - setDiscordWebhook(data.discord_webhook_url || ""); - setTimezone(data.timezone || "UTC"); - } + const data = await res.json(); + setSettings(data); + setBioDraft(data.bio ?? ""); +} } catch (error) { console.error("Failed to load settings:", error); } finally { @@ -253,78 +239,6 @@ function SettingsPageContent() { loadSettings(); }, [session, status]); - // Load active repos for spotlight pinning - useEffect(() => { - if (status !== "authenticated") return; - setLoadingRepos(true); - fetch("/api/metrics/repos?days=90") - .then((r) => r.json()) - .then((d) => { - const names = (d.repos ?? []).map((r: any) => r.name); - setUserRepos(names); - }) - .catch((err) => console.error("Failed to load user repos:", err)) - .finally(() => setLoadingRepos(false)); - }, [status]); - - const handleUpdatePinnedRepos = async (newPins: string[]) => { - if (!settings) return; - setSaving(true); - try { - const res = await fetch("/api/user/settings", { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ pinned_repos: newPins }), - }); - if (res.ok) { - const updated = await res.json(); - setSettings(updated); - toast.success("Spotlight repositories updated successfully!"); - } else { - toast.error("Failed to update spotlight repositories."); - } - } catch (err) { - console.error(err); - toast.error("Error updating spotlight repositories."); - } finally { - setSaving(false); - } - }; - - const handlePinRepo = async (repoName: string) => { - if (!settings) return; - const currentPins = settings.pinned_repos || []; - if (currentPins.includes(repoName)) return; - if (currentPins.length >= 3) { - toast.error("Maximum 3 pinned repositories allowed!"); - return; - } - - const updatedPins = [...currentPins, repoName]; - await handleUpdatePinnedRepos(updatedPins); - }; - - const handleUnpinRepo = async (repoName: string) => { - if (!settings) return; - const currentPins = settings.pinned_repos || []; - const updatedPins = currentPins.filter((name) => name !== repoName); - await handleUpdatePinnedRepos(updatedPins); - }; - - const handleMovePin = async (index: number, direction: "up" | "down") => { - if (!settings) return; - const currentPins = [...(settings.pinned_repos || [])]; - const targetIndex = direction === "up" ? index - 1 : index + 1; - if (targetIndex < 0 || targetIndex >= currentPins.length) return; - - // Swap elements - const temp = currentPins[index]; - currentPins[index] = currentPins[targetIndex]; - currentPins[targetIndex] = temp; - - await handleUpdatePinnedRepos(currentPins); - }; - useEffect(() => { if (status !== "authenticated" || !session?.githubLogin) { return; @@ -402,30 +316,6 @@ function SettingsPageContent() { } }; - const handleToggleWeeklyDigest = async (value: boolean) => { - if (!settings) return; - - setSaving(true); - try { - const res = await fetch("/api/user/settings", { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ weekly_digest_opt_in: value }), - }); - - if (res.ok) { - const updated = await res.json(); - setSettings(updated); - } else { - console.error("Failed to update weekly digest setting"); - } - } catch (error) { - console.error("Error updating weekly digest setting:", error); - } finally { - setSaving(false); - } - }; - const handleSaveWakatime = async () => { if (!settings) return; setSavingWakatime(true); @@ -453,6 +343,7 @@ function SettingsPageContent() { } }; + const handleSaveBio = async () => { if (!settings || bioDraft.length > 500) return; @@ -481,59 +372,8 @@ function SettingsPageContent() { setSavingBio(false); } }; - - const handleSaveDiscord = async () => { - if (!settings) return; - setSavingDiscord(true); - try { - const res = await fetch("/api/user/settings", { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ discord_webhook_url: discordWebhook, timezone }), - }); - if (res.ok) { - const updated = await res.json(); - setSettings(updated); - setIsDirty(false); - toast.success(discordWebhook === "" ? "Discord Webhook removed" : "Discord settings saved successfully!"); - } else { - const errorData = await res.json(); - toast.error(errorData.error || "Failed to update Discord settings"); - } - } catch (error) { - console.error("Error updating Discord settings:", error); - toast.error("Failed to update Discord settings"); - } finally { - setSavingDiscord(false); - } - }; - - const handleTestDiscord = async () => { - if (!discordWebhook) { - toast.error("Please enter a Webhook URL first"); - return; - } - setTestingDiscord(true); - try { - const res = await fetch("/api/user/settings/discord-test", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ webhookUrl: discordWebhook }), - }); - if (res.ok) { - toast.success("Test notification sent! Check your Discord server."); - } else { - const errorData = await res.json(); - toast.error(errorData.error || "Failed to send test notification"); - } - } catch (error) { - console.error("Error sending test notification:", error); - toast.error("Failed to send test notification"); - } finally { - setTestingDiscord(false); - } - }; - + + const copyShareLink = () => { if (!settings) return; const link = `${window.location.origin}/u/${settings.github_login}`; @@ -632,11 +472,10 @@ function SettingsPageContent() { {statusMessage && (
{statusMessage.message}
@@ -841,6 +680,20 @@ function SettingsPageContent() { )} +
+
+

+ Dashboard Theme +

+ +

+ Personalize your dashboard appearance with curated developer themes. +

+
+ + +
+
@@ -884,174 +737,6 @@ function SettingsPageContent() {
- {/* Repository Spotlight Section */} -
-

- Repository Spotlight 🚀 -

-

- Pin up to 3 repositories to showcase on your dashboard and public profile. -

- - {/* Currently Pinned */} -
-

- Pinned Repositories ({(settings.pinned_repos || []).length}/3) -

- {(settings.pinned_repos || []).length === 0 ? ( -
- No repositories pinned yet. Use the search below to spotlight your best projects! -
- ) : ( -
- {(settings.pinned_repos || []).map((repoName, index) => ( -
-
- - {repoName} - -
- -
- {/* Reorder Buttons */} - - - - {/* Unpin Button */} - -
-
- ))} -
- )} -
- - {/* Pin New Repos (Search) */} - {(settings.pinned_repos || []).length < 3 && ( -
-

- Search & Pin Repositories -

- setRepoSearchQuery(e.target.value)} - placeholder="Type to search your repositories..." - aria-label="Search repositories to pin" - className="w-full rounded-lg border border-[var(--border)] bg-[var(--control)] px-4 py-2 text-sm text-[var(--card-foreground)] placeholder:text-[var(--muted-foreground)] focus:outline-none focus:ring-2 focus:ring-[var(--accent)] mb-4" - /> - - {loadingRepos ? ( -
- Loading your repositories... -
- ) : ( -
- {userRepos - .filter( - (repoName) => - !(settings.pinned_repos || []).includes(repoName) && - repoName.toLowerCase().includes(repoSearchQuery.toLowerCase()) - ) - .slice(0, 5) - .map((repoName) => ( -
- - {repoName} - - -
- ))} - {userRepos.filter( - (repoName) => - !(settings.pinned_repos || []).includes(repoName) && - repoName.toLowerCase().includes(repoSearchQuery.toLowerCase()) - ).length === 0 && ( -
- No repositories available to pin. -
- )} -
- )} -
- )} -
- -
-
-
-

- Weekly Email Digest -

-

- Receive an optional weekly email digest every Monday morning summarizing your coding habits. -

-
- -