Skip to content

Commit 195be8c

Browse files
committed
feat: badge to indicate archieved statements
1 parent a681386 commit 195be8c

File tree

5 files changed

+186
-39
lines changed

5 files changed

+186
-39
lines changed

src/components/statementWizard/selectors/VerbSelector.tsx renamed to src/components/VerbSelector.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import type React from 'react';
44
import { useState, useEffect } from 'react';
55
import { motion } from 'framer-motion';
66
import { ChevronLeft, ChevronRight } from 'lucide-react';
7-
import categoryStructure from '../../../../data/categoryStructure.json';
8-
import verbData from '../../../../data/verbs.json';
9-
import type { Category, Verb } from '../../../../types/entries';
7+
import categoryStructure from '../../data/categoryStructure.json';
8+
import verbData from '../../data/verbs.json';
9+
import type { Category, Verb } from '../../types/entries';
1010

1111
interface VerbSelectorProps {
1212
onVerbSelect?: (verb: Verb) => void;

src/components/statements/QuestionCard.tsx

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,99 @@
22

33
import React from 'react';
44
import type { SetQuestion } from '../../../types/entries';
5-
import { HelpCircle, ChevronRight } from 'lucide-react';
5+
import { HelpCircle, ChevronRight, BellOff, Bell } from 'lucide-react';
66
import { cn } from '../../../lib/utils';
77
import { Tooltip, TooltipTrigger, TooltipContent } from '../ui/tooltip';
88

99
export interface QuestionCardProps {
1010
presetQuestion: SetQuestion;
1111
onSelect: (presetQuestion: SetQuestion) => void;
12+
onToggleSnooze?: (questionId: string) => void;
1213
}
1314

1415
const QuestionCard: React.FC<QuestionCardProps> = ({
1516
presetQuestion,
1617
onSelect,
18+
onToggleSnooze = () => {},
1719
}) => {
20+
// Handle snooze button click without triggering the card's onClick
21+
const handleSnoozeClick = (e: React.MouseEvent) => {
22+
e.stopPropagation();
23+
onToggleSnooze(presetQuestion.id);
24+
};
25+
1826
return (
19-
<div onClick={() => onSelect(presetQuestion)} className='cursor-pointer'>
20-
<div
27+
<div className='relative'>
28+
{/* Question Card */}
29+
<div
30+
onClick={presetQuestion.isSnoozed ? () => {} : () => onSelect(presetQuestion)}
2131
className={cn(
22-
'bg-white rounded-md p-3 shadow-sm flex items-center hover:bg-gray-100 transition-colors',
23-
'border-2 border-dotted border-brand-pink'
32+
presetQuestion.isSnoozed ? 'cursor-default' : 'cursor-pointer',
33+
presetQuestion.isSnoozed && 'opacity-60'
2434
)}
2535
>
26-
{/* Tooltip wrapped around the HelpCircle icon */}
27-
<Tooltip>
28-
<TooltipTrigger asChild>
29-
<span className='inline-flex items-center justify-center text-brand-pink mr-2'>
30-
<HelpCircle size={16} />
36+
<div
37+
className={cn(
38+
'bg-white rounded-md p-3 shadow-sm flex items-center',
39+
!presetQuestion.isSnoozed && 'hover:bg-gray-100',
40+
'transition-colors border-2 border-dotted',
41+
presetQuestion.isSnoozed ? 'border-blue-300' : 'border-brand-pink'
42+
)}
43+
>
44+
{/* Help icon with tooltip - only for non-snoozed questions */}
45+
{!presetQuestion.isSnoozed && (
46+
<Tooltip>
47+
<TooltipTrigger asChild>
48+
<span className="inline-flex items-center justify-center mr-2 text-brand-pink bg-pink-50 p-1 rounded-full">
49+
<HelpCircle size={16} />
50+
</span>
51+
</TooltipTrigger>
52+
<TooltipContent className='p-2 bg-black text-white rounded'>
53+
Your employer would like to ask you this question
54+
</TooltipContent>
55+
</Tooltip>
56+
)}
57+
58+
{/* Main question text (truncated if too long) */}
59+
<span className='flex-1 truncate text-lg font-semibold'>
60+
{presetQuestion.mainQuestion}
61+
</span>
62+
63+
{/* Snooze/Unsnooze button */}
64+
<div className="relative">
65+
<Tooltip>
66+
<TooltipTrigger asChild>
67+
<button
68+
onClick={handleSnoozeClick}
69+
className={cn(
70+
"mr-2 p-1.5 rounded-full transition-colors flex items-center justify-center",
71+
"border border-gray-200 shadow-sm",
72+
presetQuestion.isSnoozed
73+
? "hover:bg-blue-100 bg-blue-50"
74+
: "hover:bg-gray-100"
75+
)}
76+
aria-label={presetQuestion.isSnoozed ? "Unsnooze question" : "Snooze question"}
77+
>
78+
{presetQuestion.isSnoozed ? (
79+
<Bell size={16} className="text-blue-600" />
80+
) : (
81+
<BellOff size={16} className="text-gray-600" />
82+
)}
83+
</button>
84+
</TooltipTrigger>
85+
<TooltipContent className='p-2 bg-black text-white rounded'>
86+
{presetQuestion.isSnoozed ? 'Unsnooze question' : 'Snooze question'}
87+
</TooltipContent>
88+
</Tooltip>
89+
</div>
90+
91+
{/* Chevron icon to indicate clickability - only for non-snoozed questions */}
92+
{!presetQuestion.isSnoozed && (
93+
<span className='inline-flex items-center justify-center p-1 rounded-full bg-gray-100 text-gray-500'>
94+
<ChevronRight size={16} />
3195
</span>
32-
</TooltipTrigger>
33-
<TooltipContent className='p-2 bg-black text-white rounded'>
34-
Your employer would like to ask you this question.
35-
</TooltipContent>
36-
</Tooltip>
37-
{/* Main question text (truncated if too long) */}
38-
<span className='flex-1 truncate text-lg font-semibold'>
39-
{presetQuestion.mainQuestion}
40-
</span>
41-
{/* Chevron icon to indicate clickability */}
42-
<span className='inline-flex items-center text-gray-400'>
43-
<ChevronRight size={16} />
44-
</span>
96+
)}
97+
</div>
4598
</div>
4699
</div>
47100
);

src/components/statements/StatementItem.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,19 @@ const StatementItem: React.FC<StatementItemProps> = ({
287287
// Static view when not in editing mode.
288288
return (
289289
<div
290-
className={`border rounded-md p-3 space-y-2 ${
290+
className={`border rounded-md p-3 space-y-2 relative ${
291291
statement.isResolved
292292
? 'bg-gray-100 border-gray-300 opacity-80'
293293
: 'bg-white border-gray-200 shadow-sm'
294294
}`}
295295
>
296+
{/* Archived badge - positioned in top right corner */}
297+
{statement.isResolved && (
298+
<span className="absolute -top-2 -right-2 bg-gray-200 text-gray-600 text-xs px-2 py-0.5 rounded-full">
299+
Archived
300+
</span>
301+
)}
302+
296303
<div className='flex items-center justify-between'>
297304
<div className='flex items-center space-x-2'>
298305
{/* Privacy status icon */}
@@ -324,16 +331,6 @@ const StatementItem: React.FC<StatementItemProps> = ({
324331
statement.atoms.verb
325332
)} ${statement.atoms.object}`}
326333
</span>
327-
328-
{/* Only show badges if needed */}
329-
{statement.isResolved && (
330-
<div className="flex mt-1">
331-
{/* Archived badge */}
332-
<span className="bg-gray-200 text-gray-600 text-xs px-2 py-0.5 rounded-full">
333-
Archived
334-
</span>
335-
</div>
336-
)}
337334
</div>
338335
</div>
339336
<div className='flex items-center space-x-4'>
@@ -390,6 +387,7 @@ const StatementItem: React.FC<StatementItemProps> = ({
390387
)}
391388
</DropdownMenuItem>
392389

390+
393391
{onReset && (
394392
<DropdownMenuItem onClick={() => onReset(statement.id)}>
395393
<RotateCcw className='mr-2 h-4 w-4' />

src/components/statements/StatementList.tsx

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import statementsCategories from '../../../data/statementsCategories.json';
1212
import { formatCategoryName } from '../../../lib/utils';
1313
import { updateEntry } from '../../api/entriesApi';
1414
import { EditStatementModal } from '../statementWizard/EditStatementModal';
15+
import { BellOff, ChevronUp, ChevronDown } from 'lucide-react';
1516

1617
const groupQuestionsByCategory = (questions: SetQuestion[]) => {
1718
return questions.reduce<Record<string, SetQuestion[]>>((acc, question) => {
@@ -36,9 +37,11 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
3637
const { entries } = data;
3738

3839
const [usedPresetQuestions, setUsedPresetQuestions] = useState<string[]>([]);
39-
const { questions } = useQuestions();
40+
const { questions, setQuestions } = useQuestions();
41+
42+
// Get available preset questions (not used and not in snoozed section)
4043
const presetQuestions = questions.filter(
41-
(q) => !usedPresetQuestions.includes(q.id)
44+
(q) => !usedPresetQuestions.includes(q.id) && !q.isSnoozed
4245
);
4346

4447
const questionsByCategory = groupQuestionsByCategory(presetQuestions);
@@ -66,13 +69,48 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
6669
// Keep a backup of the original entries when entering edit mode
6770
const [originalEntries, setOriginalEntries] = useState<{[id: string]: Entry}>({});
6871

72+
// Handle toggling the resolved state (archive/unarchive)
6973
const handleToggleResolved = (statementId: string) => {
7074
const stmt = entries.find((s) => s.id === statementId);
7175
if (!stmt) return;
7276
const updated = { ...stmt, isResolved: !stmt.isResolved };
7377
setData({ type: 'UPDATE_ENTRY', payload: updated });
7478
updateEntry(updated);
7579
};
80+
81+
82+
// Handle toggling the snoozed state for questions
83+
const handleToggleQuestionSnooze = (questionId: string) => {
84+
// Find the question in the questions array
85+
const questionToToggle = questions.find(q => q.id === questionId);
86+
if (!questionToToggle) return;
87+
88+
// Create a new array with the updated question
89+
const updatedQuestions = questions.map(q => {
90+
if (q.id === questionId) {
91+
if (q.isSnoozed) {
92+
// Unsnooze - restore to original category if available
93+
return {
94+
...q,
95+
isSnoozed: false,
96+
category: q.originalCategory || q.category
97+
};
98+
} else {
99+
// Snooze - store original category and move to snoozed
100+
return {
101+
...q,
102+
isSnoozed: true,
103+
originalCategory: q.category,
104+
category: 'snoozed'
105+
};
106+
}
107+
}
108+
return q;
109+
});
110+
111+
// Update the questions in context
112+
setQuestions(updatedQuestions);
113+
};
76114

77115
const handleToggleActionResolved = (
78116
statementId: string,
@@ -254,10 +292,17 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
254292
updateEntry(updatedStatement);
255293
};
256294

295+
// State for managing the visibility of the snoozed section
296+
const [isSnoozedQuestionsSectionExpanded, setIsSnoozedQuestionsSectionExpanded] = useState(false);
297+
257298
const renderCategorySection = (catId: string, catLabel: string) => {
299+
// Don't render the snoozed category in the normal flow
300+
if (catId === 'snoozed') return null;
301+
258302
const presetForCat = questionsByCategory[catId] || [];
259303
const statementsForCat = statementsByCategory[catId] || [];
260304
if (presetForCat.length === 0 && statementsForCat.length === 0) return null;
305+
261306
return (
262307
<div key={catId} className='mb-8'>
263308
<h3 className='text-lg font-semibold mb-2'>
@@ -270,6 +315,7 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
270315
<QuestionCard
271316
presetQuestion={presetQuestion}
272317
onSelect={handlePresetQuestionSelect}
318+
onToggleSnooze={handleToggleQuestionSnooze}
273319
/>
274320
</li>
275321
))}
@@ -327,6 +373,49 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
327373
</div>
328374
);
329375
};
376+
377+
378+
// Special renderer for the snoozed questions section
379+
const renderSnoozedQuestionsSection = () => {
380+
// Filter questions to get snoozed ones
381+
const snoozedQuestions = questions.filter(q => q.isSnoozed);
382+
if (snoozedQuestions.length === 0) return null;
383+
384+
return (
385+
<div className='mb-8 mt-4 border-t pt-4'>
386+
<div
387+
className='flex items-center justify-between cursor-pointer py-2 px-3 bg-blue-50 rounded-md mb-2'
388+
onClick={() => setIsSnoozedQuestionsSectionExpanded(!isSnoozedQuestionsSectionExpanded)}
389+
>
390+
<h3 className='text-lg font-semibold flex items-center text-blue-700'>
391+
<BellOff className='h-5 w-5 mr-2' />
392+
Snoozed Questions ({snoozedQuestions.length})
393+
</h3>
394+
<div className='text-blue-600'>
395+
{isSnoozedQuestionsSectionExpanded ? (
396+
<ChevronUp className='h-5 w-5' />
397+
) : (
398+
<ChevronDown className='h-5 w-5' />
399+
)}
400+
</div>
401+
</div>
402+
403+
{isSnoozedQuestionsSectionExpanded && (
404+
<ul className='space-y-2 mt-4 pl-4 border-l-2 border-blue-100'>
405+
{snoozedQuestions.map((question) => (
406+
<li key={`snoozed-${question.id}`}>
407+
<QuestionCard
408+
presetQuestion={question}
409+
onSelect={() => {/* Disabled for snoozed questions */}}
410+
onToggleSnooze={handleToggleQuestionSnooze}
411+
/>
412+
</li>
413+
))}
414+
</ul>
415+
)}
416+
</div>
417+
);
418+
};
330419

331420
const definedCategories = categoriesList;
332421
const definedCategoryIds = definedCategories.map((c) => c.id);
@@ -340,10 +429,15 @@ const StatementList: React.FC<{ username: string }> = ({ username }) => {
340429
return (
341430
<>
342431
<div className='mt-8 bg-white rounded-xl shadow-lg p-6 w-full'>
432+
{/* Regular categories */}
343433
{definedCategories.map((cat) =>
344434
renderCategorySection(cat.id, cat.name)
345435
)}
346436
{extraCategoryIds.map((catId) => renderCategorySection(catId, catId))}
437+
438+
{/* Snoozed sections */}
439+
{renderSnoozedQuestionsSection()}
440+
347441
<ConfirmationDialog
348442
isOpen={deleteConfirmation.isOpen}
349443
onClose={handleDeleteCancel}

types/entries.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export interface SetQuestion {
5959
id: string;
6060
mainQuestion: string;
6161
category?: string;
62+
isSnoozed?: boolean;
63+
originalCategory?: string; // Store the original category when snoozed
6264
steps: {
6365
subject: SetQuestionStep;
6466
verb: SetQuestionStep;

0 commit comments

Comments
 (0)