Skip to content

Commit eea87e2

Browse files
committed
stash
1 parent 7dcc28f commit eea87e2

6 files changed

Lines changed: 316 additions & 52 deletions

File tree

src/components/DigitalColleagues/EditableField.tsx

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ interface EditableFieldProps {
1010
value: string;
1111
label: string;
1212
multiline?: boolean;
13-
onSave: (fieldName: string, value: string) => void;
13+
onSave: (fieldName: string, value: string) => void | Promise<void>;
1414
className?: string;
15+
disabled?: boolean;
1516
}
1617

1718
export const EditableField: React.FC<EditableFieldProps> = ({
@@ -20,19 +21,22 @@ export const EditableField: React.FC<EditableFieldProps> = ({
2021
label,
2122
multiline = false,
2223
onSave,
23-
className = ""
24+
className = "",
25+
disabled = false
2426
}) => {
2527
const [isEditing, setIsEditing] = useState(false);
2628
const [tempValue, setTempValue] = useState('');
2729

2830
const handleFieldClick = () => {
29-
setIsEditing(true);
30-
setTempValue(value);
31+
if (!disabled) {
32+
setIsEditing(true);
33+
setTempValue(value);
34+
}
3135
};
3236

33-
const handleSave = () => {
37+
const handleSave = async () => {
3438
if (tempValue !== value) {
35-
onSave(fieldName, tempValue);
39+
await onSave(fieldName, tempValue);
3640
}
3741
setIsEditing(false);
3842
};
@@ -75,14 +79,16 @@ export const EditableField: React.FC<EditableFieldProps> = ({
7579
) : (
7680
<div
7781
onClick={handleFieldClick}
78-
className={`cursor-pointer hover:bg-muted/50 p-2 rounded border border-transparent hover:border-border transition-colors group ${
82+
className={`${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} hover:bg-muted/50 p-2 rounded border border-transparent hover:border-border transition-colors group ${
7983
multiline ? "min-h-[100px]" : "min-h-[40px]"
8084
} flex items-start gap-2`}
8185
>
8286
<span className={multiline ? "text-sm text-muted-foreground" : "text-lg font-medium text-foreground"}>
8387
{value || "Click to edit..."}
8488
</span>
85-
<Edit className="h-4 w-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
89+
{!disabled && (
90+
<Edit className="h-4 w-4 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
91+
)}
8692
</div>
8793
)}
8894
</div>

src/components/DigitalColleagues/SearchableSelect.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ interface SearchableSelectProps {
2323
options: SearchableSelectOption[];
2424
placeholder?: string;
2525
allowCustomValue?: boolean;
26+
disabled?: boolean;
2627
}
2728

2829
export const SearchableSelect: React.FC<SearchableSelectProps> = ({
@@ -31,7 +32,8 @@ export const SearchableSelect: React.FC<SearchableSelectProps> = ({
3132
onValueChange,
3233
options,
3334
placeholder = 'Search...',
34-
allowCustomValue = false
35+
allowCustomValue = false,
36+
disabled = false
3537
}) => {
3638
const [isOpen, setIsOpen] = useState(false);
3739
const [searchTerm, setSearchTerm] = useState('');
@@ -55,15 +57,21 @@ export const SearchableSelect: React.FC<SearchableSelectProps> = ({
5557
}
5658
};
5759

60+
const handleOpenChange = (open: boolean) => {
61+
if (!disabled) {
62+
setIsOpen(open);
63+
}
64+
};
65+
5866
return (
5967
<div className="space-y-1">
6068
<Label className="text-xs text-muted-foreground">{label}</Label>
61-
<Select value={value} onValueChange={handleSelect} open={isOpen} onOpenChange={setIsOpen}>
69+
<Select value={value} onValueChange={handleSelect} open={isOpen} onOpenChange={handleOpenChange} disabled={disabled}>
6270
<SelectTrigger className="h-auto p-0 border-none bg-transparent hover:bg-muted/50 rounded-md">
6371
<Badge
6472
variant="secondary"
65-
className="cursor-pointer hover:bg-muted transition-colors"
66-
onClick={() => setIsOpen(!isOpen)}
73+
className={`${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} hover:bg-muted transition-colors`}
74+
onClick={() => !disabled && setIsOpen(!isOpen)}
6775
>
6876
{selectedOption?.label || value || 'Select...'}
6977
</Badge>

src/components/DigitalColleagues/TaskDetailsModal.stories.tsx

Lines changed: 100 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,53 @@ const meta: Meta<typeof TaskDetailsModal> = {
99
layout: 'centered',
1010
},
1111
tags: ['autodocs'],
12+
argTypes: {
13+
onUpdateTask: { action: 'update task' },
14+
onDeleteTask: { action: 'delete task' },
15+
onClose: { action: 'close' },
16+
},
1217
};
1318

1419
export default meta;
1520
type Story = StoryObj<typeof TaskDetailsModal>;
1621

22+
// Mock async functions
23+
const mockUpdateTask = (taskId: string, updates: any) => {
24+
return new Promise<void>((resolve) => {
25+
setTimeout(() => {
26+
console.log('Task updated:', taskId, updates);
27+
resolve();
28+
}, 2000); // 2 second delay to show loading state
29+
});
30+
};
31+
32+
const mockDeleteTask = (taskId: string) => {
33+
return new Promise<void>((resolve) => {
34+
setTimeout(() => {
35+
console.log('Task deleted:', taskId);
36+
resolve();
37+
}, 1500); // 1.5 second delay to show loading state
38+
});
39+
};
40+
41+
const mockUpdateTaskFail = (taskId: string, updates: any) => {
42+
return new Promise<void>((resolve, reject) => {
43+
setTimeout(() => {
44+
console.log('Task update failed:', taskId, updates);
45+
reject(new Error('Update failed'));
46+
}, 2000);
47+
});
48+
};
49+
50+
const mockDeleteTaskFail = (taskId: string) => {
51+
return new Promise<void>((resolve, reject) => {
52+
setTimeout(() => {
53+
console.log('Task delete failed:', taskId);
54+
reject(new Error('Delete failed'));
55+
}, 1500);
56+
});
57+
};
58+
1759
const mockEpics = [
1860
{
1961
id: '1',
@@ -81,8 +123,56 @@ export const Default: Story = {
81123
epics: mockEpics,
82124
sprints: mockSprints,
83125
onClose: () => {},
84-
onUpdateTask: () => {},
85-
onDeleteTask: () => {},
126+
onUpdateTask: mockUpdateTask,
127+
onDeleteTask: mockDeleteTask,
128+
},
129+
};
130+
131+
export const WithLoadingStates: Story = {
132+
args: {
133+
isOpen: true,
134+
task: {
135+
id: '1',
136+
title: 'Design login page',
137+
description: 'Create wireframes and mockups for the login interface',
138+
status: 'in-progress',
139+
priority: 'high',
140+
type: 'story',
141+
epicId: '1',
142+
sprintId: '2',
143+
assignee: 'John Doe',
144+
points: 8,
145+
createdAt: new Date('2024-01-15'),
146+
},
147+
epics: mockEpics,
148+
sprints: mockSprints,
149+
onClose: () => {},
150+
onUpdateTask: mockUpdateTask,
151+
onDeleteTask: mockDeleteTask,
152+
},
153+
};
154+
155+
export const WithErrorStates: Story = {
156+
args: {
157+
isOpen: true,
158+
task: {
159+
id: '1',
160+
title: 'Design login page',
161+
description: 'Create wireframes and mockups for the login interface',
162+
status: 'in-progress',
163+
priority: 'high',
164+
type: 'story',
165+
epicId: '1',
166+
sprintId: '2',
167+
assignee: 'John Doe',
168+
points: 8,
169+
createdAt: new Date('2024-01-15'),
170+
},
171+
epics: mockEpics,
172+
sprints: mockSprints,
173+
onClose: () => {},
174+
onUpdateTask: mockUpdateTaskFail,
175+
onDeleteTask: mockDeleteTaskFail,
86176
},
87177
};
88178

@@ -105,8 +195,8 @@ export const BugTask: Story = {
105195
epics: mockEpics,
106196
sprints: mockSprints,
107197
onClose: () => {},
108-
onUpdateTask: () => {},
109-
onDeleteTask: () => {},
198+
onUpdateTask: mockUpdateTask,
199+
onDeleteTask: mockDeleteTask,
110200
},
111201
};
112202

@@ -129,8 +219,8 @@ export const CompletedTask: Story = {
129219
epics: mockEpics,
130220
sprints: mockSprints,
131221
onClose: () => {},
132-
onUpdateTask: () => {},
133-
onDeleteTask: () => {},
222+
onUpdateTask: mockUpdateTask,
223+
onDeleteTask: mockDeleteTask,
134224
},
135225
};
136226

@@ -153,8 +243,8 @@ export const SpikeTask: Story = {
153243
epics: mockEpics,
154244
sprints: mockSprints,
155245
onClose: () => {},
156-
onUpdateTask: () => {},
157-
onDeleteTask: () => {},
246+
onUpdateTask: mockUpdateTask,
247+
onDeleteTask: mockDeleteTask,
158248
},
159249
};
160250

@@ -177,7 +267,7 @@ export const NoSprint: Story = {
177267
epics: mockEpics,
178268
sprints: mockSprints,
179269
onClose: () => {},
180-
onUpdateTask: () => {},
181-
onDeleteTask: () => {},
270+
onUpdateTask: mockUpdateTask,
271+
onDeleteTask: mockDeleteTask,
182272
},
183273
};

0 commit comments

Comments
 (0)