Skip to content

Commit 45fa295

Browse files
authored
Merge pull request #50 from GlebkaF/add-fav
Add favorites page
2 parents daf3b86 + c1a2297 commit 45fa295

File tree

11 files changed

+645
-4
lines changed

11 files changed

+645
-4
lines changed

app/favorites/layout.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Metadata } from "next";
2+
3+
export const metadata: Metadata = {
4+
title: "Favorite Presets | NUX Mighty Plug Pro & Mighty Space | nxrig",
5+
description:
6+
"Your favorite presets for NUX Mighty Plug Pro and Mighty Space. Save and manage your favorite guitar tones.",
7+
alternates: {
8+
canonical: "https://nxrig.com/favorites",
9+
},
10+
openGraph: {
11+
title: "Favorite Presets | NUX Mighty Plug Pro & Mighty Space | nxrig",
12+
description:
13+
"Your favorite presets for NUX Mighty Plug Pro and Mighty Space. Save and manage your favorite guitar tones.",
14+
url: "https://nxrig.com/favorites",
15+
type: "website",
16+
},
17+
twitter: {
18+
card: "summary_large_image",
19+
},
20+
};
21+
22+
export default function FavoritesLayout({
23+
children,
24+
}: {
25+
children: React.ReactNode;
26+
}) {
27+
return children;
28+
}

app/favorites/page.tsx

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"use client";
2+
3+
import { useFavorites } from "hooks/useFavorites";
4+
import { presets } from "lib/public/presets";
5+
import Header from "components/Header";
6+
import Footer from "components/Footer";
7+
import { PresetCard } from "components/PresetCard";
8+
import { ReactElement } from "react";
9+
import Link from "next/link";
10+
11+
export default function FavoritesPage(): ReactElement {
12+
const { favorites, isLoaded } = useFavorites();
13+
14+
// Фильтруем пресеты по избранным ID
15+
const favoritePresets = presets.filter((preset) =>
16+
favorites.includes(preset.id),
17+
);
18+
19+
return (
20+
<div className="min-h-screen flex flex-col bg-gray-900 text-white">
21+
<Header />
22+
<main className="flex-grow">
23+
<div className="container mx-auto px-4 py-8">
24+
<div className="flex items-center gap-4 mb-6">
25+
<svg
26+
width="32"
27+
height="32"
28+
viewBox="0 0 24 24"
29+
fill="currentColor"
30+
className="text-pink-400"
31+
>
32+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
33+
</svg>
34+
<h1 className="text-4xl font-bold text-white">Favorite Presets</h1>
35+
</div>
36+
37+
<p className="mb-8 text-gray-300 leading-relaxed">
38+
Here are all your favorite presets for{" "}
39+
<strong>NUX Mighty Plug Pro</strong> and{" "}
40+
<strong>Mighty Space</strong>. Add your favorite tones to quickly
41+
find them later.
42+
</p>
43+
44+
{!isLoaded ? (
45+
<div className="flex items-center justify-center py-12">
46+
<div className="text-gray-400">Loading favorite presets...</div>
47+
</div>
48+
) : favoritePresets.length === 0 ? (
49+
<div className="text-center py-12">
50+
<div className="mb-4">
51+
<svg
52+
width="64"
53+
height="64"
54+
viewBox="0 0 24 24"
55+
fill="none"
56+
stroke="currentColor"
57+
strokeWidth="1"
58+
strokeLinecap="round"
59+
strokeLinejoin="round"
60+
className="mx-auto text-gray-500"
61+
>
62+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
63+
</svg>
64+
</div>
65+
<h2 className="text-xl font-semibold mb-2 text-gray-300">
66+
No favorite presets yet
67+
</h2>
68+
<p className="text-gray-400 mb-6">
69+
Add presets to favorites by clicking the star on preset cards.
70+
</p>
71+
<Link
72+
href="/preset"
73+
className="inline-flex items-center gap-2 px-6 py-3 bg-pink-500 hover:bg-pink-600 text-white rounded-lg transition-colors"
74+
>
75+
Browse All Presets
76+
</Link>
77+
</div>
78+
) : (
79+
<>
80+
<div className="mb-6 text-sm text-gray-400">
81+
Found {favoritePresets.length} favorite preset
82+
{favoritePresets.length !== 1 ? "s" : ""}
83+
</div>
84+
85+
<div className="grid gap-6">
86+
{favoritePresets.map((preset) => (
87+
<PresetCard key={preset.id} preset={preset} />
88+
))}
89+
</div>
90+
</>
91+
)}
92+
</div>
93+
</main>
94+
<Footer>
95+
<div className="container mx-auto px-4">
96+
<p className="text-gray-300 text-center">
97+
Manage your favorite presets for NUX Mighty Plug Pro and Mighty
98+
Space.
99+
</p>
100+
</div>
101+
</Footer>
102+
</div>
103+
);
104+
}

app/layout.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import "../styles/globals.css";
22
import { Metadata } from "next";
33
import YandexMetrika from "../components/YandexMetrika";
44
import { GoogleTagManager } from "../components/GoogleTagManager";
5+
import { isProduction } from "../lib/env";
56

67
export const metadata: Metadata = {
78
title: "NXRIG",
@@ -15,8 +16,6 @@ export default function RootLayout({
1516
}: {
1617
children: React.ReactNode;
1718
}) {
18-
const isProduction = process.env.NODE_ENV === "production";
19-
2019
return (
2120
<html lang="en">
2221
<head>

components/FavoriteButton.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"use client";
2+
3+
import { useFavorites } from "hooks/useFavorites";
4+
import { ReactElement } from "react";
5+
import { trackAddToFavorites, trackRemoveFromFavorites } from "lib/analytics";
6+
import { presets } from "lib/public/presets";
7+
8+
interface FavoriteButtonProps {
9+
presetId: string;
10+
variant?: "default" | "compact";
11+
className?: string;
12+
}
13+
14+
export function FavoriteButton({
15+
presetId,
16+
variant = "default",
17+
className = "",
18+
}: FavoriteButtonProps): ReactElement {
19+
const { isFavorite, toggleFavorite, isLoaded } = useFavorites();
20+
21+
const isInFavorites = isFavorite(presetId);
22+
23+
// Получаем информацию о пресете для аналитики
24+
const preset = presets.find((p) => p.id === presetId);
25+
const presetName = preset
26+
? `${preset.origin.artist.title} - ${preset.origin.song} ${preset.origin.part}`
27+
: presetId;
28+
29+
// Не показываем кнопку пока не загрузились данные из localStorage
30+
if (!isLoaded) {
31+
return (
32+
<div
33+
className={`${variant === "compact" ? "w-8 h-8" : "w-10 h-10"} ${className}`}
34+
/>
35+
);
36+
}
37+
38+
const handleClick = (e: React.MouseEvent) => {
39+
e.preventDefault(); // Предотвращаем навигацию если кнопка внутри Link
40+
e.stopPropagation();
41+
42+
// Отправляем событие в аналитику перед изменением состояния
43+
if (isInFavorites) {
44+
trackRemoveFromFavorites(presetId, presetName);
45+
} else {
46+
trackAddToFavorites(presetId, presetName);
47+
}
48+
49+
toggleFavorite(presetId);
50+
};
51+
52+
if (variant === "compact") {
53+
return (
54+
<button
55+
onClick={handleClick}
56+
className={`w-8 h-8 flex items-center justify-center rounded-full transition-all duration-200 hover:scale-110 ${
57+
isInFavorites
58+
? "text-pink-400 hover:text-pink-300"
59+
: "text-gray-400 hover:text-pink-400"
60+
} ${className}`}
61+
title={isInFavorites ? "Remove from favorites" : "Add to favorites"}
62+
aria-label={
63+
isInFavorites ? "Remove from favorites" : "Add to favorites"
64+
}
65+
>
66+
<svg
67+
width="20"
68+
height="20"
69+
viewBox="0 0 24 24"
70+
fill={isInFavorites ? "currentColor" : "none"}
71+
stroke="currentColor"
72+
strokeWidth="2"
73+
strokeLinecap="round"
74+
strokeLinejoin="round"
75+
>
76+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
77+
</svg>
78+
</button>
79+
);
80+
}
81+
82+
return (
83+
<button
84+
onClick={handleClick}
85+
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg transition-all duration-200 border ${
86+
isInFavorites
87+
? "bg-pink-500/20 border-pink-500/50 text-pink-300 hover:bg-pink-500/30"
88+
: "bg-gray-700/50 border-gray-600 text-gray-300 hover:bg-gray-600/50 hover:border-pink-500/50 hover:text-pink-400"
89+
} ${className}`}
90+
>
91+
<svg
92+
width="18"
93+
height="18"
94+
viewBox="0 0 24 24"
95+
fill={isInFavorites ? "currentColor" : "none"}
96+
stroke="currentColor"
97+
strokeWidth="2"
98+
strokeLinecap="round"
99+
strokeLinejoin="round"
100+
>
101+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
102+
</svg>
103+
{isInFavorites ? "In Favorites" : "Add to Favorites"}
104+
</button>
105+
);
106+
}

components/Header.tsx

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const Header: React.FC = () => {
1111
return (
1212
<header className="bg-gray-800/50 backdrop-blur-sm border-b border-white/10">
1313
<div className="container mx-auto px-4 py-4">
14-
<div className="flex items-center justify-between w-full">
14+
{/* Desktop Layout */}
15+
<div className="hidden md:flex items-center justify-between w-full">
1516
<div className="flex items-center gap-6">
1617
<Link
1718
href="/"
@@ -27,6 +28,29 @@ const Header: React.FC = () => {
2728

2829
{!isAdminPage && (
2930
<nav className="flex items-center gap-6">
31+
<Link
32+
href="/favorites"
33+
className={`hover:text-pink-400 transition-colors flex items-center gap-2 ${
34+
pathname === "/favorites"
35+
? "text-pink-400 font-medium"
36+
: "text-gray-300"
37+
}`}
38+
title="Favorite Presets"
39+
>
40+
<svg
41+
width="18"
42+
height="18"
43+
viewBox="0 0 24 24"
44+
fill={pathname === "/favorites" ? "currentColor" : "none"}
45+
stroke="currentColor"
46+
strokeWidth="2"
47+
strokeLinecap="round"
48+
strokeLinejoin="round"
49+
>
50+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
51+
</svg>
52+
<span>Favorites</span>
53+
</Link>
3054
<Link
3155
href="/order"
3256
className={`hover:text-pink-400 transition-colors px-4 py-2 rounded-lg border border-pink-500/30 bg-pink-500/10 ${
@@ -40,6 +64,59 @@ const Header: React.FC = () => {
4064
</nav>
4165
)}
4266
</div>
67+
68+
{/* Mobile Layout */}
69+
<div className="md:hidden">
70+
<div className="flex items-center justify-between mb-2">
71+
<Link
72+
href="/"
73+
className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-pink-500 to-violet-500"
74+
>
75+
nxrig.com
76+
</Link>
77+
78+
{!isAdminPage && (
79+
<nav className="flex items-center gap-3">
80+
<Link
81+
href="/favorites"
82+
className={`hover:text-pink-400 transition-colors flex items-center gap-1 ${
83+
pathname === "/favorites"
84+
? "text-pink-400 font-medium"
85+
: "text-gray-300"
86+
}`}
87+
title="Favorite Presets"
88+
>
89+
<svg
90+
width="20"
91+
height="20"
92+
viewBox="0 0 24 24"
93+
fill={pathname === "/favorites" ? "currentColor" : "none"}
94+
stroke="currentColor"
95+
strokeWidth="2"
96+
strokeLinecap="round"
97+
strokeLinejoin="round"
98+
>
99+
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26" />
100+
</svg>
101+
</Link>
102+
<Link
103+
href="/order"
104+
className={`hover:text-pink-400 transition-colors px-3 py-1.5 text-sm rounded-lg border border-pink-500/30 bg-pink-500/10 ${
105+
pathname === "/order"
106+
? "text-pink-400 font-medium border-pink-400"
107+
: "text-pink-300"
108+
}`}
109+
>
110+
Request
111+
</Link>
112+
</nav>
113+
)}
114+
</div>
115+
116+
<p className="text-gray-400 text-sm leading-tight">
117+
Professional presets for NUX Mighty Plug Pro & Mighty Space
118+
</p>
119+
</div>
43120
</div>
44121

45122
{isAdminPage && (

components/PresetCard.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Preset } from "lib/public/interface";
77
import { encodeChain } from "lib/core/encoder";
88
import { createPresetLink } from "lib/utils/urls";
99
import { CompatibleDevices } from "./DeviceBadge";
10+
import { FavoriteButton } from "./FavoriteButton";
1011

1112
interface PresetCardProps {
1213
preset: Preset;
@@ -59,6 +60,11 @@ export function PresetCard({ preset }: PresetCardProps): React.ReactElement {
5960
<CompatibleDevices />
6061
</div>
6162
</div>
63+
64+
{/* Favorite Button */}
65+
<div className="shrink-0">
66+
<FavoriteButton presetId={preset.id} variant="compact" />
67+
</div>
6268
</div>
6369
</div>
6470
</Link>

0 commit comments

Comments
 (0)