Skip to content

Commit f601776

Browse files
committed
add conflict dialogs
1 parent 0259dad commit f601776

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

platforms/web/src/Teach.css

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,83 @@
149149
padding: 2rem;
150150
}
151151

152+
.conflict-dialog {
153+
max-width: 700px;
154+
}
155+
156+
.conflict-info {
157+
display: grid;
158+
grid-template-columns: 1fr 1fr;
159+
gap: 1rem;
160+
margin: 1.5rem 0;
161+
padding: 1rem;
162+
background: var(--learningmap-color-whitesmoke, #f5f5f5);
163+
border-radius: 4px;
164+
}
165+
166+
.conflict-map-info h4 {
167+
margin: 0 0 0.5rem 0;
168+
font-size: 0.9rem;
169+
color: var(--learningmap-color-quicksilver, #a4a4a4);
170+
text-transform: uppercase;
171+
}
172+
173+
.conflict-map-info p {
174+
margin: 0.25rem 0;
175+
font-size: 1rem;
176+
}
177+
178+
.conflict-map-info .text-small {
179+
font-size: 0.85rem;
180+
color: var(--learningmap-color-quicksilver, #a4a4a4);
181+
}
182+
183+
.conflict-actions {
184+
display: flex;
185+
flex-direction: column;
186+
gap: 0.75rem;
187+
margin-top: 1.5rem;
188+
}
189+
190+
.conflict-button {
191+
padding: 0.875rem 1.5rem;
192+
border: none;
193+
border-radius: 4px;
194+
cursor: pointer;
195+
font-size: 1rem;
196+
font-weight: 500;
197+
transition: all 0.2s;
198+
text-align: center;
199+
}
200+
201+
.conflict-button-danger {
202+
background: #dc3545;
203+
color: white;
204+
}
205+
206+
.conflict-button-danger:hover {
207+
background: #c82333;
208+
}
209+
210+
.conflict-button-primary {
211+
background: var(--learningmap-color-openpatch, #007864);
212+
color: white;
213+
}
214+
215+
.conflict-button-primary:hover {
216+
background: var(--learningmap-color-dark-forest, #004c45);
217+
}
218+
219+
.conflict-button-secondary {
220+
background: white;
221+
color: #333;
222+
border: 2px solid #dee2e6;
223+
}
224+
225+
.conflict-button-secondary:hover {
226+
background: var(--learningmap-color-whitesmoke, #f5f5f5);
227+
}
228+
152229
.add-map-section {
153230
background: var(--learningmap-color-whitesmoke, #f5f5f5);
154231
padding: 2rem;

platforms/web/src/Teach.tsx

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ function Teach() {
1414
const [loading, setLoading] = useState(false);
1515
const [error, setError] = useState<string | null>(null);
1616
const [showAddDialog, setShowAddDialog] = useState(false);
17+
const [showConflictDialog, setShowConflictDialog] = useState(false);
18+
const [conflictData, setConflictData] = useState<{
19+
storageId: string;
20+
roadmapData: any;
21+
jsonId?: string;
22+
existingMap: TeacherMapEntry;
23+
} | null>(null);
1724

1825
useEffect(() => {
1926
db.getAllTeacherMaps().then(setAllMaps);
@@ -83,6 +90,16 @@ function Teach() {
8390
const roadmapData = await response.json();
8491
const storageId = roadmapData.settings?.id || jsonId;
8592

93+
// Check if map already exists
94+
const existingMap = await db.getTeacherMap(storageId);
95+
if (existingMap) {
96+
// Show conflict dialog
97+
setConflictData({ storageId, roadmapData, jsonId, existingMap });
98+
setShowConflictDialog(true);
99+
setLoading(false);
100+
return;
101+
}
102+
86103
await db.addTeacherMap(storageId, roadmapData, jsonId);
87104
const maps = await db.getAllTeacherMaps();
88105
setAllMaps(maps);
@@ -114,6 +131,15 @@ function Teach() {
114131
const uploadId = `upload-${Date.now()}`;
115132
const storageId = json.settings?.id || uploadId;
116133

134+
// Check if map already exists
135+
const existingMap = await db.getTeacherMap(storageId);
136+
if (existingMap) {
137+
// Show conflict dialog
138+
setConflictData({ storageId, roadmapData: json, existingMap });
139+
setShowConflictDialog(true);
140+
return;
141+
}
142+
117143
await db.addTeacherMap(storageId, json, undefined);
118144
const maps = await db.getAllTeacherMaps();
119145
setAllMaps(maps);
@@ -133,6 +159,37 @@ function Teach() {
133159
event.target.value = '';
134160
};
135161

162+
const handleConflictOverwrite = async () => {
163+
if (!conflictData) return;
164+
165+
await db.addTeacherMap(conflictData.storageId, conflictData.roadmapData, conflictData.jsonId);
166+
const maps = await db.getAllTeacherMaps();
167+
setAllMaps(maps);
168+
setNewMapUrl('');
169+
setShowAddDialog(false);
170+
setShowConflictDialog(false);
171+
setConflictData(null);
172+
};
173+
174+
const handleConflictNewId = async () => {
175+
if (!conflictData) return;
176+
177+
// Generate a new unique ID
178+
const newId = `${conflictData.storageId}-${Date.now()}`;
179+
await db.addTeacherMap(newId, conflictData.roadmapData, conflictData.jsonId);
180+
const maps = await db.getAllTeacherMaps();
181+
setAllMaps(maps);
182+
setNewMapUrl('');
183+
setShowAddDialog(false);
184+
setShowConflictDialog(false);
185+
setConflictData(null);
186+
};
187+
188+
const handleConflictCancel = () => {
189+
setShowConflictDialog(false);
190+
setConflictData(null);
191+
};
192+
136193
return (
137194
<div className="teach-container">
138195
<Header>
@@ -195,6 +252,60 @@ function Teach() {
195252
</div>
196253
)}
197254

255+
{showConflictDialog && conflictData && (
256+
<div className="dialog-overlay" onClick={handleConflictCancel}>
257+
<div className="dialog-content conflict-dialog" onClick={(e) => e.stopPropagation()}>
258+
<div className="dialog-header">
259+
<h2>⚠️ Map Already Exists</h2>
260+
<button
261+
className="dialog-close"
262+
onClick={handleConflictCancel}
263+
aria-label="Close dialog"
264+
>
265+
×
266+
</button>
267+
</div>
268+
<div className="dialog-body">
269+
<p>
270+
A learning map with ID <strong>{conflictData.storageId}</strong> already exists in your collection.
271+
</p>
272+
<div className="conflict-info">
273+
<div className="conflict-map-info">
274+
<h4>Existing Map:</h4>
275+
<p><strong>{conflictData.existingMap.roadmapData.settings?.title || 'Untitled'}</strong></p>
276+
<p className="text-small">Last modified: {new Date(conflictData.existingMap.lastModified).toLocaleString()}</p>
277+
</div>
278+
<div className="conflict-map-info">
279+
<h4>New Map:</h4>
280+
<p><strong>{conflictData.roadmapData.settings?.title || 'Untitled'}</strong></p>
281+
</div>
282+
</div>
283+
<p>What would you like to do?</p>
284+
<div className="conflict-actions">
285+
<button
286+
onClick={handleConflictOverwrite}
287+
className="conflict-button conflict-button-danger"
288+
>
289+
Overwrite Existing
290+
</button>
291+
<button
292+
onClick={handleConflictNewId}
293+
className="conflict-button conflict-button-primary"
294+
>
295+
Keep Both (New ID)
296+
</button>
297+
<button
298+
onClick={handleConflictCancel}
299+
className="conflict-button conflict-button-secondary"
300+
>
301+
Cancel
302+
</button>
303+
</div>
304+
</div>
305+
</div>
306+
</div>
307+
)}
308+
198309
<div className="teach-content">
199310
<div className="teach-header">
200311
<h2>My Created Maps</h2>

0 commit comments

Comments
 (0)