Skip to content

Commit f987ac6

Browse files
committed
feat: discovery logs view options
1 parent 2c8be34 commit f987ac6

File tree

2 files changed

+159
-21
lines changed

2 files changed

+159
-21
lines changed

src/components/LogCard.jsx

Lines changed: 108 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import React from 'react';
22
import { Link } from 'react-router-dom';
3-
import { ArrowRight, Star, CalendarBlank } from '@phosphor-icons/react';
3+
import {
4+
ArrowRightIcon,
5+
StarIcon,
6+
CalendarBlankIcon,
7+
} from '@phosphor-icons/react';
48
import { motion } from 'framer-motion';
59
import GenerativeArt from './GenerativeArt';
610
import colors from '../config/colors';
711

8-
const LogCard = ({ log, index, totalLogs }) => {
12+
const LogCard = ({ log, index, totalLogs, viewMode = 'grid' }) => {
913
const {
1014
title,
1115
category,
@@ -29,6 +33,105 @@ const LogCard = ({ log, index, totalLogs }) => {
2933
const categoryColorLight =
3034
colors[category.toLowerCase() + '-light'] || categoryColor;
3135

36+
if (viewMode === 'list') {
37+
return (
38+
<motion.div
39+
whileHover={{ x: 10 }}
40+
className="group relative flex items-center gap-4 py-4 border-b border-white/5 bg-transparent hover:bg-white/[0.02] transition-colors px-4"
41+
>
42+
<Link
43+
to={`/logs/${category.toLowerCase()}/${slug}`}
44+
className="flex items-center gap-6 w-full"
45+
>
46+
{/* Index & Date */}
47+
<div className="flex flex-col items-start gap-1 min-w-[100px]">
48+
<span className="font-mono text-xs text-gray-400 uppercase tracking-widest">
49+
#{String(totalLogs - index).padStart(3, '0')}
50+
</span>
51+
<span className="font-mono text-xs text-gray-400 uppercase flex items-center gap-1">
52+
<CalendarBlankIcon size={14} />
53+
{date}
54+
</span>
55+
</div>
56+
57+
{/* Visual Thumbnail (Small) */}
58+
<div className="hidden sm:block h-14 w-14 flex-shrink-0 overflow-hidden rounded-sm border border-white/10 grayscale group-hover:grayscale-0 transition-all">
59+
{image ? (
60+
<img
61+
src={image}
62+
alt={title}
63+
className="w-full h-full object-cover"
64+
/>
65+
) : (
66+
<GenerativeArt
67+
seed={title + category}
68+
className="w-full h-full opacity-60"
69+
/>
70+
)}
71+
</div>
72+
73+
{/* Main Content */}
74+
<div className="flex-grow min-w-0">
75+
<div className="flex items-center gap-3 mb-1.5">
76+
<span
77+
className="px-2 py-0.5 text-[10px] font-mono font-bold uppercase tracking-widest border rounded-sm"
78+
style={{
79+
color: categoryColor,
80+
backgroundColor: `${categoryColor}10`,
81+
borderColor: `${categoryColor}20`,
82+
}}
83+
>
84+
{category}
85+
</span>
86+
{rating > 0 && (
87+
<div className="flex items-center gap-1.5">
88+
<span className="font-mono text-xs font-bold text-white">
89+
{rating}
90+
</span>
91+
<div className="flex gap-0.5">
92+
{[...Array(5)].map((_, i) => (
93+
<StarIcon
94+
key={i}
95+
weight="fill"
96+
size={10}
97+
className={i < rating ? 'text-yellow-500' : 'text-white/10'}
98+
/>
99+
))}
100+
</div>
101+
</div>
102+
)}
103+
</div>
104+
<h3
105+
className="text-lg font-medium font-sans uppercase truncate transition-colors"
106+
style={{ color: categoryColorLight }}
107+
>
108+
{title}
109+
</h3>
110+
{creatorName && (
111+
<p className="text-xs text-gray-400 font-mono truncate uppercase mt-0.5">
112+
BY {creatorName}
113+
</p>
114+
)}
115+
</div>
116+
117+
{/* Source & Action */}
118+
<div className="hidden md:flex flex-col items-end gap-1 min-w-[140px] text-right">
119+
{sourceName && (
120+
<span className="text-xs text-gray-400 font-mono uppercase truncate w-full">
121+
{sourceName}
122+
</span>
123+
)}
124+
<ArrowRightIcon
125+
weight="bold"
126+
size={16}
127+
className="text-emerald-500 opacity-0 group-hover:opacity-100 transition-opacity"
128+
/>
129+
</div>
130+
</Link>
131+
</motion.div>
132+
);
133+
}
134+
32135
return (
33136
<motion.div
34137
whileHover={{ y: -5 }}
@@ -78,7 +181,7 @@ const LogCard = ({ log, index, totalLogs }) => {
78181
<span className="font-mono text-xs font-bold text-white">
79182
{rating}
80183
</span>
81-
<Star weight="fill" size={10} className="text-yellow-500" />
184+
<StarIcon weight="fill" size={10} className="text-yellow-500" />
82185
</div>
83186
)}
84187
</div>
@@ -87,7 +190,7 @@ const LogCard = ({ log, index, totalLogs }) => {
87190
<div className="flex flex-col flex-grow p-5">
88191
<div className="flex items-center gap-2 mb-3">
89192
<span className="font-mono text-[9px] text-gray-500 uppercase tracking-widest flex items-center gap-1">
90-
<CalendarBlank size={12} />
193+
<CalendarBlankIcon size={12} />
91194
{date}
92195
</span>
93196
<span className="text-gray-700 text-[9px]"></span>
@@ -120,7 +223,7 @@ const LogCard = ({ log, index, totalLogs }) => {
120223
<span className="text-[10px] font-mono font-bold uppercase tracking-widest text-gray-500 group-hover:text-white transition-colors">
121224
View Log
122225
</span>
123-
<ArrowRight
226+
<ArrowRightIcon
124227
weight="bold"
125228
size={14}
126229
className="text-emerald-500 transform -translate-x-2 opacity-0 transition-all duration-300 group-hover:translate-x-0 group-hover:opacity-100"

src/pages/LogsPage.jsx

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import React, { useState, useEffect } from 'react';
22
import { Link } from 'react-router-dom';
3-
import { ArrowLeft, Funnel, XCircle, Info } from '@phosphor-icons/react';
3+
import {
4+
ArrowLeftIcon,
5+
FunnelIcon,
6+
XCircleIcon,
7+
InfoIcon,
8+
SquaresFourIcon,
9+
ListIcon,
10+
} from '@phosphor-icons/react';
411
import { motion, AnimatePresence } from 'framer-motion';
512
import LogCard from '../components/LogCard';
613
import useSeo from '../hooks/useSeo';
14+
import usePersistentState from '../hooks/usePersistentState';
715
import colors from '../config/colors';
816
import { useAchievements } from '../context/AchievementContext';
917
import piml from 'piml';
@@ -39,6 +47,7 @@ const LogsPage = () => {
3947
const [searchQuery, setSearchQuery] = useState('');
4048
const [filteredLogs, setFilteredLogs] = useState([]);
4149
const [isInfoModalOpen, setIsInfoModalOpen] = useState(false);
50+
const [viewMode, setViewMode] = usePersistentState('fez_logs_view_mode', 'grid');
4251
const { unlockAchievement } = useAchievements();
4352
const { openSidePanel } = useSidePanel();
4453

@@ -110,7 +119,7 @@ const LogsPage = () => {
110119
to="/"
111120
className="mb-8 inline-flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-white transition-colors uppercase tracking-widest"
112121
>
113-
<ArrowLeft weight="bold" />
122+
<ArrowLeftIcon weight="bold" />
114123
<span>Home</span>
115124
</Link>
116125

@@ -124,13 +133,32 @@ const LogsPage = () => {
124133
</p>
125134
</div>
126135

127-
<button
128-
onClick={() => setIsInfoModalOpen(true)}
129-
className="text-gray-500 hover:text-emerald-400 transition-colors font-mono text-xs uppercase tracking-widest flex items-center gap-2"
130-
>
131-
<Info size={16} />
132-
<span>Rating System</span>
133-
</button>
136+
<div className="flex items-center gap-6">
137+
<div className="flex bg-white/5 p-1 rounded-sm border border-white/10">
138+
<button
139+
onClick={() => setViewMode('grid')}
140+
className={`p-2 transition-all ${viewMode === 'grid' ? 'bg-white text-black' : 'text-gray-500 hover:text-white'}`}
141+
title="Grid View"
142+
>
143+
<SquaresFourIcon size={18} weight={viewMode === 'grid' ? 'fill' : 'regular'} />
144+
</button>
145+
<button
146+
onClick={() => setViewMode('list')}
147+
className={`p-2 transition-all ${viewMode === 'list' ? 'bg-white text-black' : 'text-gray-500 hover:text-white'}`}
148+
title="List View"
149+
>
150+
<ListIcon size={18} weight={viewMode === 'list' ? 'fill' : 'regular'} />
151+
</button>
152+
</div>
153+
154+
<button
155+
onClick={() => setIsInfoModalOpen(true)}
156+
className="text-gray-500 hover:text-emerald-400 transition-colors font-mono text-xs uppercase tracking-widest flex items-center gap-2"
157+
>
158+
<InfoIcon size={16} />
159+
<span>Rating System</span>
160+
</button>
161+
</div>
134162
</div>
135163
</header>
136164

@@ -150,7 +178,7 @@ const LogsPage = () => {
150178
{/* Filters */}
151179
<div className="flex flex-wrap items-center gap-3">
152180
<div className="flex items-center gap-2 mr-2 text-gray-600 font-mono text-xs uppercase tracking-widest">
153-
<Funnel size={14} weight="fill" />
181+
<FunnelIcon size={14} weight="fill" />
154182
<span>Filter By</span>
155183
</div>
156184

@@ -190,39 +218,46 @@ const LogsPage = () => {
190218
}}
191219
className="ml-auto text-red-500 hover:text-red-400 transition-colors"
192220
>
193-
<XCircle size={20} weight="fill" />
221+
<XCircleIcon size={20} weight="fill" />
194222
</button>
195223
)}
196224
</div>
197225
</div>
198226

199227
{/* Content List */}
200228
{loading ? (
201-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 animate-pulse">
229+
<div className={viewMode === 'grid'
230+
? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 animate-pulse"
231+
: "flex flex-col gap-4 animate-pulse"
232+
}>
202233
{[...Array(8)].map((_, i) => (
203-
<div key={i} className="h-64 w-full bg-white/5 rounded-sm" />
234+
<div key={i} className={viewMode === 'grid' ? "h-64 w-full bg-white/5 rounded-sm" : "h-20 w-full bg-white/5 rounded-sm"} />
204235
))}
205236
</div>
206237
) : (
207238
<div className="pb-32">
208239
<AnimatePresence mode="popLayout">
209240
<motion.div
210241
layout
211-
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"
242+
className={viewMode === 'grid'
243+
? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"
244+
: "flex flex-col"
245+
}
212246
>
213247
{filteredLogs.map((log) => (
214248
<motion.div
215249
layout
216250
key={log.id}
217-
initial={{ opacity: 0, scale: 0.9 }}
251+
initial={{ opacity: 0, scale: 0.95 }}
218252
animate={{ opacity: 1, scale: 1 }}
219-
exit={{ opacity: 0, scale: 0.9 }}
253+
exit={{ opacity: 0, scale: 0.95 }}
220254
transition={{ duration: 0.2 }}
221255
>
222256
<LogCard
223257
log={log}
224258
index={log.originalIndex}
225259
totalLogs={logs.length}
260+
viewMode={viewMode}
226261
/>
227262
</motion.div>
228263
))}

0 commit comments

Comments
 (0)