diff --git a/packages/skin-database/app/(modern)/scroll/BottomMenuBar.tsx b/packages/skin-database/app/(modern)/scroll/BottomMenuBar.tsx
index 4cab5632..86f753f2 100644
--- a/packages/skin-database/app/(modern)/scroll/BottomMenuBar.tsx
+++ b/packages/skin-database/app/(modern)/scroll/BottomMenuBar.tsx
@@ -57,7 +57,7 @@ export default function BottomMenuBar() {
{isHamburgerOpen && (
}
label="About"
onClick={() => {
@@ -87,13 +89,12 @@ export default function BottomMenuBar() {
}}
/>{" "}
}
label="Feedback"
onClick={() => {
setIsHamburgerOpen(false);
}}
- external
/>
+
+ {children}
+
+
+ );
+}
+
+// Styled heading components
+export function Heading({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+export function Subheading({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+// Styled link component
+export function Link({
+ href,
+ children,
+ ...props
+}: {
+ href: string;
+ children: ReactNode;
+ target?: string;
+ rel?: string;
+}) {
+ return (
+
+ {children}
+
+ );
+}
+
+// Styled paragraph component
+export function Paragraph({ children }: { children: ReactNode }) {
+ return
{children}
;
+}
+
+// Styled form components
+export function Label({ children }: { children: ReactNode }) {
+ return (
+
+ );
+}
+
+export function Input({
+ style,
+ ...props
+}: React.InputHTMLAttributes
& { style?: CSSProperties }) {
+ return (
+
+ );
+}
+
+export function Textarea({
+ style,
+ ...props
+}: React.TextareaHTMLAttributes & {
+ style?: CSSProperties;
+}) {
+ return (
+
+ );
+}
+
+export function Button({
+ style,
+ disabled,
+ ...props
+}: React.ButtonHTMLAttributes & { style?: CSSProperties }) {
+ return (
+
+ );
+}
diff --git a/packages/skin-database/app/(modern)/scroll/about/page.tsx b/packages/skin-database/app/(modern)/scroll/about/page.tsx
new file mode 100644
index 00000000..9c6c8ed3
--- /dev/null
+++ b/packages/skin-database/app/(modern)/scroll/about/page.tsx
@@ -0,0 +1,67 @@
+import StaticPage, {
+ Heading,
+ Subheading,
+ Link,
+ Paragraph,
+} from "../StaticPage";
+
+export default function AboutPage() {
+ return (
+
+ About
+
+ The Winamp Skin Museum is an attempt to build a fast,{" "}
+ searchable, and shareable, interface for the collection of
+ Winamp Skins amassed on the{" "}
+
+ Internet Archive
+
+ .
+
+ Features:
+
+ -
+ Infinite scroll preview images
+
+ -
+ Experience skins with integrated{" "}
+
+ Webamp
+
+
+ -
+ Fast search of indexed readme.txt texts
+
+
+
+ Made by Jordan Eldredge
+
+
+
+ Want Winamp on your Windows PC, but with supported updates & new
+ features?{" "}
+
+ Try WACUP
+
+
+
+ );
+}
diff --git a/packages/skin-database/app/(modern)/scroll/feedback/page.tsx b/packages/skin-database/app/(modern)/scroll/feedback/page.tsx
new file mode 100644
index 00000000..3985d4fd
--- /dev/null
+++ b/packages/skin-database/app/(modern)/scroll/feedback/page.tsx
@@ -0,0 +1,117 @@
+"use client";
+
+import { useState, useCallback } from "react";
+import { usePathname } from "next/navigation";
+import StaticPage, {
+ Heading,
+ Paragraph,
+ Label,
+ Input,
+ Textarea,
+ Button,
+} from "../StaticPage";
+
+async function sendFeedback(variables: {
+ message: string;
+ email: string;
+ url: string;
+}) {
+ const mutation = `
+ mutation GiveFeedback($message: String!, $email: String, $url: String) {
+ send_feedback(message: $message, email: $email, url: $url)
+ }
+ `;
+
+ const response = await fetch("/api/graphql", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: mutation,
+ variables,
+ }),
+ });
+
+ if (!response.ok) {
+ throw new Error("Failed to send feedback");
+ }
+
+ return response.json();
+}
+
+export default function FeedbackPage() {
+ const pathname = usePathname();
+ const [message, setMessage] = useState("");
+ const [email, setEmail] = useState("");
+ const [sending, setSending] = useState(false);
+ const [sent, setSent] = useState(false);
+
+ const send = useCallback(async () => {
+ if (message.trim().length === 0) {
+ alert("Please add a message before sending.");
+ return;
+ }
+ const body = {
+ message,
+ email,
+ url: "https://skins.webamp.org" + pathname,
+ };
+ setSending(true);
+ try {
+ await sendFeedback(body);
+ setSent(true);
+ } catch (error) {
+ alert("Failed to send feedback. Please try again.");
+ setSending(false);
+ }
+ }, [message, email, pathname]);
+
+ if (sent) {
+ return (
+
+ Sent!
+
+ Thanks for your feedback. I appreciate you taking the time to share
+ your thoughts.
+
+
+ );
+ }
+
+ return (
+
+ Feedback
+
+ Let me know what you think about the Winamp Skin Museum. Bug reports,
+ feature suggestions, personal anecdotes, or criticism are all welcome.
+
+
+
+
+
+
+ setEmail(e.target.value)}
+ placeholder="user@example.com"
+ />
+
+
+
+
+
+
+ );
+}