Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions components/ConflictResolutionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React from 'react';
import Modal from './Modal';
import Button from './Button';
import { WarningIcon } from './Icons';

interface DbStats {
fileSize: string;
nodeCount: number;
documentCount: number;
templateCount: number;
modifiedTime: string;
}

interface ConflictResolutionModalProps {
localStats: DbStats;
remoteStats: DbStats;
onResolve: (resolution: 'local' | 'remote') => void;
onClose: () => void;
}

const ConflictResolutionModal: React.FC<ConflictResolutionModalProps> = ({
localStats,
remoteStats,
onResolve,
onClose,
}) => {
const localDate = new Date(localStats.modifiedTime);
const remoteDate = new Date(remoteStats.modifiedTime);

const isLocalNewer = localDate.getTime() > remoteDate.getTime();
const isRemoteNewer = remoteDate.getTime() > localDate.getTime();

return (
<Modal onClose={onClose} title="Database Sync Conflict Detected">
<div className="flex flex-col">
{/* Warning Banner */}
<div className="p-4 bg-warning/10 border-b border-warning/20 flex gap-3 items-start">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-warning/15 flex items-center justify-center text-warning">
<WarningIcon className="w-5 h-5" />
</div>
<div>
<h4 className="text-xs font-semibold text-text-main mb-1">Divergent Database Changes</h4>
<p className="text-[11px] text-text-secondary leading-relaxed">
Both your local database and the cloud database have been modified independently since the last sync.
Please select which version you would like to keep. The other version will be overwritten.
</p>
</div>
</div>

{/* Comparison Area */}
<div className="p-5 grid grid-cols-2 gap-4 bg-secondary">
{/* Local Stats Card */}
<div className={`p-4 rounded-lg border bg-background flex flex-col justify-between ${
isLocalNewer ? 'border-primary/45 shadow-[0_0_12px_rgba(var(--color-primary),0.05)]' : 'border-border-color'
}`}>
<div>
<div className="flex justify-between items-center mb-3">
<h5 className="text-xs font-semibold text-text-main">Local Database</h5>
{isLocalNewer && (
<span className="text-[9px] px-1.5 py-0.5 rounded bg-primary/10 text-primary border border-primary/20 font-medium">
Newer
</span>
)}
</div>
<ul className="space-y-2 text-[11px] text-text-secondary">
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>Last Modified:</span>
<span className={`font-medium ${isLocalNewer ? 'text-primary' : 'text-text-main'}`}>
{localDate.toLocaleString()}
</span>
</li>
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>File Size:</span>
<span className="text-text-main font-medium">{localStats.fileSize}</span>
</li>
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>Folders & Documents:</span>
<span className="text-text-main font-medium">{localStats.nodeCount}</span>
</li>
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>Document Templates:</span>
<span className="text-text-main font-medium">{localStats.templateCount}</span>
</li>
</ul>
</div>
<div className="mt-5">
<Button
variant={isLocalNewer ? 'primary' : 'secondary'}
className="w-full text-xs"
onClick={() => onResolve('local')}
>
Keep Local (Overwrite Cloud)
</Button>
</div>
</div>

{/* Cloud Stats Card */}
<div className={`p-4 rounded-lg border bg-background flex flex-col justify-between ${
isRemoteNewer ? 'border-primary/45 shadow-[0_0_12px_rgba(var(--color-primary),0.05)]' : 'border-border-color'
}`}>
<div>
<div className="flex justify-between items-center mb-3">
<h5 className="text-xs font-semibold text-text-main">Cloud Database</h5>
{isRemoteNewer && (
<span className="text-[9px] px-1.5 py-0.5 rounded bg-primary/10 text-primary border border-primary/20 font-medium">
Newer
</span>
)}
</div>
<ul className="space-y-2 text-[11px] text-text-secondary">
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>Last Modified:</span>
<span className={`font-medium ${isRemoteNewer ? 'text-primary' : 'text-text-main'}`}>
{remoteDate.toLocaleString()}
</span>
</li>
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>File Size:</span>
<span className="text-text-main font-medium">{remoteStats.fileSize}</span>
</li>
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>Folders & Documents:</span>
<span className="text-text-main font-medium">{remoteStats.nodeCount}</span>
</li>
<li className="flex justify-between py-1 border-b border-border-color/30">
<span>Document Templates:</span>
<span className="text-text-main font-medium">{remoteStats.templateCount}</span>
</li>
</ul>
</div>
<div className="mt-5">
<Button
variant={isRemoteNewer ? 'primary' : 'secondary'}
className="w-full text-xs"
onClick={() => onResolve('remote')}
>
Keep Cloud (Overwrite Local)
</Button>
</div>
</div>
</div>

{/* Footer */}
<div className="flex justify-end gap-3 px-6 py-4 bg-background/50 border-t border-border-color rounded-b-lg">
<Button onClick={onClose} variant="secondary" type="button" className="text-xs">
Cancel & Resolve Later
</Button>
</div>
</div>
</Modal>
);
};

export default ConflictResolutionModal;
6 changes: 6 additions & 0 deletions components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,9 @@ export const ChatIcon: React.FC<IconProps> = ({ className }) => (
<path strokeLinecap="round" strokeLinejoin="round" d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 01-.825-.242m9.345-8.334a2.126 2.126 0 00-.476-.095 48.64 48.64 0 00-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0011.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155" />
</svg>
);

export const CloudIcon: React.FC<IconProps> = ({ className }) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className={className}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 16.5V9.75m0 0l3 3m-3-3l-3 3M6.75 19.5a4.5 4.5 0 01-1.41-8.775 5.25 5.25 0 0110.233-2.33 3 3 0 013.758 3.848A3.752 3.752 0 0118 19.5H6.75z" />
</svg>
);
11 changes: 11 additions & 0 deletions components/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
DatabaseIcon,
KeyboardIcon,
TerminalIcon,
CloudIcon,
} from './Icons';
import Button from './Button';
import KeyboardShortcutsSection from './KeyboardShortcutsSection';
Expand All @@ -22,6 +23,7 @@ import { PythonSettingsSection } from './settings/PythonSettingsSection';
import { ScriptDefaultsSection } from './settings/ScriptDefaultsSection';
import { GeneralSettingsSection } from './settings/GeneralSettingsSection';
import { DatabaseSettingsSection } from './settings/DatabaseSettingsSection';
import { CloudSyncSettingsSection } from './settings/CloudSyncSettingsSection';
import { AdvancedSettingsSection } from './settings/AdvancedSettingsSection';

interface SettingsViewProps {
Expand All @@ -44,6 +46,7 @@ type SettingsCategory =
| 'powershell'
| 'general'
| 'database'
| 'sync'
| 'advanced';

const categories: { id: SettingsCategory; label: string; icon: React.FC<{ className?: string }> }[] = [
Expand All @@ -57,6 +60,7 @@ const categories: { id: SettingsCategory; label: string; icon: React.FC<{ classN
{ id: 'powershell', label: 'PowerShell', icon: TerminalIcon },
{ id: 'general', label: 'General', icon: GearIcon },
{ id: 'database', label: 'Database', icon: DatabaseIcon },
{ id: 'sync', label: 'Cloud Sync', icon: CloudIcon },
{ id: 'advanced', label: 'Advanced', icon: FileIcon },
];

Expand Down Expand Up @@ -222,6 +226,13 @@ const SettingsView: React.FC<SettingsViewProps> = ({
);
case 'database':
return <DatabaseSettingsSection />;
case 'sync':
return (
<CloudSyncSettingsSection
settings={currentSettings}
setCurrentSettings={setCurrentSettings}
/>
);
case 'advanced':
return (
<AdvancedSettingsSection
Expand Down
Loading
Loading